Mongoose: Documents aren't updating - node.js

I'm finding documents by _id in a loop and updating a boolean in each document:
db.items.findById(key, function(error, item) {
item.flags.cake = false;
item.update(function(error, zzz) {
if(error) return next(error);
console.log('output ',zzz);
});
});
But the documents will not update. The mongoose schema for item:
flags: {
cake:Boolean
}

Use the save() method instead which makes use of a callback that will receive three parameters you can use:
1) err if an error occurred
2) item which is the saved item
3) numAffected will be 1 when the document was successfully persisted to MongoDB, otherwise 0.
Items.findById(key, function(error, item) {
item.flags.cake = false;
item.save(function (err, item, numAffected) {
if (err) console.log(err)
console.log('output ', item);
});
});
As an extra measure of flow control, save will return a Promise.
item.save().then(function(item) {
console.log('output ', item);
});

Related

Update an array of documents in Mongoose

How can I update ALL documents in a collection where an attributes value needs to be different (a unique number) for each document?
Below is my current code. This actually seems to update (I don't get an error) but the values in the db are not being updated.
Model.find({}, function (err, docs) {
if (err) {
console.log(err);
};
if(docs && docs.length > 0){
for(var i=0; i<docs.length; i++){
//SET NEW VALUE FOR EACH DOC - VALUE MUST BE UNIQUE TO EACH DOC
docs[i].code = generateRandomCode();
}
// PASS IN ARRAY OF UPDATED DOCS TO BE SAVED
Model.update(docs, function (err, docs) {
if (err) {
console.log(err);
}
if(!err){
req.updatedSuccessfully = true;
}
return next();
});
}
else{
return next();
}
});
Before this I was trying to do something like this:
Model.update({}, { code: generateRandomCode() }, { multi: true }, function (err, numberAffected, raw) {
if (err) return handleError(err);
console.log('The number of updated documents was %d', numberAffected);
console.log('The raw response from Mongo was ', raw);
});
The problem with this is that generateRandomCode() is only called once but I need to create a different code for each document. So neither of these example work.
Instead of trying model.update(), can you try to simply save the documents?
See answer to this question on this url: Update model with Mongoose, Express, NodeJS

Making butch upsert in mongodb: callback never fired

I have an array of documents with unique _id and I want to insert them to my database. Some of them already in db, and for those I want to update an array property (push in array an item). All of this I need to make asyncronuosly, so after all inserted/updated I want to write response back (with callback) to client than all ok or write an error. After googling on subject I've found this solution with async module I've tried to implement it for my case. Now my code looks like this:
function processUsers(arr, listName, callback) {
var users = global.db.collection('vkusers');
var q = async.queue(function(task, cb) {
console.log('upsert butch');
users.insert(task.doc, function(err, doc) {
if (err) {
users.update({
_id : task.doc._id
}, {
$addToSet : {
mLists : listName
}
}, function(error, result){ console.log(error); console.log(result); });
}
});
}, arr.length);
for ( var doc in arr) {
q.push({
doc : arr[doc]
}, function(err) {
if (err)
callback(err, null);
})
}
q.drain = function() {
// this is the queue's callback, called when the queue is empty,
// i.e. when all your documents have been processed.
console.log('drain');
callback(null, { result: "success", upserted: arr.length });
}
}
Callback has signature callback(error, result), arr - my array of documents. I've tested it and with database everything is OK, i am getting the right result. But callback, and q.drain never fired!
You need to call async.queue's callback (cb in your code) when your insert/update is complete. Something like this:
var q = async.queue(function(task, cb) {
console.log('upsert butch');
users.insert(task.doc, function(err, doc) {
if (err) {
users.update({
_id : task.doc._id
}, {
$addToSet : {
mLists : listName
}
}, function(error, result) {
console.log(error);
console.log(result);
cb(error); // Update finished; call cb and pass in "error" so that it can bubble up if it exists
});
} else {
cb(); // Insert succeeded; call cb
}
});
}, arr.length);

How do I remove documents after search results using Node.js Mongoose?

AAAModel.find({'category' : category})
.skip(100)
.sort({date: 'desc'})
.exec(function(err, result) {
if (err) {
next(err);
}
if (result) {
result.remove();
}
});
the above doesn't work.
I would like to remove the 100 Items or more of the search results, what should I do?
You could try one of this approach:
Model.findOne({_id: 'specific_id'}, (err, doc) => {
doc.remove((err) => {
if (err) // handle err
});
// or simply use
doc.remove();
});
or
Model.findOne({_id: 'specific_id'}).remove((err, doc) => {
});
or
Model.remove({_id: 'specific_id'}, (err, doc) => {
});
Use this query
AAAModel.find(
{'category' : category},
{ skip: 100,sort:{date: -1}},
function(err, results) {
if (err) {
next(err);
}
if (result) {
result.remove();
}
});
I've got the same problem.
In fact in latest mongoose query has remove method. So, it theory it could work like this:
AAAModel.find({'category' : category})
.skip(100)
.sort({date: -1})
.remove(function(err, result) {
if (err) {
next(err);
}
if (result) {
console.log("Number of deleted records:" + result);
}
});
But in my case this code removed all records
So, the possible solution is
AAAModel.find({'category' : category})
.select("_id")
.skip(100)
.sort({date: -1})
.exec(function(err, idObjLst) {
if (err) {
return next(err);
}
var ids = idObjLst.map(function(idObj) { return idObj._id; });
if (ids && ids.length > 0) {
AAAModel.remove({_id: {$in: ids}}).exec(function(err, result) {
console.log("Removed " + result + " elements");
});
});
Could be quite expensive though.
As we know, the parameter results is an array of documents in your find callback, not a single document. so you can iterate it and remove item one by one, despite this is not best way to remove documents for performance.
Actually, mongoose Mode.remove can be used to remove documents, but the query condition can not skip and sort until now.

cannot print all elements in collection although count is positive

I can get positive count of elements in collection ...
var collection;
collection = db.collection("allCodes");
collection.count(function(err, count) {
if (err) {
throw err;
}
console.log("There are " + count + " records.");
});
... with output:
Connected to Database ok
There are 354 records.
... but can not get elements in this collection:
collection.find().each(function(err, doc) {
if (err) {
throw err;
}
console.log("each doc");
console.log(doc);
});
... it prints nothing. I am new in mongodb. So what I do wrong? I want to print all elements in allCodes collection.
Update: all code that inserts data then gets count and then tries to fetch data itself, but nothing comes out.
var MongoClient, collection;
MongoClient = require("mongodb").MongoClient;
var objectToInsert = [{
'a': 1
}, {
'a': 2
}, {
'b': 3
}]
MongoClient.connect("mongodb://127.0.0.1:27017/test", function(err, db) {
console.log("Connected to Database");
collection = db.collection("test2");
// clear collection -------------------------------
collection.remove(function(err, result) {
// insert ------------------------------------
collection.insert(objectToInsert, function(docs) {
// count - ok -----------------------------------
collection.count(function(err, count) {
console.log("Count: " + count);
// find - fail - no objects printed -----------------------
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array");
docs.forEach(function(doc) {
console.log("Doc from array");
console.log(doc);
});
});
db.close();
});
});
});
});
It has output:
Connected to Database
Count: 3
So why I have only count. Where is my data?
You're closing your connection to the database before the find gets a chance to complete.
Move the db.close() call inside the callback of toArray like this:
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array");
docs.forEach(function(doc) {
console.log("Doc from array");
console.log(doc);
});
db.close();
});
Disclaimer: I am not familiar with node.js at all.
From the example in the documentation, it seems that you are required to create a cursor object first and only afterwards iterate over the results. I'm not sure what considerations there are for chaining commands.
var cursor = collection.find();
// Execute the each command, triggers for each document
cursor.each( function( err, item ) {
console.log( "each doc" );
});

How to commit transaction after loop over asynchonous updates

I'm using node-mysql-queues to handle database transactions in my application.
for (lineitem in lineitems) {
transaction.query("SELECT n from inventory WHERE productId = ?", [lineitem], function (err, rows) {
if (err)
transaction.rollback();
var newN = rows[0].n - lineitems[lineitem].quantity;
if (newN >= 0) {
transaction.query("UPDATE inventory SET n = ? WHERE productId = ?", [newN, lineitem], function (err) {
if (err){
transaction.rollback();
console.log(err);
}
//here I want to commit if all updates were successfull!!!
});
}
})
}
As you can see in the code, I don't know how to handle the commit part. If it was synchronous it would be easy, but don't know how ro solve this problem.
Thanks & Regards
This is easy with something like the async module.
async.each(lineitems, performQuery, function(err) {
if(err) {
transaction.rollback();
console.log(err);
return;
}
transaction.commit();
});
function performQuery(lineitem, callback) {
transaction.query("SELECT n from inventory WHERE productId = ?", [lineitem], function (err, rows) {
if (err) return callback(err);
var newN = rows[0].n - lineitems[lineitem].quantity;
if (newN >= 0) {
transaction.query("UPDATE inventory SET n = ? WHERE productId = ?", [newN, lineitem], function (err) {
if (err) return callback(err);
callback();
});
}
});
}
I found a solution for my problem. Since I had problems with doing a select and then an update depending on the result of the select, I implemented something like a conditional update.
But see my code:
mysql.getTransaction(function (err, transaction) {
//For each item in the cart, call the performUpdate method
//If an error occures, rollback the whole transaction
async.each(lineitems, performUpdate, function (err) {
if (err) {
transaction.rollback();
res.json(err.message);
return;
}
//Since we are going to call another callback, we need to pause the transaction, else it would be committed automatically
transaction.pause();
//If the Updates were successfull, create an Order in MongoDB
orderController.createMongoOrder(lineitems, req.session.cart.total, req.session.passport.user, function (err) {
if (err) {
//If there is a Problem with Mongo, cancel the transaction
transaction.resume();
transaction.rollback();
res.json(err.message);
} else {
//Else commit the transaction and empty the cart
transaction.resume();
transaction.commit();
req.session.cart = {
products: {},
count: 0,
total: 0
};
res.json("Order accepted!");
}
})
});
function performUpdate(lineitem, callback) {
//This query can be seen as conditional update. If the number of articles in stock is not sufficient, there will be no affectedRows in the returned info message
transaction.query("UPDATE inventory SET n = n -? WHERE productId = ? AND n >= ?", [lineitem.quantity, lineitem.id, lineitem.quantity],function (err, info) {
if (err) {
return callback(err);
} else {
//if for any item there is no affectedRow, this means the Updated failed. This should make the whole transaction roll back so we return an error to the callback
if (info.affectedRows != 1) {
return callback(new Error("Article: " + lineitem.productObject.name + " out of stock!"))
}
return callback(null, info);
}
}).execute()
}
})

Resources