I'm trying to update objects in an array from my Mongo database and saving the updated values back to the database. However, after calling save on the data I changed, nothing gets updated to the database.
router.post('/:poll*', (req, res)=>{
var url = '/poll' + req.url;
Poll.findOne({link: url}, (err, data)=>{
if(err){
throw err;
}
else{
var theValue;
for(var key in req.body){
if(key === 'opt'){
theValue = req.body[key];
}
}
var voteCount;
if(data.voters[0] == null){
data.voters[0] = {};
data.voters[0][req.user.username] = theValue;
data.options[0][theValue] = data.options[0][theValue] + 1;
}
else{
if(data.voters[0][req.user.username] != theValue){
var previous = data.voters[0][req.user.username];
data.voters[0][req.user.username] = theValue;
data.options[0][theValue] = data.options[0][theValue] + 1;
if(previous){
data.options[0][previous] = data.options[0][previous] - 1;
}
}
}
}
console.log(data.voters)
data.save(function(err){
if(!err){
console.log('saved');
}
else{
console.log('error');
}
});
res.send("Your input has been submitted.");
});
});
I also put console.log(data that I've changed) right before the (data that I've changed).save(function( ...)) code.
What can I do to fix this?
EDIT (HOW I SOLVED THIS):
It appears that Mongo does not save my array when I access the array and modify them like:
data.voters[0][username] = value;
Instead I resorted to popping out the object, modifying it, and then pushing it back in. Now I see the values being properly saved/updated in the database.
Aka something like:
var obj = data.voters.pop();
obj[username] = value;
data.voters.push(obj);
Anyone know why this happens?
Related
I don't get results from nested query, loc is always null. The query parameter has proper value when I print it, and the database collection 'users' has documents with ids from the array friendsP.
var acquireFriendsPositions = function(db, id, res, callback) {
var cursor = db.collection('users').find({"_id" : new ObjectId(id)}, {_id:0, friends:1});
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
friendsP = doc.friends;
console.log(friendsP); //I get the array friendsP
for(var i =0; i<friendsP.length; i++)
{
console.log(friendsP[i]); //friendsP[i] has proper value
var curs = db.collection('users').find({"_id" : new ObjectId(friendsP[i])}); //but query returns null
curs.each(function(err, loc) {
//assert.equal(err, null);
if(loc!= null) {
console.log(loc);
friendsPos.push(loc);
}
else {
console.log("else");
}
});
}
promise(friendsPos, res); //here i wait for friendsPos and use it in res.send(), but friendsPos is empty because loc is always null
} else {
callback(); //callback does db.close();
}
});
};
If this is the exact code that you are using I suspect that the friendsP value gets hoisted and overwritten in the next each cycle. Meaning that you should be able to fix this by simply changing the code to var friendsP = doc.friends so the friendsP variable is in the function scope. If this is what is happening this is a nasty bug and you should always the declare the variables with a local scope to prevent this from happening.
Try using this for casting Object Id:
var mongodb = require('mongodb');
mongodb.ObjectID.createFromHexString(friendsP[i]);
Thank you, guys. Actually, the problem was callback() which was closing the connection before queries were executed. Here is my new code:
var acquireFriendsPositions = function(db, id, res, callback) {
db.collection('users').findOne({"_id" : new ObjectId(id)},
function(err, item) {
var friendsP = item.friends;
var locFriends = [];
promise(locFriends, res);
var x = 0;
for(i =0; i<friendsP.length; i++)
{
db.collection('users').findOne({"_id" : friendsP[i]}, function(err,subItem){
x=x+1;
//console.log(subItem);
locFriends.push(subItem);
if(x==friendsP.length)
callback();
});
}
});
};
I have a small program that reads each of the record and update each of the record. Given the async nature of node and callback. what is the efficient and the correct way to close the db connection?
Sample Program:
var MongoClient = require('mongodb').MongoClient;
var updateCount = 0;
MongoClient.connect('mongodb://localhost:27017/school', function(err, db) {
if(err) throw err;
var query = { };
// get all the students in the database
var cursor = db.collection('students').find(query);
cursor.each(function(err, doc) {
if(err) throw err;
if(doc == null) {
return;
}
// filter out only the homework scores
var homeworksOnly = doc.scores.filter(function(scores){
if (scores.type === "homework") return true;
return false;
})
// filter out the non homework scores
var notHomeWorks = doc.scores.filter(function(scores){
if (scores.type !== "homework") return true;
return false;
})
// sort the homework score to remove the min score from the list.
homeworksOnly.sort(function(a,b){
if (a.score > b.score) return 1;
if (b.score > a.score) return -1;
return 0;
});
console.log("Before removing the min score"+doc._id);
console.dir(homeworksOnly);
console.log("After removing the min score"+doc._id);
homeworksOnly.splice(0,1);
console.dir(homeworksOnly);
console.log("Merge the homework with other scores"+doc._id);
var newScores = homeworksOnly.concat(notHomeWorks);
console.dir(newScores);
console.log("*****");
// Now update the database for this student with the new scores
var search = {"_id":doc._id};
var operator = { '$set' : { 'scores' : newScores } };
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
});
});
});
Now the program works but I need to hit the Ctrl+C to terminate the program. Is there a way to know that all the callbacks have completed so that the program can be terminated?
There are better libaries you can integrate with nodejs to handle the callback flow better, but simply working with the basic driver as a dependency, all you need is the basic node stream interface which is already built in to the cursor.
This allows .pause() and .resume()for flow control when processing, and an "end" event when the cursor stream is complete:
var MongoClient = require('mongodb').MongoClient;
var updateCount = 0;
MongoClient.connect('mongodb://localhost:27017/school', function(err, db) {
if(err) throw err;
var query = { };
// get all the students in the database
var cursor = db.collection('students').find(query);
// called on errors
cursor.on("error",function(err) {
throw err;
});
// called on stream complete
cursor.on("end",function() {
db.close();
});
// process each document in the stream
cursor.on("data",function(data) {
cursor.pause(); // stops the cursor stream while processing
// filter out only the homework scores
var homeworksOnly = doc.scores.filter(function(scores){
if (scores.type === "homework") return true;
return false;
})
// filter out the non homework scores
var notHomeWorks = doc.scores.filter(function(scores){
if (scores.type !== "homework") return true;
return false;
})
// sort the homework score to remove the min score from the list.
homeworksOnly.sort(function(a,b){
if (a.score > b.score) return 1;
if (b.score > a.score) return -1;
return 0;
});
console.log("Before removing the min score"+doc._id);
console.dir(homeworksOnly);
console.log("After removing the min score"+doc._id);
homeworksOnly.splice(0,1);
console.dir(homeworksOnly);
console.log("Merge the homework with other scores"+doc._id);
var newScores = homeworksOnly.concat(notHomeWorks);
console.dir(newScores);
console.log("*****");
// Now update the database for this student with the new scores
var search = {"_id":doc._id};
var operator = { '$set' : { 'scores' : newScores } };
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
cursor.resume(); // restarts the stream processing now we are done
});
});
});
After the update statement is done use
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
});
db.close();
So let's say I have the following for loop
for(var i = 0; i < array.length; i++){
Model.findOne({ _id = array[i].id}, function(err, found){
//Some stuff
});
}
How do I make this code work? Every time I run it I get array[i] = undefinedbecause the mongo-db query is asynchronous and the loop has already iterated 5 times by the time the first query is even completed. How do I go about tackling this issue and waiting for the query to complete before going on to the next iteration?
This doesn't specifically answer your question, but addresses your problem.
I'd use an $in query and do the filtering all at once. 20 calls to the db is pretty slow compared to 1:
// grab your ids
var arrayIds = myArray.map(function(item) {
return item._id;
});
// find all of them
Model.find({_id: {$in: arrayIds}}, function(error, foundItems) {
if (error) {
// error handle
}
// set up a map of the found ids
var foundItemsMap = {};
foundItems.forEach(function(item) {
foundItemsMap[item._id] = true;
});
// pull out your items that haven't been created yet
var newItems = [];
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
if ( foundItemsMap[arrayItem._id] ) {
// this array item exists in the map of foundIds
// so the item already exists in the database
}
else {
// it doesn't exist, push it into the new array
newItems.push(arrayItem);
}
}
// now you have `newItems`, an array of objects that aren't in the database
});
One of the easiest ways to accomplish something like you want is using promises. You could use the library q to do this:
var Q = require('q');
function fetchOne(id) {
var deferred = Q.defer();
Model.findOne({ _id = id}, function(err, found){
if(err) deferred.reject(err);
else deferred.resolve(found);
});
return deferred.promise;
}
function fetch(ids, action) {
if(ids.length === 0) return;
var id = ids.pop();
fetchOne(id).then(function(model) {
action(model);
fetch(ids, action);
});
}
fetch([1,2,3,4,5], function(model) { /* do something */ });
It is not the most beautiful implementation, but I'm sure you get the picture :)
Not sure if this is the right way, it could be a bit expensive but this how i did it.
I think the trick is to pull all your data and then looking for an id match.
Model.find(function(err, data) {
if (err) //handle it
for (var i=0; i<array.length; i++) {
for (var j=0; ij<data.length; j++) {
if(data[j].id == array[i].id) {
// do something
}
}
}
}
I am new to node.js and mongodb and I have the following problem:
I need to drop all collections from my mongodb from node.js file. I have such a function:
service.dropCollections = function(db, colls){
for(var i = 0; i < colls.length; i++){
var name = colls[i].name;
db.dropCollection(name, function(err) {
if(!err) {
console.log( name + " dropped");
} else {
console.log("!ERROR! " + err.errmsg);
}
});
}
}
And I am using it in the following function:
service.clearDB = function() {
var MongoClient = require('mongodb').MongoClient
, format = require('util').format;
MongoClient.connect('mongodb://127.0.0.1:27017/shiny_d', function(err, db){
if(err) throw err;
db.collectionNames(function(err, collections){
if(!err){
service.dropCollections(db, collections);
} else {
console.log("!ERROR! "+ err.errmsg);
}
service.showCollections();
});
});
}
As an output I have
!ERROR! ns not found
shiny_db.physicalinfos
Dunno what to do right now. I'll be very thankful for your help.
Ain't it more faster, easier and less error prone if you just drop the entire database?
db.dropDatabase();
At least from the Mongo CLI, whenever you access an inexistent DB, it'll be persisted the soon you create data. That's the same as dropping all collections from it.
I haven't tried MongoDB for anything except studying yet, so I don't know much about permissions. So, probably the only problem of dropping the entire DB would be the permissions of your users that would be lost (I believe).
If this script you're trying to create is not for production, then you're good to go with dropping the DB.
I found an answer. First of all I've made mistake in my connection it should be like following: 'mongodb://127.0.0.1:27017/shiny_db'. The second mistake was in the name of collection. It was like 'db_name.coll_name', that's why db.dropCollection(name, callback) couldn't find particular collection and because of it I had mistake ns not found. So I've used following mechanism to separate db_name from coll_name:
var name = colls[i].name.substring('shiny_db.'.length); and I've added checking for "system" collection.
Final code looks like following:
service.clearDB = function() {
var MongoClient = require('mongodb').MongoClient
, format = require('util').format;
MongoClient.connect('mongodb://localhost/shiny_db', function(err, db) {
if(err) throw err;
db.collectionNames(function(err, collections){
if(!err){
service.dropCollections(db, collections);
} else {
console.log("!ERROR! "+ err.errmsg);
}
});
});
}
service.dropCollections = function(db, colls){
for(var i = 0; i < colls.length; i++){
var name = colls[i].name.substring('shiny_db.'.length);
if (name.substring(0, 6) !== "system") {
db.dropCollection(name, function(err) {
if(!err) {
console.log( name + " dropped");
} else {
console.log("!ERROR! " + err.errmsg);
}
});
} else {
console.log(name + " cannot be dropped because it's a system file");
}
}
}
Hope it will help someone!
listCollections gives you an array of collection names as strings.
It looks like you may be confusing it with something that returns an array of collection objects like maybe db.collections()
In my code below, my value printed out at the console.log is correct, but when I search and go about entering the objects into the db, all the objects in the db contain the same hex, and image path but the id's are different. I tried first using findOne but the resulted in the same outcome. I am new to MongoDb so I am assuming it is just somethign I am doing stupid. Any ideas please send them my way :)
exports.addImage = function(req,res){
var params = req.body;
var colors = params.color;
var passedImg = params.path;
var ndxobj;
for(var index in colors){
ndxobj = colors[index];
//Values here are the correct index and contain valid data
console.log("col: ", ndxobj);
var query = clrModel.find({hex: ndxobj.hex}, function(err,result){
if(!err && result.length > 0){
console.log(result);
}else if(!err){
//We have an empty db for the searched obj
var locclr = new clrModel({
hex: ndxobj.hex
});
locclr.img.push({path:passedImg, date:ndxobj.imagedate});
locclr.save(function(error, data){
if(error){
console.log("Error in addImage find call: ",error);
res.json(error);
}
else{
console.log("Saving: ",data);
res.json(data);
}
});
}else {
//Handle error
}
});
}
};
I think that your paths are all the same because you set path to be passedImage, and passedImage is not updated from each index, but is set at the top of your code sample. As for the hex values being all the same, that seems to be happening because the callbacks are closing over ndxobj, so by the time they're called, all of them are looking at the same value. To make that work, you'll want to use a function to create your callbacks, something like what follows (hopefully I closed all my parens & brackets...). See this StackOverflow post for more info.
exports.addImage = function(req,res){
var makeCallback=function(ndxobj){
return function(err,result){
if(!err && result.length > 0){
console.log(result);
}else if(!err){
//We have an empty db for the searched obj
var locclr = new clrModel({
hex: ndxobj.hex
});
locclr.img.push({path:passedImg, date:ndxobj.imagedate});
locclr.save(function(error, data){
if(error){
console.log("Error in addImage find call: ",error);
res.json(error);
}else{
console.log("Saving: ",data);
res.json(data);
}
});
}else{
//Handle error
}
};
});
var params = req.body;
var colors = params.color;
var passedImg = params.path;
var ndxobj;
for(var index in colors){
ndxobj = colors[index];
//Values here are the correct index and contain valid data
console.log("col: ", ndxobj);
var query = clrModel.find({hex: ndxobj.hex}, makeCallback(ndxobj.hex));
}
};