如果您需要计算出在运行时是这样的,从阵列确定排序顺序“过滤”的内容,那么你最好做.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”,并且可能包含多个文档和预期结果,可能会有更清晰的问题。但我想我明白你的意思了。 –