SubDocument updates in mongodb using node - node.js

I have collection structure like this.
application_data: {
name : A,
site : B,
location : [
{ key1 : value1, key2 : value2},
{ key3 : value3, key4 : value4}
]
}
Now I want to add a another array to "location" as a sub document so that my location becomes
location : [
{ key1 : value1, key2 : value2},
{ key3 : value3, key4 : value4, key5 :[{subkey1:subvalue1, subkey2:subvalue2}]}
]
I tried $push & $addToSet which did not help me. Can somebody help me?
Helpful if you explain an example using nodejs.

What you'r actually trying to do is to add new field to an existing subdocument. You can do it using $set and positional operator $:
db.applications.update({
name: 'A', // querying for parent document
'location.key3': 'value3' // querying for an exact subdocument to add new field to
}, {
$set: {
'location.$.key5': [{
subkey1: 'subvalue1',
subkey2: 'subvalue2'
}]
}
})
You can achieve the same result using $push or $addToSet, which is better if you want to add more than one subsubdocument to 'location.key5':
db.applications.update({
name: 'A',
'location.key3': 'value3'
}, {
$push: {
'location.$.key5': {
subkey1: 'subvalue1',
subkey2: 'subvalue2'
}
}
})
or
db.applications.update({
name: 'A',
'location.key3': 'value3'
}, {
$addToSet: {
'location.$.key5': {
subkey1: 'subvalue1',
subkey2: 'subvalue2'
}
}
})
See Update Documents in an Array for more info.

Related

How to update specific object inside the array?

I have a data that looks like below in MongoDB
{
_id: aasdfeasfeasdf,
todo: [
{_todoIde: 333, _with: []},
{_todoIde: 111, _with: []},
]
}
I want to $addToSet value to _todoIde: 333's _with like {_todoIde: 333, _with: [aaaa]},. How can I do it?
.updateOne(
{_id},
{ $addToSet: {}}
)
I got to the document but I can't specify that _todoIde: 333 to update just that one.
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array,
.updateOne(
{ _id: "aasdfeasfeasdf", "todo._todoIde": 333 },
{
$addToSet: {
"todo.$._with": "aaaa"
}
}
)
Playground
You have to add an extra condition to specify the todoIde
Try this:
db.collection.update(
{$and:[{_id: typeId},{'todo._todoIde': 333}]},
{$set: { "todo._todoIde.$._with":[a,b,c]}},
);

MongoDB merge two collections with unmatched documents

I am trying to compare and find different documents from two collections
below are the samples, Mongodb version:4.0, ORM:mongoose
**col1: Has one new document**
{ "id" : 200001, "mobileNo" : #######001 }
{ "id" : 200002, "mobileNo" : #######002 } //mobileNo may not be unique.
{ "id" : 200003, "mobileNo" : #######002 }
{ "id" : 200004, "mobileNo" : #######004 }
**col2:**
{ "id" : 200001, "mobileNo" : #######001 }
{ "id" : 200002, "mobileNo" : #######002 }
{ "id" : 200003, "mobileNo" : #######003 }
Now I want to insert the document { "id" : 200004, "mobileNo" : #######004 } from col1 to col2
i.e; the documents which doesn't match.
This is what I've tried so far :
const col1= await Col1.find({}, { mobileNo: 1,id: 1, _id: 0 })
col1.forEach(async function (col1docs) {
let col2doc = await Col2.find({ mobileNo: { $ne: col1docs.mobileNo},
id:{$ne:col1docs.id} }, { mobileNo: 1, _id: 0, id: 1 })
if (!(col2doc)) {
Col2.insertMany(col1docs);
}
});
I have also tried with $eq instead of $ne but neither i am getting the unmatched documents nor they are getting inserted. Any suggestions??? Combination of id+phoneNo is unique
I would say instead of doing two .find() calls plus iteration & then third call to write data, try this query :
db.col1.aggregate([
{
$lookup: {
from: "col2",
let: { id: "$id", mobileNo: "$mobileNo" },
pipeline: [
{
$match: { $expr: { $and: [ { $eq: [ "$id", "$$id" ] }, { $gte: [ "$mobileNo", "$$mobileNo" ] } ] } }
},
{ $project: { _id: 1 } } // limiting to `_id` as we don't need entire doc of `col2` - just need to see whether a ref exists or not
],
as: "data"
}
},
{ $match: { data: [] } // Filtering leaves docs in `col1` which has no match in `col2`
},
{ $project: { data: 0, _id: 0 } }
])
Test : mongoplayground
Details : From the above query you're taking advantage of specifying conditions in $lookup to get docs from col1 which have reference in col2. Let's say $lookup will run on each document of col1 - So with the unique combination of id & mobileNo from current document in col1 has a matching in col2 then col2 doc's _id will be pushed in data array, at the end what we get out of col1 is data: [] to say no matching docs were found for these col1 doc's. Now you can just write all the returned docs to col2 using .insertMany(). Actually you can do this entire thing using $merge on MongoDB version > 4.2 without any need of 2nd write call(.insertMany()).
For your scenario on MongoDB version > 4.2 something like this will merge docs to second collection :
{ $merge: 'col2' } // Has to be final stage in aggregation
Note : If this has to be done periodically - no matter how you do this, try to minimize data that you're operating on, maybe maintain a time field & you can use that field to filter docs first & do this job, or you can also take advantage of _id to say we've done for all these docs in last run & we need to start from this docs - which helps you a lot to reduce data to be worked on. Additionally don't forget to maintain indexes.

How to find and update particular object from an array (mongoose) in node js

I want to update the 'Name' value from array of object (myArr[ ]) using Arr_id.
mydb:{
"_id" : ObjectId("5eb2b06d626fc539172013f8"),
"is_deleted" : false,
"email" : "rajnish#dresma.com",
"myArr": [
{"Arr_id":"5eb2b06d626fc539172013f7",
"Name":"Aman"
},
{"Arr_id":"5eb2b06d626fc539172001k9",
"Name":"Ram"
},
{"Arr_id":"5eb2b06d626fc539172013k4",
"Name":"Piyush"
}
]
}
You can update in nested objects using $ positional operator.
here , Id is the id you want to update the value of name.
db.collection.update(
{ "myArr.Arr_id": Id },
{ "$set": { "myArr.$.Name": "SHYam" } }
)

How do I query for the value of a key in an object? [duplicate]

So I'm attempting to find all records who have a field set and isn't null.
I try using $exists, however according to the MongoDB documentation, this query will return fields who equal null.
$exists does match documents that contain the field that stores the null value.
So I'm now assuming I'll have to do something like this:
db.collection.find({ "fieldToCheck" : { $exists : true, $not : null } })
Whenever I try this however, I get the error [invalid use of $not] Anyone have an idea of how to query for this?
Use $ne (for "not equal")
db.collection.find({ "fieldToCheck": { $ne: null } })
Suppose we have a collection like below:
{
"_id":"1234"
"open":"Yes"
"things":{
"paper":1234
"bottle":"Available"
"bottle_count":40
}
}
We want to know if the bottle field is present or not?
Ans:
db.products.find({"things.bottle":{"$exists":true}})
i find that this works for me
db.getCollection('collectionName').findOne({"fieldName" : {$ne: null}})
This comment is written in 2021 and applies for MongoDB 5.X and earlier versions.
If you value query performance never use $exists (or use it only when you have a sparse index over the field that is queried. the sparse index should match the criteria of the query, meaning, if searching for $exists:true, the sparse index should be over field:{$exist:true} , if you are querying where $exists:true the sparse index should be over field:{$exist:false}
Instead use :
db.collection.find({ "fieldToCheck": { $ne: null } })
or
db.collection.find({ "fieldToCheck": { $eq: null } })
this will require that you include the fieldToCheck in every document of the collection, however - the performance will be vastly improved.
db.<COLLECTION NAME>.find({ "<FIELD NAME>": { $exists: true, $ne: null } })
In my case, i added new field isDeleted : true to only fields that are deleted.
So for all other records there was no isDeleted field, so i wanted to get all the fields that isDeleted either does not exist or false. So query is
.find({ isDeleted: { $ne: true } });
I Tried to convert it into boolean condition , where if document with
table name already exist , then it will append in the same document ,
otherwise it will create one .
table_name is the variable using which i am trying to find the document
query = { table_name : {"$exists": "True"}}
result = collection.find(query)
flag = 0
for doc in result:
collection.update_one({}, { "$push" : { table_name : {'name':'hello'} } } )
flag = 1
if (flag == 0):
collection.insert_one({ table_name : {'roll no' : '20'}})
aggregate example
https://mongoplayground.net/p/edbKil4Zvwc
db.collection.aggregate([
{
"$match": {
"finishedAt": {
"$exists": true
}
}
},
{
"$unwind": "$tags"
},
{
"$match": {
"$or": [
{
"tags.name": "Singapore"
},
{
"tags.name": "ABC"
}
]
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": 1
}
}
}
])

Complex sort with MongoDB + Sails.js

I am developing a web application using Sails.js and MongoDB. I have to query the database and sort the results by a complex function. Actually is not complex, but is more difficult than sort by a key. In this case, I have this collection
Users
{ user : "Luis", key1 : 23, key2 : 29 };
{ user : "Juan", key1 : 53, key2 : 22 };
{ user : "Paco", key1 : 26, key2 : 42 };
And I'm trying get the results order by key1 - key2
I have tried different queries having Mongo complex sorting? in account, but it didn't help me.
User.find({}).sort(function(doc1,doc2){return doc1.a . doc2.b })
.exec(function(err,users){console.log(users)});
I have checked that this way runs ok in the Robomongo console (MongoDB GUI client).
db.eval(function() {
return db.scratch.find().toArray().sort(function(doc1, doc2) {
return doc1.a - doc2.a
})
});
But I am not able to implement this using Sails and waterline orm. I would use native connection if necessary but I don't know how to do it.
Please help me, thanks you a lot!
You need to use the .aggregate() method which provides access to the aggregation pipeline. The first stage in the pipeline is the $project stage where you use the $subtract operator to return the difference of "key1" and "key2".
The last step in the pipeline is the $sort stage where you sort your documents in ascending order using { 'diffkey': 1 } or descending order { 'diffkey': -1 }.
db.user.aggregate([
{ '$project': {
'user': 1,
'key1': 1,
'key2': 1,
'diffkeys': { '$subtract': [ '$key1', '$key2' ] }
}},
{ '$sort': { 'diffkeys': 1 } }
])
Which yields:
{
"_id" : ObjectId("567d112eaac525feef33a5b7"),
"user" : "Juan",
"key1" : 53,
"key2" : 22,
"diffkeys" : -31
}
{
"_id" : ObjectId("567d112eaac525feef33a5b6"),
"user" : "Luis",
"key1" : 23,
"key2" : 29,
"diffkeys" : 6
}
{
"_id" : ObjectId("567d112eaac525feef33a5b8"),
"user" : "Paco",
"key1" : 26,
"key2" : 42,
"diffkeys" : 16
}
You may be tempted to add another extra $project to the pipeline to filter out the diffkey field from your query result but doing so will cause a drop of performance. And generally speaking you shouldn't worry about one additional field in your query result.
If you mean sort by the difference between key1 and key2 I don't think it can be done with a simple find, I think you'll need to use aggregation.
Note: I'm not familiar with Sail.js, so I'll write the query in mongoshell and let you translate to sail
db.user.aggregate([
{
$project:{
doc: "$$ROOT",
sortOrder: { $subtract: ["$key1", "$key2"] }
}
},
{
$sort:{ sortOrder: 1}
}
]);
Not sure how exactly you want your final output to look, so you may want to add some another project at the end.

Resources