2015-11-08 118 views
0

嵌入对象值我有这样一个模式:排序MongoDB中

{ 
    tags:[ 
     { 
     id:"t1", 
     score:70 
     }, 
     { 
     id:"t1", 
     score:60 
     } 
    ] 
} 

我想在tag.id搜索查询排序得到由相应的分数排序。因此,如果我搜索db.collection.find({tags.id:“t1”})。sort({tags.score:-1}),它会根据“t1”对象的评分而不是其他标签进行排序。

什么建议吗?

+0

如果您的数组样本包含的其他值不只是“t1”,并且可能包含多个文档和预期结果,可能会有更清晰的问题。但我想我明白你的意思了。 –

回答

1

如果您需要计算出在运行时是这样的,从阵列确定排序顺序“过滤”的内容,那么你最好做.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()上向文档添加附加属性意味着您只需引用这些属性即可直接对结果进行排序。

+0

感谢您的解释。第二种方法不适合我的情况,因为我可以有50-100个变量的标签。虽然它非常有效。聚合函数给了我一个错误,没有定义el,当它变成$ el时,它没有显示任何结果,我在这里丢失了什么? – MKoosej

+0

按照预期在您的答案中使用了小编辑。 – MKoosej

+0

如何将整个文档恢复到组中?我应该在投影中逐一添加它们吗? – MKoosej

0

根据上述描述的查询

db.collection.find({tags.id:"t1"}).sort({tags.score:-1}) 

将过滤由标签id 1的文件和将按降序返回由得分排序的结果。

也请提供例如文档一起详细描述和预期的输出结果

+0

公平地确定OP的问题清楚地表明,“排序”仅适用于匹配“t1”的数组中的值。这不会那样做,只匹配包含“t1”的文档,并且不会像这样限制排序。至少如果“点符号”语法在这里是正确的而不是非法的。但它仍然不是被问到的问题的答案。 –