MongoDB组聚合的结果很奇怪

时间:2017-01-05 11:30:29

标签: mongodb mongodb-query aggregation-framework

我有两个不同的文档,我想用匹配的字段进行分组(在这种情况下为idOrdem)。两者都有一个timestamp字段,只有一个字段有timeElapsed字段。我需要在聚合中返回所有这些信息,但我得到的结果似乎并不正确。我得到null字段的timeElapsed结果,其中有一个带有此字段的文档。

我的陈述有什么问题?

db.Logging.aggregate( [
 { $match : { $or : [ { "action":"resetDslVerify"}, { "action":"assia/reset/RequestQueryOs" } ] } },
 { $group : {
    _id : "$idOrdem",
    timestamp1: { $first: '$timestamp' },
    timestamp2: { $last: '$timestamp' },
    timeElapsed: { $first: '$timeElapsed' }
    }
 },
 { $sort: { timestamp: -1}  } ]
);

出乎意料的结果:

{ "_id" : "159251", "timestamp1" : 1483456382058, "timestamp2" : 1483456382058, "timeElapsed" : 1091 }
{ "_id" : "134601", "timestamp1" : 1482949316671, "timestamp2" : 1482949349410, "timeElapsed" : 821 }
{ "_id" : "168801", "timestamp1" : 1483560599899, "timestamp2" : 1483560564505, "timeElapsed" : null }
{ "_id" : "158901", "timestamp1" : 1483452698756, "timestamp2" : 1483452673424, "timeElapsed" : null }
{ "_id" : "135001", "timestamp1" : 1482949653229, "timestamp2" : 1482949711541, "timeElapsed" : 838 }

匹配idOrdem的文档示例,其中包含我需要的所有信息:

s-1:PRIMARY> db.Logging.find( { $or : [ { "action":"resetDslVerify"}, { "action":"assia/reset/RequestQueryOs" } ], "idOrdem":"135001" } );
{ "_id" : ObjectId("586404155b88db1209c3f998"), "success" : true, "action" : "assia/reset/RequestQueryOs", "timestamp" : 1482949653229, "httpCode" : 200, "timeElapsed" : 838, "idOrdem" : "135001", "creator" : "TecnicoVirtual" }
{ "_id" : ObjectId("5864044f5b88db1209c3f99b"), "success" : true, "action" : "resetDslVerify", "timestamp" : 1482949711541, "terminal" : "2134599099", "httpCode" : 200, "idOrdem" : "135001", "idOrdem" : "135001", "result" : "OK", "timestamp" : 1482949711541, "isResetDslSuccess" : true, "creator" : "TecnicoVirtual" }

2 个答案:

答案 0 :(得分:1)

$ first将获取第一个文档的值,即使该文档中不存在该字段。您可以像这样解决此问题:

db.Logging.aggregate([
   {
      $match:{
         $or:[
            {
               "action":"resetDslVerify"
            },
            {
               "action":"assia/reset/RequestQueryOs"
            }
         ]
      }
   },
   {
      $group:{
         _id:"$idOrdem",
         timestamp1:{
            $first:'$timestamp'
         },
         timestamp2:{
            $last:'$timestamp'
         },
         timeElapsed:{
            $push:"$timeElapsed"
         }
      }
   },
   {
      $project:{
         _id:1,
         timestamp1:1,
         timestamp2:1,
         timeElapsed:{
            $arrayElemAt:[
               "$timeElapsed",
               0
            ]
         }
      }
   },
   {
      $sort:{
         timestamp:-1
      }
   }
]);

输出:(对于您提供的数据)

{ "_id" : "135901", "timestamp1" : 1482950884849, "timestamp2" : 1482950907877, "timeElapsed" : 801 }

答案 1 :(得分:1)

$first $last 累加器分别从每个组的第一个/最后一个文档返回一个值。订单仅在文档按照定义的顺序时定义,但在您订购的情况下 他们AFTER按任意顺序对它们进行分组,这使得累加器无用,因为它们只返回未定义顺序的文档,从而得到奇怪的结果。

要调试管道,请运行累积添加每个步骤并在每个步骤检查生成的文档。例如,您可以从 $match 管道开始,并验证该阶段的结果是否仅为预期结果:

db.Logging.aggregate([
    { 
        "$match": {
            "action": { 
                "$in": [
                    "resetDslVerify",
                    "assia/reset/RequestQueryOs"
                ]
            }
        }
    }
])

在此阶段观察生成的文档,这些文档应该是符合给定条件的文档(在您的案例中缩写为使用 $in 运算符)并且是任意排序的。

添加下一个管道步骤:

db.Logging.aggregate([
    { 
        "$match": {
            "action": { 
                "$in": [
                    "resetDslVerify",
                    "assia/reset/RequestQueryOs"
                ]
            }
        }
    },
    {
        "$group": {
            "_id": "$idOrdem",
            "timestamp1": { "$first": "$timestamp" }
            "timestamp2": { "$last": "$timestamp" }
            "timeElapsed": { "$first": "$timeElapsed" }
        }
    }
])

现在这是事情变得有趣的地方,生成的管道中的文档按任意顺序排列,因为它们按此顺序进入 $group 阶段。即使放置最后的 $sort 管道步骤也不会删除庆祝软膏中的苍蝇:它不会改变原始文档的顺序,只是{{1的顺序}}

解决方案是过滤掉 $match 步骤中的空值,将 $sort 运算符放在 {{3管道,以及添加按键和GROUP属性分组的其他排序字段:

timeElapsed

不使用 $group 管道的更好方法是使用 $sort {{ 3}} 运营商:

db.Logging.aggregate([
    { 
        "$match": {
            "action": { 
                "$in": [
                    "resetDslVerify",
                    "assia/reset/RequestQueryOs"
                ]
            },
            "timestamp": { "$ne": null },
            "timeElapsed": { "$ne": null }
        }
    },
    { "$sort": { "idOrdem": 1, "timestamp": -1, "timeElapsed": -1 } }
    {
        "$group": {
            "_id": "$idOrdem",
            "timestamp1": { "$first": "$timestamp" }
            "timestamp2": { "$last": "$timestamp" }
            "timeElapsed": { "$first": "$timeElapsed" }
        }
    }
])