我有一個 mongodb 集合,我按每個專案的點數排序,并根據它在集合中的位置顯示排名:
db.collection('websites').find({}).sort({ "points": -1 }).forEach(doc => {
rank ;
doc.rank = rank;
delete doc._id;
console.log(doc)

Si 我心想:好的,我要更新收藏中的排名,所以我添加了這個:
db.collection('websites').updateMany({},
{ $set: { rank: doc.rank } }
)
但我好得令人難以置信,它會更新每個具有相同等級的專案,每次重繪 都會改變,這里到底發生了什么?

編輯:我設法通過這樣做來做到這一點:
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 }
)
})
uj5u.com熱心網友回復:
嘗試這個:
db.collection('websites')
.updateOne( //update only one
{rank: doc.rank}, //update the one where rank is the sent in parameter doc.rank
{ $set: { rank: doc.rank } } // if multiple docs have the same rank you should send in more parameters
)
uj5u.com熱心網友回復:
db.collection('websites').updateMany({/*All docs match*/},
{ $set: { rank: doc.rank } }
)
它更新相同排名的原因是因為您沒有過濾器,這意味著它匹配集合中的所有檔案并且您有updateMany
您需要設定一個過濾器來限制要更新的檔案。
db.collection('websites').updateMany({id: "someID"},
{ $set: { rank: doc.rank } }
)
uj5u.com熱心網友回復:
OP 宣告我們要按點對所有檔案進行排序,然后按該順序從 1 到 n 將它們“重新排列”并更新資料庫。這是一個“聚合是新更新”的示例,這要歸功于與輸入相同$merge的集合的強大功能:
db.foo.aggregate([
// Get everything in descending order...
{$sort: {'points':-1}}
// ... and turn it into a big array:
,{$group: {_id:null, X:{$push: '$$ROOT'}}}
// Walk the array and incrementally set rank. The input arg
// is $X and we set $X so we are overwriting the old X:
,{$addFields: {X: {$function: {
body: function(items) {
for(var i = 0; i < items.length; i ) {
items[i]['rank'] = (i 1);
}
return items;
},
args: [ '$X' ],
lang: "js"
}}
}}
// Get us back to regular docs, not an array:
,{$unwind: '$X'}
,{$replaceRoot: {newRoot: '$X'}}
// ... and update everything:
,{$merge: {
into: "foo",
on: [ "_id" ],
whenMatched: "merge",
whenNotMatched: "fail"
}}
]);
如果使用$function嚇到你,你可以使用更鈍的方法$reduce作為有狀態的 for 回圈替代。為了更好地了解正在發生的事情,請使用/* */以下階段阻止注釋,$group并逐個取消注釋每個連續階段,以查看該操作員如何影響管道。
db.foo.aggregate([
// Get everything in descending order...
{$sort: {'points':-1}}
// ... and turn it into a big array:
,{$group: {_id:null, X:{$push: '$$ROOT'}}}
// Use $reduce as a for loop with state.
,{$addFields: {X: {$reduce: {
input: '$X',
// The value (stateful) part of the loop will contain a
// counter n and the array newX which we will rebuild with
// the incremental rank:
initialValue: {
n:0,
newX:[]
},
in: {$let: {
vars: {qq:{$add:['$$value.n',1]}}, // n = n 1
in: {
n: '$$qq',
newX: {$concatArrays: [
'$$value.newX',
// A little weird but this means "take the
// current item in the array ($$this) and
// set $$this.rank = $qq by merging it into the
// item. This results in a new object but
// $concatArrays needs an array so wrap it
// with [ ]":
[ {$mergeObjects: ['$$this',{rank:'$$qq'}]} ]
]}
}
}}
}}
}}
,{$unwind: '$X.newX'}
,{$replaceRoot: {newRoot: '$X.newX'}}
,{$merge: {
into: "foo",
on: [ "_id" ],
whenMatched: "merge",
whenNotMatched: "fail"
}}
]);
uj5u.com熱心網友回復:
這里的問題是 mongo 使用相同的doc.rank值來更新與過濾條件匹配的所有記錄(在您的情況下為所有記錄)。現在您有兩種選擇來解決問題 -
- 有效,但效率較低) - 這里的想法是您需要計算要更新的每個網站的排名。回圈遍歷所有檔案并在查詢下方運行,該查詢將使用計算出的排名更新每個檔案。你可能會認為這是低效的,你是對的。我們正在進行大量網路呼叫以更新記錄。更糟糕的是,速度是無限的,并且會隨著記錄數量的增加而變慢。
db.collection('websites')
.updateOne(
{ id: 'docIdThatNeedsToBeUpdated'},
{ $set: { rank: 'calculatedRankOfTheWebsite' } }
)
- 高效選項- 使用相同的技術來計算每個網站的排名并回圈通過它來生成上述更新陳述句。但是這次您不會為所有網站單獨進行更新呼叫。相反,您將使用批量更新技術。您將所有更新陳述句添加到一個批處理中并一次性執行它們。
//loop and use below two line to add the statements to a batch.
var bulk = db.websites.initializeUnorderedBulkOp();
bulk.find({ id: 'docIdThatNeedsToBeUpdated' })
.updateOne({
$set: {
rank: 'calculatedRankOfTheWebsite'
}
});
//execute all of the statement at one go outside of the loop
bulk.execute();
uj5u.com熱心網友回復:
我設法做到了:
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 }
)
})
謝謝大家 !
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/482023.html
