i have a mongodb collection that I sort by the amount of points each item has, and it shows a rank according to it’s place in the collection :
db.collection('websites').find({}).sort({ "points": -1 }).forEach(doc => {
rank++;
doc.rank = rank;
delete doc._id;
console.log(doc)
Si I thought to myself : Ok, I’m gonna update the rank in the collection, so I added this :
db.collection('websites').updateMany({},
{ $set: { rank: doc.rank } }
)
But I was too good to be true, and it updates every single item with the same rank, which changes at each refresh, what exactly is going on, here ?
EDIT : I managed to do it by doing this :
rank = 0;
db.collection('websites').find({}).sort({ "points": -1 }).forEach(doc => {
rank++;
doc.rank = rank;
//delete doc._id;
console.log(doc._id);
db.collection('websites').updateMany({_id : doc._id},
{ $set: { rank: doc.rank } },
{ upsert: true }
)
})
5
Answers
I managed to do it by doing:
Thank you everyone !
Try this:
Reason it updates same rank because you have no filter which means it matches all docs in the collection and you have
updateMany
You need to set a filter to restrict docs to be updated.
The problem here is that mongo is using the same doc.rank value to update all the records that match the filter criteria (all records in your case). Now you have two options to resolve the issue –
The OP states we want to sort all the docs by points, then "rerank" them from 1 to n in that order and update the DB. Here is an example of where "aggregate is the new update" thanks to the power of
$merge
onto the same collection as the input:If using
$function
spooks you, you can use a somewhat more obtuse approach with$reduce
as a stateful for loop substitute. To better understand what is happening, block comment with/* */
the stages below$group
and one by one uncomment each successive stage to see how that operator is affecting the pipeline.