How can I update all Mongo documents within an array of _ids? - javascript

I have an array of User _id's in foundStory.authors like so:
"authors": [
{
"$oid": "5814ef8cafc25327a572eee5"
},
{
"$oid": "5814ef80afc25327a572eee4"
}
],
I would like to run through that array of authors, and increment their scores by two. Currently, I am trying to do this with the following:
User.update({ _id: { $in: foundStory.authors } },{ $inc: { score : 2 } })
But, this is only incrementing the author at the last index of my array. From what I have read, I expected this to work. Any ideas?

Figured it out... adding {multi: true} seemed to solve the problem.
User.update({ _id: { $in: foundStory.authors } },{ $inc: { score : 2 } }, { multi: true })

Related

return what was not found in array in MongoDB

say my database collection has
* user collection*
[
{id:'1'}
{id:'2'}
]
I have an array of object
[
{id:'1'}
{id:'2'}
{id:'3'}
]
I want the object that was not found in the collection.
I want
[
{id:'3'}
]
I'm currently have this
const records = await dbo
.collection('user collection')
.find({
'id': { $in: newArr },
})
.toArray();
I'm a bit stumped on what to do! ... hope someone can help Thanks!
Option 1:
Looks like this is what you need via the not in operation ( $nin ) when you need to check the not exisitng id in collection documents from provided array:
db.collection.aggregate([
{
$match: {
id: {
"$nin": [
1,
2
]
}
}
},
{
$group: {
_id: null,
"idnotIntheArray": {
$push: "$id"
}
}
}
])
Explained:
$match for any documents with id not in provided array.
$group all id's in an array
plaground1
Option 2:
And this is the option where you output only the array elements not existing in the collection:
db.collection.aggregate([
{
$group: {
_id: null,
ids: {
$push: "$id"
}
}
},
{
$project: {
missingFromCollection: {
"$setDifference": [
[
1,
5,
4
],
"$ids"
]
}
}
}
])
Explained:
Push all id elements from collection to array ids ( note this solution will not allow more then 16MB total size of id's )
Use $setDifference to identify the difference between the two arrays.
playground2
You can use this aggregation:
db.entity.aggregate([
{
$match : {
"myObjList.id" : 1
}
},
{
$unwind : "$myObjList"
},
{
$match : {
"myObjList.id" : 1
}
}
])
and my aggregation result:
{
"_id" : ObjectId("6225a0f78d435fd2845f1dd1"),
"myObjList" : {
"id" : 1
}
}

Get some elements from an array mongoDB

In MongoDB shell version v4.4.6
the following code works perfectly.
db['pri-msgs'].findOne({tag:'aaa&%qqq'},{msgs:{$slice:-2}})
But in nodeJs mongoDB the following code doesn't work.
db.collection('pri-msgs').findOne({
tag: 'aaa&%qqq'
}, {
msgs: {
slice: -2
}
})
My document-->
{"_id":{"$oid":"60c4730fadf6891850db90f9"},"tag":"aaa&%qqq","msgs":[{"msg":"abc","sender":0,"mID":"ctYAR5FDa","time":1},{"msg":"bcd","sender":0,"mID":"gCjgPf85z","time":2},{"msg":"def","sender":0,"mID":"lAhc4yLr6","time":3},{"msg":"efg","sender":0,"mID":"XcBLC2rGf","time":4,"edited":true},{"msg":"fgh","sender":0,"mID":"9RWVcEOlD","time":5},{"msg":"hij","sender":0,"mID":"TJXVTuWrR","time":6},{"msg":"jkl","sender":0,"mID":"HxUuzwrYN","time":7},{"msg":"klm","sender":0,"mID":"jXEOhARC2","time":8},{"msg":"mno","sender":0,"mID":"B8sVt4kCy","time":9}]}
Actually what I'm trying to do is Get last 2 itmes from msgs Array where time is greater than 'n'. Here 'n' is a number.
You can use aggregation-pipeline to get the results you are looking for. The steps are the following.
Match the documents you want by tag.
Unwind the msgs array.
Sort descending by msgs.time.
Limit first 2 elements.
Match the time you are looking for using a range query.
Group the documents back by _id.
Your query should look something like this:
db['pri-msgs'].aggregate([
{ $match: { tag: 'aaa&%qqq' } },
{ $unwind: '$msgs' },
{
$sort: {
'msgs.time': -1 //DESC
}
},
{ $limit: 2 },
{
$match: {
'msgs.time': {
$gt: 2 //n
}
}
},
{
$group: {
_id: '$_id',
tag: { $first: '$tag' },
msgs: {
$push: { msg: '$msgs.msg', sender: '$msgs.sender', mID: '$msgs.mID', time: '$msgs.time' }
}
}
}
]);

How to remove duplicates in mongoDB with mongoose(NodeJS)

I have a collection in MongoDB where there are around (~200k records). My sample record would look like,
{
name:String,
slug:String,
metaDes:String,
content:String,
parentID:String,
thumbnail:String,
display:Boolean,
}
I am having a lot of duplicate records in the collection having same slug
I want to remove duplicate records based on slug
Is there any fast way to remove all duplicates with mongoose(Nodejs)?
Thanks!
Remove duplicate records in the collection having the same slug
db.table.aggregate([
{
"$group": {
_id: {slug: "$slug"},
slugs: { $addToSet: "$_id" } ,
count: { $sum : 1 }
}
},
{
"$match": {
count: { "$gt": 1 }
}
}
]).forEach(function(doc) {
doc.slugs.shift();
db.table.remove({
_id: {$in: doc.slugs}
});
})
Refarnce link

Mongoose - How to update object in array and then sort the updated array?

I have the following document...
{
"_id": {
"$oid": "5bca528b49079d64dd4cea5d"
},
"song_queue": [
{
"song_id": "best",
"vote": 1
},
{
"song_id": "west",
"vote": 1
}
],
"room_id": "FHWAN",
"__v": 0
My issue is that I need to first update a vote for one of the songs and then re-arrange the order of the array based on the vote count.
I have a way of doing it with two separate queries...
// Give the song an upvote.
await Room.findOneAndUpdate(
{ "song_queue.song_id": song.song_id },
{$set: { "song_queue.$.vote": song.votes }},
{ new: true }
);
// Sort the queue.
room = await Room.findOneAndUpdate(
{ room_id: data.room_id },
{ $push: { song_queue: { $each: [], $sort: { vote: -1 } } } },
{ new: true }
);
Is there a way to merge the two queries into one query, or perhaps make it more concise? I would like one query that updates the song in song_queue and then sorts the array after the change.
There is no way to merge this queries because the order of the operations inside update parameter is not guaranteed to be preserved so the code like this might fail to run correctly because you can't be sure that updating song_queue.$.vote with occur before song_queue is sorted:
await Room.findOneAndUpdate(
{ "song_queue.song_id": song.song_id },
{
$set: { "song_queue.$.vote": song.votes },
$push: { song_queue: { $each: [], $sort: { vote: -1 } } }
},
{ new: true }
);
What can be done to optimize db querying is using Ordered Bulk Write operation which will make only one round trip to MongoDB instead of two.

How to query documents by a condition on the subdocument with the latest date [duplicate]

This question already has an answer here:
Query on Last Array Value
(1 answer)
Closed 4 years ago.
I'm trying to figure out the best way to query documents based on a criteria on the latest subdocument.
So my data might look like this:
[{
_id: '59bb31efae69726bd5fc9391',
name: 'Something',
terms: [
{
_id: '58e54f5aad59a6000cdcd590',
begDate: '2017-06-13T07:00:00.000Z',
endDate: '2018-01-01T07:59:59.999Z'
},
{
_id: '59bb32765e651d28909ed706',
begDate: '2018-01-01T08:00:00.000Z',
endDate: '2019-01-01T07:59:59.999Z'
}
]
}, {
_id: '59f20ddeef426f6bca3abbf1',
name: 'Something',
terms: [
{
_id: '59f20e35c8257b5b0f22d2a6',
begDate: '2018-06-13T07:00:00.000Z',
endDate: '2019-01-01T07:59:59.999Z'
},
{
_id: '59f20e9394c8108d9db33bf9',
begDate: '2019-01-01T08:00:00.000Z',
endDate: '2020-01-01T07:59:59.999Z'
}
]
}]
What I want is to get all documents whose last term's endDate is 2019-01-01T07:59:59.999Z This could be done by either getting the last term in an array, or more reliably sorting terms, and then grabbing the last one.
I can see how I could do this with $where but I know if I can find another way it would be more performant.
I also want to add, whatever I do here would accompany other query parameters. For example:
{
_id: {
'$in': [
ObjectId("591e5e37abddad14afe1b272"),
ObjectId("591e5e37abddad14afe1b123")
]
}
}
UPDATE:
As noted, this question has a duplicate (which was hard for me to find as the question referenced is difficult to understand). That being said, I'm not only looking for the last in an array but also the most recent (I agree that's not clear in the body of the question). I'm not arguing against the duplicate question reference, but for the sake of making this easier for future readers, you'll find in the accepted answer a clean solution for mongo 3.6+ as well as a reference to another question in the comments which should help if you want to query by date in subdocuments.
Using $expr to perform a 'complex' match and $let to have an intermediate variable storing the last element of arrays found with "$arrayElemAt": [ "$terms", -1 ] in order to compare it to the date in question:
db.collection.find({
$expr: {
$let: {
vars: { "last": { $arrayElemAt: [ "$terms", -1 ] } },
in: { $eq: [ "$$last.endDate", "2019-01-01T07:59:59.999Z" ] }
}
}
})
which returns with the input you provided the first record.
And, as per your requirements, in order not to exclude the possibility to add additional filters, you can add them using $and:
db.collection.find({
$and: [
{ $expr: { $let: {
vars: { "last": { $arrayElemAt: [ "$terms", -1 ] } },
in: { $eq: [ "$$last.endDate", "2019-01-01T07:59:59.999Z" ] }
}}},
{ "_id": { $ne: "sss" } } // actually whatever additional filter
]
})
Exact same thing can be achieved with an aggregate pipeline, if you wish to perform additional stages with your matching documents:
db.collection.aggregate([
{ $match: {
$and: [
{ $expr: { $let: {
vars: { "last": { $arrayElemAt: [ "$terms", -1 ] } },
in: { $eq: [ "$$last.endDate", "2019-01-01T07:59:59.999Z" ] }
}}},
{ "_id": { $ne: "sss" } }
]
}},
{ ... }
])

Categories

Resources