mongoose findOneAndUpdate callback with array of ids - node.js

I am using findOneAndUpdate in a forEach loop to create/update multiple entries.
I would like it to return an array of all the object id's it has created or updated.
During the loop, I can see it adding data to the array, but one it leaves the loop, the array is empty.
Should the array not be populated?
here is my code.
var softwareArray = ["Software1","Software2","Software3"],
updatedArray = [];
softwareArray.forEach(function(software){
Software.findOneAndUpdate(
{
Name: software
},
{
Name: software
},
{upsert:true},
function(err, rows){
updatedArray.push(rows._id);
console.log(updatedArray); //This has data in it....
}
);
});
console.log(updatedArray); //This has no data in it...
Edit: Updated with my working changes for Thiago
var softwareArray = ["Software1","Software2","Software3"],
updatedArray = [];
loopSoftware(softwareArray, function(updatedArray){
console.log(updatedArray);
//carry on....
}
function loopSoftware(input, cb){
var returnData = [];
var runLoop = function(software, done) {
Software.findOneAndUpdate(
{Name: software},
{Name: software},
{upsert:true},function(err, rows){
returnData.push(rows._id);
done()
}
);
};
var doneLoop = function(err) {
cb(returnData);
};
async.forEachSeries(input, runLoop, doneLoop);
}

I decorated your code to make you see when what is happening:
var softwareArray = ["Software1","Software2","Software3"],
updatedArray = [];
// TIMESTAMP: 0
softwareArray.forEach(function(software){
// TIMESTAMP: 1, 2, 3
Software.findOneAndUpdate(
{
Name: software
},
{
Name: software
},
{upsert:true},
function(err, rows){
// TIMESTAMP: 5, 6, 7
updatedArray.push(rows._id);
console.log(updatedArray); // This has data in it....
// want to use the result?
if (updatedArray.length == softwareArray.length) {
console.log(updatedArray);
}
}
);
});
// TIMESTAMP: 4
console.log(updatedArray);

Of course this will happen - just like any other networking on Node, it's asynchronous!
This means that the callback you specified for your findOneAndUpdate operation have not run yet when it reaches the console.log(updatedArray); code.
Take a look at Q for working around this common problem.

Related

Node Js forEach unable to get result

Im trying to get iterate the results array variable.
I am able to get the array values inside the function, but when i try to log it outside it shows null.
Wen i googled few posts, i see that forEach was not recommended, suggested was for loop, i tried even that and i get null for result.
What is the issue in cursor.forEach()...
router.get('/getlist', function(req, res, handleError) {
client.connect('mongodb://localhost', function(err, client) {
if (err) throw err;
var db = client.db('angular-demo');
var collection = db.collection("api_details");
var query = {};
var cursor = collection.find(query);
var results = new Array();
var results = cursor.forEach(
function(result) {
return result;
console.log("insert")
console.log(results);
}
);
console.log("append")
console.log(results); //results here shows null
});
});
Log result:
append
[]
insert
[ { _id: 5a6867c8e54f6120709eabc5,
app_id: 'CaseRegistration',
description: 'API to register cases in the system',
cost_per_usage: '0.5',
__v: 0 } ]
insert
[ { _id: 5a6867c8e54f6120709eabc5,
app_id: 'CaseRegistration',
description: 'API to register cases in the system',
cost_per_usage: '0.5',
__v: 0 },
{ _id: 5a6867fde54f6120709eabc6,
app_id: 'CheckCreation',
description: 'CREs create the case with minimal data and assigns it to case initiation team to create checks',
cost_per_usage: '1',
__v: 0 } ]
If you want to grab results from find you can use toArray
cursor.toArray(function (error, documents) {
console.log(documents)
})
// or
cursor.toArray().then(function (documents) {
console.log(documents)
})
// or in async function
const documnets = await cursor.toArray()
or if you need to transform them somehow use map
It's not relevant if you use forEach or for loop in this case. The results variable isn't being populated because you're trying to populate it with the return result from the forEach which doesn't return anything useful.
What you want to do, is just iterate over the results, and fill the results array while iterating.
Something more like this :
var results = [];
cursor.forEach(
function(result) {
results.push(result);
}
);
console.log(results); // This should be populated now

Nodejs and MongoDB : Unable to return value from a function

var config = require('config.json');
var mongo = require('mongoskin');
var db = mongo.db(config.connectionString, { native_parser: true });
module.exports.getNextSequence = function (name) {
var temp;
db.collection("counters").findAndModify(
{ _id: name }, // query
[], // represents a sort order if multiple matches
{ $inc: { seq: 1 } }, // update statement
{ new: true }, // options - new to return the modified document
function (err, doc) {
temp = doc.value.seq;
console.log(temp); // <-- here the temp is getting printed correctly
}
);
return temp;
}
Using the above code, I am not able to return the value of doc.value.seq. When doing console.log(obj.getNextSequence) it prints undefined.
I want the function to return the value of doc.value.seq.
I'm not familiar with mongoskin so I'm not positive this is correct, but a database query is typically asynchronous, so you need to access the queried value via a callback.
I'm guessing your "getNextSequence" function is returning the "temp" variable before the database query completes (i.e. before the "temp = doc.value.seq" statement).
Try something like this:
module.exports.getNextSequence = function (name, callback) {
var temp;
db.collection("counters").findAndModify(
{ _id: name }, // query
[], // represents a sort order if multiple matches
{ $inc: { seq: 1 } }, // update statement
{ new: true }, // options - new to return the modified document
function (err, doc) {
temp = doc.value.seq;
callback(temp);
}
);
}
Then access "temp" from within the callback passed to getNextSequence.
findAndModify is an asynchronous function. Your console.log line will run after you return temp, which will therefore be undefined. In order to get this to work, you'll want to use an asynchronous approach of your own. There are two available approaches in your situation.
Callbacks:
You're already using a callback, which you provide as the final argument to findAndModify. You could extend this approach and feed this into a callback of your own, as follows:
module.exports.getNextSequence = function (name, callback) {
db.collection("counters").findAndModify(
{ _id: name },
[],
{ $inc: { seq: 1 } },
{ new: true },
function (err, doc) {
if (err) {
return callback(err);
}
callback(null, doc.value.seq);
}
);
}
Of course, this will require you to pass a callback into getNextSequence and follow the callback pattern upstream. You might also want to handle the error from mongoskin and do some handling of your own.
Promises:
If you don't provide a callback to findAndModify, it will return a promise, which you can chain on to, as follows:
module.exports.getNextSequence = function (name) {
return db.collection("counters").findAndModify(
{ _id: name },
[],
{ $inc: { seq: 1 } },
{ new: true }
).then(function (doc) {
return doc.value.seq;
});
}
Again, this will require you to follow the promise pattern upstream. You'll want to read up on promises if you choose this approach, so that you can correctly handle errors, which I have not addressed in the example above.

Query with Mongoose multiple times without nesting

I'm trying to generate a document with node.js that needs to run multiple unrelated database queries from a mongo database.
Here is my current code:
Data.find({}, function(err, results) {
if (err) return next(err);
//finished getting data
res.render('page');
}
}
The problem is if I try to run another query, I seem to have to nest it within the first one so that it waits for the first one to finish before starting, and then I have to put res.render() within the innermost nested query (if I don't, res.render() will be called before the database is finished grabbing data, and it wont be rendered with the page).
What I have to do:
Data.find({}, function(err, results) {
if (err) return next(err);
//finished getting data
Data2.find({}, function(err, results2) {
if (err) return next(err);
//finished getting data 2
res.render('page');
}
}
}
}
I am going to have more than 2 queries, so if I keep nesting them it's going to get really messy really fast. Is there a cleaner way to do this, such as a way to make the code wait until all the data is returned and the function is run before continuing with the script?
For mongoose you can probably just do a Promise.all() and use .concat() on the resulting arrays of each query.
As a full demo:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var d1Schema = new Schema({ "name": String });
var Data1 = mongoose.model("Data1", d1Schema);
var d2Schema = new Schema({ "title": String });
var Data2 = mongoose.model("Data2", d2Schema);
mongoose.set('debug',true);
mongoose.connect('mongodb://localhost/test');
async.series(
[
// Clean
function(callback) {
async.each([Data1,Data2],function(model,callback) {
model.remove({},callback)
},callback);
},
// Setup some data
function(callback) {
async.each([
{ "name": "Bill", "model": "Data1" },
{ "title": "Something", "model": "Data2" }
],function(data,callback) {
var model = data.model;
delete data.model;
mongoose.model(model).create(data,callback);
},callback);
},
// Actual Promise.all demo
function(callback) {
Promise.all([
Data1.find().exec(),
Data2.find().exec()
]).then(function(result) {
console.log([].concat.apply([],result));
callback()
}).catch(callback);
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
)
I'm just mixing in async there for brevity of example, but the meat of it is in:
Promise.all([
Data1.find().exec(),
Data2.find().exec()
]).then(function(result) {
console.log([].concat.apply([],result));
})
Where the Promise.all() basically waits for and combines the two results, which would be an "array of arrays" here but the .concat() takes care of that. The result will be:
[
{ _id: 59420fd33d48fa0a490247c8, name: 'Bill', __v: 0 },
{ _id: 59420fd43d48fa0a490247c9, title: 'Something', __v: 0 }
]
Showing the objects from each collection, joined together in one array.
You could also use the async.concat method as an alternate, but unless you are using the library already then it's probably just best to stick to promises.

Inconsistent behaviour with concurrent postgresql access through node.js

A rather trivial use case of node.js and postgresql puzzles me.
It seems that the results from the first query executed by this code linger and are erroneously included in the results array from the second query.
Expected output:
running queries ---------------
[ { name: 'Company 0' } ]
[ { id: 0, company_id: 0 }, { id: 1, company_id: 0 } ]
Usual output:
running queries ---------------
[ { name: 'Company 0' } ]
[ { name: 'Company 0' },
{ id: 0, company_id: 0 },
{ id: 1, company_id: 0 } ]
Occasionally I get the correct result. Below the code, include a 'fix' which ensures I always get the correct result:
var pg = require('pg');
var connectionString = require('./dbconfig.js');
var doQuery = function(queryString) {
// Get a Postgres client from the connection pool
pg.connect(connectionString, function(err, client, done) {
results = [];
// results.push(queryString); // PART OF 'FIX'
var query = client.query(queryString);
// Stream results back one row at a time
query.on('row', function(row) {
results.push(row);
});
// After all data is returned, close connection and return results
query.on('end', function() {
client.end();
// results.splice(0, 1); // PART OF 'FIX'
console.log(results);
});
// Handle Errors
if(err) {
console.log(err);
}
});
};
var company_id = 0;
console.log('running queries ---------------');
doQuery('SELECT companies.name FROM companies WHERE companies.id = '+company_id+';');
doQuery('SELECT * FROM users WHERE users.company_id = ' + company_id + ';');
If anyone is interested in attempting to reproduce this (and assuming it is not a silly coding error by me) I will gladly provide the rest of the code to do so.
JavaScript is a terrible language (see my comment for solution).

find results of attributes in mongodb are always undefined

I really really need help in this. I am using node.js with mongodb and mongoose. So far I've managed to create a schema and save those into my database.
var Bericht = new Schema({
name : String
, mail : String
, betreff : String
, inhalt : String
, datum : Date
});
var Bericht = mongoose.model('Bericht', Bericht);
I habe a html formular where I can transmit with misc. fields data, by querystring I converting those into readable strings
var bericht_data = {
name: tempo.Name
, mail: tempo.Mail
, betreff: tempo.Betreff
, inhalt: tempo.Inhalt
};
var testoro = new Bericht(bericht_data);
testoro.save(function (err) {
if (!err) console.log('Success!');
});
so tempo.Name for example is a string and it also successful in saving it.
So far I can save all data from this formular into my mongodb.
Now the very problem: I want the data back as string to handle for dynamic html.
To get the info into my console, I use
Bericht.find(
{},
{ '_id': 0},
function(err, docs) {
if (!err){
console.log(docs);
// process.exit();
}
else { throw err;}
}
);
The console gives me all data which was ever saved in my schema Bericht excluding the long _id stuff. Sample output:
[ { name: 'Hans', mail: 'hans#wurst.de', betreff: 'I lost my wurst', inhalt: 'look at me, I am amazing' } ]
That's just one, normally there would be a huge amount of data.
The idea is right now to extract only the name into a string like "Hans". I want to get this name into a var, but hell it seems impossible!
I've tried
Bericht.find(
{},
{ '_id': 0},
function(err, docs) {
if (!err){
console.log(docs.name);
// process.exit();
}
else { throw err;}
}
);
But the I get only "undefined" delivered. I appreciate your help!
Take a look at Mongoose QueryStreams. I haven't used it myself, but I've modified their example code to fit your Model, to give you an idea of how it might work in practice:
var stream = Bericht.find().stream()
, names = []
, i;
function closeHandler() {
console.log(JSON.stringify(names));
};
stream.on('data', function (doc) {
if (doc.name) {
names.push(doc.name);
}
})
stream.on('error', function (err) {
// handle err
})
stream.on('close', closeHandler)
Mongoose find return an array of documents so you should try the following:
Bericht.find(
{},
{ '_id': 0},
function(err, docs) {
if (!err){
for(var i=0; i<docs.length; i++)
{
console.log(docs[i].name);
}
// process.exit();
}
else { throw err;}
}
);

Resources