如果您需要計算出在運行時是這樣的,從陣列確定排序順序「過濾」的內容,那麼你最好做.aggregate()
東西重塑和確定排序值是這樣的:
db.collection.aggregate([
// Pre-filter the array elements
{ "$project": {
"tags": 1,
"score": {
"$setDifference": [
{ "$map": {
"input": "$tags",
"as": "tag",
"in": {
"$cond": [
{ "$eq": [ "$$el.id", "t1" ] },
"$$el.score",
false
]
}
}},
[false]
]
}
}},
// Unwind to denormalize
{ "$unwind": "$score" },
// Group back the "max" score
{ "$group": {
"_id": "$_id",
"tags": { "$first": "$tags" },
"score": { "$max": "$score" }
}},
// Sort descending by score
{ "$sort": { "score": -1 } }
])
凡管道的第一部分是用來「預過濾器」數組內容(以及保持原始字段)到只是這些價值其中id等於「t1」的「分數」。這通過處理$map
來完成,其通過$cond
向每個元素應用條件以確定是否返回該元素的「分數」或false
。
$setDifference
操作與單個元素數組[false]
進行比較,該數組有效地移除從$map
返回的任何false
值。作爲一個「集合」,這也會刪除重複的條目,但出於排序的目的,這是一件好事。
將陣列縮小並重新設置爲值後,您將處理$unwind
準備進入下一個階段,將這些值作爲單個元素處理。 $group
階段實質上在「分數」上應用$max
以返回過濾結果中包含的最高值。
然後,它只是在確定的值上應用$sort
來訂購文檔。自然如果你想這個反過來,然後使用$min
,而不是按升序排序。
當然,如果您真正想要的是在標籤內實際包含id
的「t1」值的文檔,那麼當然要在起始處添加一個$match
階段。但該部分與您想要實現的篩選結果的排序關係最小。
計算的替代方法是在向文檔中的數組寫入條目時執行此操作。種類雜亂,但它是這樣的:
db.collection.update(
{ "_id": docId },
{
"$push": { "tags": { "id": "t1", "score": 60 } },
"$max": { "maxt1score": 60 },
"$min": { "mint1score": 60 }
}
)
這裏如果新值比現有的值或其它無屬性尚不存在較大的$max
更新操作只設置指定字段的值。相反的情況是$min
,如果小於它將被替換爲新的值。
這當然有增加各種附加屬性,以文件的效果,但最終的結果排序大爲簡化:
db.collection.find().sort({ "maxt1score": -1 })
而且它會比使用聚合計算速度跑了很多管道。
所以考慮設計原則。數組中需要篩選和配對結果的結構化數據意味着在運行時計算排序的值。在.update()
上向文檔添加附加屬性意味着您只需引用這些屬性即可直接對結果進行排序。
如果您的數組樣本包含的其他值不只是「t1」,並且可能包含多個文檔和預期結果,可能會有更清晰的問題。但我想我明白你的意思了。 –