Mongodb findAndModify node js - javascript

Following code gives me an exception in node js saying: "need to remove or update"
var args = {
query: { _id: _id },
update: { $set: data },
new: true,
remove: false
};
db.collection(COLLECTION.INVENTORY_LOCATION)
.findAndModify(args, function (err, results) {
if (err) {
return callback(err);
} else {
console.log(results);
callback(null, results);
}
});
Not able to figure out the issue as I have specified the update operation.

The syntax is different in the node driver than for the shell, which is the syntax you are using.
db.collection("collection_name").findAndModify(
{ _id: _id }, // query
[], // represents a sort order if multiple matches
{ $set: data }, // update statement
{ new: true }, // options - new to return the modified document
function(err,doc) {
}
);
There is a separate function for .findAndRemove()

As the documentation for the remove parameter of the findAndModify function states:
remove: <boolean>:
Must specify either the remove or the update field. Removes the
document specified in the query field. Set this to true to remove the
selected document . The default is false.
The default value is false so you don't have to provide it at all.
I believe the issue is that you are supplying both update and remove parameters. Try removing the remove parameter.

Related

Meteor MongoDB Setting field of array per document

I have a Documents in a Collection that have a field that is an Array (foo). This is an Array of other subdocuments. I want to set the same field (bar) for each subdocument in each document to the same value. This value comes from a checkbox.
So..my client-side code is something like
'click #checkAll'(e, template) {
const target = e.target;
const checked = $(target).prop('checked');
//Call Server Method to update list of Docs
const docIds = getIds();
Meteor.call('updateAllSubDocs', docIds, checked);
}
I tried using https://docs.mongodb.com/manual/reference/operator/update/positional-all/#positional-update-all
And came up with the following for my Server helper method.
'updateAllSubDocs'(ids, checked) {
Items.update({ _id: { $in: ids } }, { $set: { "foo.$[].bar": bar } },
{ multi: true }, function (err, result) {
if (err) {
throw new Meteor.Error('error updating');
}
});
}
But that throws an error 'foo.$[].bar is not allowed by the Schema'. Any ideas?
I'm using SimpleSchema for both the parent and subdocument
Thanks!
Try passing an option to bypass Simple Schema. It might be lacking support for this (somewhat) newer Mongo feature.
bypassCollection2
Example:
Items.update({ _id: { $in: ids } }, { $set: { "foo.$[].bar": bar } },
{ multi: true, bypassCollection2: true }, function (err, result) {
if (err) {
throw new Meteor.Error('error updating');
}
});
Old answer:
Since you say you need to make a unique update for each document it sounds like bulk updating is the way to go in this case. Here's an example of how to do this in Meteor.
if (docsToUpdate.length < 1) return
const bulk = MyCollection.rawCollection().initializeUnorderedBulkOp()
for (const myDoc of docsToUpdate) {
bulk.find({ _id: myDoc._id }).updateOne({ $set: update })
}
Promise.await(bulk.execute()) // or use regular await if you want...
Note we exit the function early if there's no docs because bulk.execute() throws an exception if there's no operations to process.
If your data have different data in the $set for each entry on array, I think you need a loop in server side.
Mongo has Bulk operations, but I don't know if you can call them using Collection.rawCollection().XXXXX
I've used rawCollection() to access aggregate and it works fine to me. Maybe work with bulk operations.

Mongodb version 3+: how to insert new document only if it doesn't exist and get the result

I have tried dozens of solutions, and none of these worked and most of them are deprecated.
I have a collection with documents like this:
_id: 5d99ef3285c93711828cd15d
code: 1234
name: "Foo"
surname: "Bar"
address: "That street"
phone: 1234567
I would like to insert new document only if there isn't any document with the same code.
My last try was this:
const result = await db.collection('users').findOneAndUpdate(
{ code: user.code },
{
$setOnInsert: user,
},
{
upsert: true,
}
);
but I get E11000 duplicate key error collection...
updateOne() returns the same error; update() is deprecated...
So, how to add only new document and get the result (true if document has been added or false if it already exists)?
Thank you.
As far as my knowledge is,
with $set and $setOnInsert, we can not update/insert the same field.
i.e. $set and $setOnInsert should be mutually exclusive.
It works if the document is being updated, but throws an exception if document is being inserted.
In case of update, $setOnInsert will be ignored.
In case of insertion, both will be executed.
I think the solution would be,
use returnNewDocument and have one field in the schema isUpdated defaults to false.
Note:
make sure whenever you use "insert" operation on the collection, you don't add isUpdated which will be set to false then or set it to false.
form a query like
db.collection('users').findOneAndUpdate(
{ code: user.code },
{
$set: user, // which has user.isUpdated set to true
},
{
upsert: true,
returnNewDocument: false, // (by default it is false only)
}
);
With this logic,
So let's go step by step,
If the document doc1 is not present, it will be inserted, and mongo will return the response null. You will know, it is Inserted.
If the document doc2 is present(considering this logic is not applied on the previously inserted document doc2 before and isUpdated field is not present in doc2), it will execute $set so in returned cursor, this field not present i.e. undefined, so you know from this, it is updated.
let's say we fire the same query for doc1 again (which is present and we have applied our new logic), then there are two cases
a. it will be updated and in the cursor, we have isUpdated equal to false.
b. it will be updated and in the cursor, we have isUpdated equal to true.
In both case you know it is Updated
I think this approach should solve your problem.
Please share if this helps you, or you find any other solution.
UPDATE
ACTUALLY
You dont even need another field isUpdated, without this fiels this should work with the same logic.
i.e. If cursor is null then its inserted, if not null then its updated.
You can still run a query like this;
document = db.collection('users').findOne({code:userCode});
if(document == null){
//document doesn't exists so you can use insertOne
}
else{
// document exists, sou you can update
}
it won't be efficient but it'll do the work.
You can simply wrap the request with a try/catch block to catch the Error. Then return false when this exception occured.
You could use findOne to see if a user with that code exists first
const result = await db.collection('users')
.findOne({ code: user.code });
if( result ){
return res
.status(400)
.json({ errors: [{ msg: 'User already exists' }] });
}
//create
user = new User({
code = code,
name = name,
foo = foo
)}
//save
user.save();
res.json(user);
Try this one
db.collection("users").findOne({ code: user.code }, (err, data) => {
if (data) {
return res.send(false);
} else {
// a document
var user = new User({
code: code,
name: "Foo",
surname: "Bar",
address: "That street",
phone: 1234568
});
// save model to database
user.save(function(err, book) {
if (err) return console.error(err);
return res.send(true);
});
}
});
Users.findOneAndUpdate({code:user.code}, {upsert: true, new: true, setDefaultsOnInsert: true }, function(error, result) { if (error) return; // do something with the document }).
I think it would work. Let us know if you have any questions.

MongoDB/Mongoose - Adding an object to an array of objects only if a certain field is unique

So I have a nested array of objects in my MongoDB document and I would like to add a new object to the array only if a certain field (in this case, eventId) is unique. My question is very similar to this post, only I cannot seem to get that solution to work in my case.
Here is what the documents (UserModel) look like:
{
"portal" : {
"events" : [
{
"important" : false,
"completed" : false,
"_id" : ObjectId("5c0c2a93bb49c91ef8de0b21"),
"eventId" : "5bec4a7361853025400ee9e9",
"user_notes" : "My event note"
},
...and so on
]
}
}
And here is my (so far unsuccessful) Mongoose operation:
UserModel.findByIdAndUpdate(
userId,
{ "portal.events.eventId": { $ne: req.body.eventId } },
{ $addToSet: { "portal.events": req.body } },
{ new: true }
);
Basically I am trying to use '$ne' to check if the field is unique, and then '$addToSet' (or '$push', I believe they are functionally equivalent in this case) to add the new object.
Could anyone point me in the right direction?
Cheers,
Gabe
If you look into the documentation on your method you will see that the parameters passed are not in the proper order.
findByIdAndUpdate(id, update, options, callback)
I would use update instead and have your id and portal.events.eventId": { $ne: req.body.eventId } part of the initial filter followed by $addToSet: { "portal.events": req.body }
Something among these lines:
UserModel.update(
{
"_id": mongoose.Types.ObjectId(userId),
"portal.events.eventId": { $ne: req.body.eventId }
},
{ $addToSet: { "portal.events": req.body } },
{ new: true }
);
You need to include your eventId check into condition part of your query. Because you're usig findByIdAndUpdate you can only pass single value matched against _id as a condition. Therefore you have to use findOneAndUpdate to specify custom filtering condition, try:
UserModel.findOneAndUpdate(
{ _id: userId, "portal.events.eventId": { $ne: req.body.eventId } },
{ $addToSet: { "portal.events": req.body } },
{ new: true }
);

Update a row in nedb

I have the following data in nedb.
["UserId":"1446943507761","UserName":"xxx","link":"xxx.html","taskDone":"false","id":14,"_id":"fdaaTWSxloQZdYlT"]
["UserId":"1446943507761","UserName":"xxx","link":"xxx.html","taskDone":"false","id":1,"_id":"fzh2cedAXxT76GwB"]
["UserId":"1446943507761","UserName":"xxx","link":"xxx.html","taskDone":"false","id":0,"_id":"k4loE7XR5gioQk54"]
I am trying to update row with id 0 and set the value of taskDone to true. I use the following query to set the value to true
db.taskmap.update({ _id: "k4loE7XR5gioQk54", UserName:"xxx" }, { $set: { taskDone: "true"} }, function (err, numReplaced) {
console.log("replaced---->" + numReplaced);
});
It updates the value but it updates as a new row. It basically inserts a new row with same values except for the taskdone value as true. It does not delete the existing data. Hence in the final data table after update i get tow rows for id 0 with all values same except for the taskDone. I am not sure if i am doing anything wrong. It will be helpful if anybody can tell me a correct way of updating the value.
You should call db.loadDatabase(); again at the end of db.update(); to see, that no second row with the same _id: appears instead the specific doc gets directly updated.
Edit:
It appears that sometimes when you do db.update() the document that should be updated appears twice instead on the database. It happened to me when I was updating an entry in a list of servers, multiple entries with the modified values were appearing on the database. So to avoid this simply do the following. (I took the same code that was suggested as the answer)
db.update(
{ _id: "k4loE7XR5gioQk54", UserName:"xxx" },
{ $set: { taskDone: "true"} },
{},// this argument was missing
function (err, numReplaced) {
console.log("replaced---->" + numReplaced);
db.loadDatabase();
}
);
Doing this prevents this from happening. I tested it multiple times and the issue disappeared.
update wants four arguments
var Datastore = require('nedb');
var db = new Datastore();
db.insert(
[
{
"UserId":"1446943507761",
"UserName":"xxx",
"link":"xxx.html",
"taskDone":"false",
"id":14,
"_id":"fdaaTWSxloQZdYlT"
},
{
"UserId":"1446943507761",
"UserName":"xxx",
"link":"xxx.html",
"taskDone":"false",
"id":1,
"_id":"fzh2cedAXxT76GwB"
},
{
"UserId":"1446943507761",
"UserName":"xxx",
"link":"xxx.html",
"taskDone":"false",
"id":0,
"_id":"k4loE7XR5gioQk54"
}],
function (err, newDocs) {
// empty here
}
);
db.update(
{ _id: "k4loE7XR5gioQk54", UserName:"xxx" },
{ $set: { taskDone: "true"} },
{},// this argument was missing
function (err, numReplaced) {
console.log("replaced---->" + numReplaced);
}
);
// should give the correct result now
db.find({}).exec(function (err, docs) {console.log(docs);});

MongoDB findAndModify not working with monk

I am trying to use findAndModify with the node.js mongodb module monk.This is the method that I am using,this throws a 500 error in my cmd:
notesCollection.findAndModify({_id:_id},[],{_id:_id,title:title,content:content},{'new':true,'upsert':true},function(err,doc){
if(err)
console.error(err);
else
{
console.log("Find and modify successfull");
console.dir(doc);
}
});
I obtained the method signature here.I get an error that looks like this and is uninformative:
POST /notes/edit/542bdec5712c0dc426d41342 500 86ms - 1.35kb
Monk implements methods that are more in line with the shell syntax for method signatures than what is provided by the node native driver. So in this case the "shell" documentation for .findAndModify() is more appropriate for here:
notescollection.findAndModify(
{
"query": { "_id": id },
"update": { "$set": {
"title": title,
"content": content
}},
"options": { "new": true, "upsert": true }
},
function(err,doc) {
if (err) throw err;
console.log( doc );
}
);
Also noting that you should be using the $set operator or posibly even the $setOnInsert operator where you only want fields applied when the document is created. When operators like this a re not applied then the "whole" document is replaced with whatever content you specify for the "update".
You also don't need to supply the "_id" field in the update section, as even when an "upsert" occurs, anything present in the "query" portion of the statement is implied to be created in a new document.
The monk documentation also hints at the correct syntax to use for the method signature.
Had the same problem, and even though I liked it, the accepted answer didn't work for me.
It's not clear enough, but the documentation hints at the correct syntax, starting with the signatures:
All commands accept the simple data[, …], fn. For example
findAndModify({}, {}, fn)
And from the finding section:
users.findAndModify({ _id: '' }, { $set: {} });
Finally, continuing with the signatures section:
You can pass options in the middle: data[, …], options, fn
Putting it all together:
collection.findAndModify({
_id: '',
}, {
$set: {
value: '',
},
}, {
upsert: true,
});
So in this case, data[, …] is the couple {}, {} objects: query and update. Then you can add the callback as a 4th parameter in my snippet.

Categories