$addToSet and upsert - node.js

I know I can't use upsert and the positional operator together, but I am looking for away to append to array if some fields of the object I am inserting do not match some fields in an existing object within the array.
so if I have the existing document below, I would like to check for 'field' field's value and update/replace that subdocument if the fields match, and simply append to the array if they don't.
{
myArray:[
{
field:'xyz'
}
]
}
Is there a good way to do this in node.js? I'm using the native driver.

I don't think you can do this in a single query. You can do this with two separate queries in the native driver. The first query would try to update the field in the array. If it doesn't find the document in the array that matches, it dispatches a second query to append the document to the array.
db.collection('coll').update({_id: _id, "myArray": {field: "xyz"}}, {"$set": {"myArray.$": {field: "xyzt"}}}, {upsert: true}, function(err, res) {
if (err && err.code == 16836) { // no document was matched
db.collection('coll').update({_id: _id}, {"$push": {myArray: {field: "xyzt"}}}, function(err, res) {
console.log("Inserted document in array");
});
}
console.log("Updated document in array");
});

Related

How can I filter arrays in mongodb using mongoose?

I have created database with two collections. Each of these collections connected with relations.
Here I want to pass one item _id and check whether it passed to other collection as foreign key. If it's passed, I want to filter all items which consist as _id. How can I do that. Here my mongoose query and screenshot of db. Thank you
route.get("/:id",(req,res)=>{
Vehicles.find({
categories: [req.params.id]
}, (err, data)=>{
if(err){
console.log(err);
}else{
console.log(data);
}
});
PS: For an example I want to get all vehicles which have category id "60c58c2dcf82de0780051378" inside categories array.
Following the mongo document, you can query for all documents where categories is an array that contains the objectId req.params.id as one of its elements.
Vehicles.find({
categories: req.params.id,
}, (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});

MongoDB upsert creates another instance

I currently have an upsert function in my project which works but my main problem is that it creates another instance of the record, and updates the new instance instead. This is the code:
router.route('/carousel/update/:_id').put(function(req, res) {
var id;
if(req.params._id == 'undefined'){
id = crypto.randomBytes(12).toString('hex');
}
else {
id = ObjectId(req.params._id)
}
db.collection('home').updateOne({"_id": id},
{$set: req.body}, {upsert: true}, (err, results) => {
if (err) throw err;
res.send(results)
console.log(req.body)
});
});
The problem:
1. It mystifies me that mongoDB takes my crypto generated _id and takes it as the new _id for the upserted document. Why is that? When {upsert: true}, isn't mongoDB supposed to generate a new _id?
2. Because of the nature of problem 1, whenever I try to update the original document, it updates the upserted document instead since they have the same _id values even though their _ids are positioned at different document levels.
In conclusion, when given a 'home' document, how do I upsert correctly without adding a new record with the same values and _ids?
Thanks for your help!
EDIT
This is the JSON body content of the document with custom generated _id using crypto:
{
"_id": "1262d480eea83567181b3206",
"header": "hello",
"subheader": "hello"
}
Whereas, this is the body content of the upserted document.
{
"_id": {
"$oid": "1262d480eea83567181b3206"
},
"header": "helloasad",
"subheader": "helloasda"
}
As observed, after upserting, it takes the same _id value of the original document but on another document level.
A possible solution/explanation based on #Ashwanth Madhav information:
In your code 'id' was being sent to the update as a String type, but the id in MongoDB is an ObjectId type:
Code will be something like that:
var id;
if(req.params._id == 'undefined'){
// 'id' NEED TO BE AN ObjectId...
// 'id' WAS BEING SENT AS A 'String'
id = ObjectId(crypto.randomBytes(12).toString('hex'));
}
else {
id = ObjectId(req.params._id)
}

MongoDB $push operator not working as expected in Node

So I have a fairly simple piece of code as follows
db.get().collection('bars').insert({
barID: req.body.button,
}, {
$push: {
usersfbID: req.body.profileUser[0].facebookID
}
}, function(err, doc) {
if (err) {
throw err;
}
if (doc) {
console.log('Had to create a new document for this bar');
console.log(doc);
//callback(null, doc);
}
});
So, I'm just checking to see if a document for a bar exists, and if it doesn't then I create that document. And I want to insert an array for the usersfbID field so that I can store all the users going to the bar.
However, when I run the code, I don't get an error and it says the document has inserted but when the document logs, it doesn't have the userfbID field.
So what am I doing wrong? Does the $push operator only work with the update method of db? If so, how do I insert an array for that field?
Yes, it does work with the update methods
Reference > Operators > Update Operators > Array Update Operators > $push
Inserting a new entry means feeding the fields. In that case, there's no $push operation, since the array of the entry is freshly created and can be explicitly set (usersfbID:[req.body.profileUser[0].facebookID], meaning that you expect several fbId for that bar). Updating an array in an element of a collection isn't an insertion, it's an update.
So, just to provide an answer to the question I was facing..
Yes, you can only use $push or $addToSet with an update operation on a mongoDB document
Here is the way I implemented the code.
db.get().collection('bars').update({
barID: req.body.button,
}, {
$addToSet: {
usersfbID: req.body.profileUser[0].facebookID,
usersDocID: req.body.profileUser[0]._id
}
}, {
upsert: true
}, function(err, doc) {
if (err) {
console.log('There is an error here');
throw err;
}
if (doc) {
console.log('Had to create a new document for this bar');
callback(null, doc);
}
});
The upsert: true makes sure to insert a new document if the update method couldn't find the specified document.

Query mongo document based on its subdocument using $exists

I am trying to fetch records from mongo using mongoose.
I have document called projects that has sub-document work. work has user, to and from properties. I want to fetch the document that has specific user, and doesn't have to value set.
So far i tried:
models.project.findOne {'work.user': user,'work.to':{$exists: false}},
(err, data) -> # data is always null, no error.
It seems that if just one of objects in work contains property 'to' then $exists: equals to true. Is there a way to do this?
Also here is same code in JS if you prefer:
models.project.findOne({
'work.user': user,
'work.to': {
$exists: false
}
}, function(err, data) {});
Thanks for your time.
You can use $elemMatch in this case, which will return results only when specific user matched and doesn't have to value set in the same array element:
models.project.users.findOne({
"work" : { $elemMatch: {"user":user, "to": {$exists: false}}}
}, function(err, data) {});
you could use "null" for this case instead of $exists
models.project.findOne({
'work.user': user,
'work.to': null
}, function(err, data) {});

Is it possible to return only a certain fields when inserting a new document?

I nedd to get the new document back with _id field only. Something like this:
db.users.insert({name: 'Jonh', age: 27}, {_id: true} , function (err, user) {
if (err) {}
// need to be user with only _id field.
});
How can I do this?
UPDATED
The second parameter to the insert callback is always an array of the inserted objects, and you can't prevent the other fields from being included. However, you can use Array#map to create the result you're looking for:
db.users.insert({name: 'Jonh', age: 27}, {_id: true} , function (err, users) {
if (err) {}
users = users.map(function(user) {
return {_id: user._id};
});
// users now contains objects with just the _id field
});

Resources