在 MongoDB 的聚合管道中获取 $group 之后的输入文档中的字段

Posted

技术标签:

【中文标题】在 MongoDB 的聚合管道中获取 $group 之后的输入文档中的字段【英文标题】:get the fields in the input document after $group in aggregation pipeline in MongoDB 【发布时间】:2015-05-12 19:24:55 【问题描述】:

我面临的问题是如何在一次分组操作后访问原始文档,以及如何在MongoDB的聚合管道中携带$group之后的字段。

例如:[group, unwind, group]

原始文件是:


"_id" : ObjectId("361de42f1938e89b179dda42"),
"user_id" : ObjectId("9424021bafbde55512e39b83"),
"candidate_id" : ObjectId("54f65356294160421ead3ca1")
"OVERALL_SCORE" : 150,
"SCORES" : [ 
     "NAME" : "asd", "OBTAINED_SCORE" : 28,  "NAME" : "acd", "OBTAINED_SCORE" : 36 ,  "NAME" : "abc", "OBTAINED_SCORE" : 40
 ]

聚合函数:

 db.coll.aggregate([ $group :  _id :  user_id : "$user_id", BEST_SCORE :  $max : "$OVERALL_SCORE", AVG_SCORE :  $avg : "$OVERALL_SCORE"  ])

以下是示例输出(第一组之后):


"result" : [ 
    
        "_id" : 
            "user_id" : ObjectId("9424021bafbde55512e39b83")
        ,
        "BEST_SCORE" : 150,
        "AVG_SCORE" : 132
    
],
"ok" : 1
 

问题是:(不知道能不能实现) 我想要原始文档中的字段(聚合的输入)。

例如: 1) 通过“candidate_id”、“user_id”展开原始文档和下一组中的“SCORES”。

2) 我希望“BEST_SCORE”、“AVG_SCORE”(在第一组之后)字段也可以在第二组中访问。

聚合函数应该是这样的:

   db.coll.aggregate([ $group :  _id :  user_id : "$user_id", BEST_SCORE :  $max : "$OVERALL_SCORE", AVG_SCORE :  $avg : "$OVERALL_SCORE" ,  $unwind : "$SCORES", /*problem is--after group operation "SCORES" field which is in original document not available */  $group : _id :  NAME: "$SCORES.NAME", AVG_OBTAINED_SCORE:  $avg : "$SCORES.OBTAINED_SCORE" **/*problem is--this is also in the original document*/** ])

输出应如下所示:

   "BEST_SCORE": 150,                     //after 1st group
  "AVG_SCORE": 132,                       //after 1st group
  "SCORES": [                             //problem --- unwind "SCORES" and then group which is actually will not be available after 1st group (get this from original document)
    
      "NAME": "abc",
      "AVG_OBTAINED_SCORE": 25.5
    ,
    
      "NAME": "asd",
      "AVG_OBTAINED_SCORE": 24
    ,
    
      "NAME": "acd",
      "AVG_OBTAINED_SCORE": 32
    
  ]

谁能帮帮我。

谢谢

【问题讨论】:

这并没有多大意义。您只能真正“保留”“分组键”中的内容,否则可以由分组运算符之一保留,就像您使用其他值或可选的 $last 或 $push 之类的东西一样。保留其他字段的意义何在?您对最终结果的期望是什么。 $$ROOT 仅指调用它的聚合阶段的“整个文档”。因此,一旦您在一个阶段更改了文档,那么$$ROOT 的值就是在另一个阶段访问时更改的形式。您无法看到文档在进行其他更改之前的样子。正如我之前建议的那样,您的预期结果并不是很清楚。如果您编辑您的问题以显示您的期望,那么人们可能会就实现目标的方法提出建议。 我已经编辑了..希望现在更清楚.. 你在 "name": "asd" 应该来自的地方失去了我们。除非它是“SCORES”数组文档的一部分,否则你很可能在这里不走运。它真的是那些数组文档中的一个元素吗? 我们不要聊天。这是一个是或不是的答案。否则,请更改您问题的详细信息以解释数据应该来自哪里 【参考方案1】:

当与您希望保留组中所有考虑文档的值的内容进行分组时,您需要使用$push。抓住的是,这是一个数组。所以你处理$unwind 两次,并且还有两个$group 阶段:

db.coll.aggregate([
      "$group" :  
        "_id": "$user_id", 
        "BEST_SCORE":  "$max": "$OVERALL_SCORE" ,
        "AVG_SCORE":  "$avg": "$OVERALL_SCORE" ,
        "SCORES":  "$push": "SCORES" 
    , 

    // SCORES in an array of arrays. Unwind twice
     "$unwind": "$SCORES" ,
     "$unwind": "$SCORES" ,

    // Group for averages on elements
     "$group": 
        "_id": 
            "user_id": "$_id",
            "NAME": "$SCORES.name"
        ,
        "BEST_SCORE":  "$first": "$BEST_SCORE" ,
        "AVG_SCORE":  "$first": "$AVG_SCORE" 
        "AVG_OBTAINED_SCORE":  "$avg": "$SCORES.OBTAINED_SCORE"  
    ,

    // Group to user_id
     "$group": 
        "user_id": "$_id.user_id",
        "BEST_SCORE":  "$first": "$BEST_SCORE" ,
        "AVG_SCORE":  "$first": "$AVG_SCORE" 
        "SCORES":  "$push": 
            "NAME": "$_id.NAME",
            "AVG_OBTAINED_SCORE": "$AVG_OBTAINED_SCORE"
             
    
])

您可能想在第一个 $group 之前考虑使用 $unwind,但如果您这样做了,那么计算的平均值将受到正在“解绕”的数组中存在的元素数量的影响。所以这里的“双重$unwind”是一个必要的过程。

【讨论】:

如果有任何字段要结转到下一个管道操作员,我们需要使用$push或$addToSet。 @user3805045 你需要做一些事情,但这些都适合这个用例。 $push 和 $addToSet 之间的主要区别在于 push 添加的元素保持其顺序(无论在前面的阶段是什么),而 $addToSet 则以无特定顺序添加元素:docs.mongodb.com/manual/reference/operator/aggregation/addToSet

以上是关于在 MongoDB 的聚合管道中获取 $group 之后的输入文档中的字段的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB——聚合管道之$group操作

MongoDB——聚合管道之$group操作

MongoDb 聚合 $match 错误:“参数必须是聚合管道运算符”

mongodb聚合命令

MongoDB的聚合操作

Mongodb中数据聚合之聚合管道aggregate