我有两个不同的文档,我想用匹配的字段进行分组(在这种情况下为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" }
答案 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" }
}
}
])