我有一个聚合表达式如下:
db.Booking.aggregate([
{ $match: { created_at: {$exists:true} } },
{ $sort : { created_at : -1 } },
{ $group: {
_id: {
year : { $year : "$created_at" },
month : { $month : "$created_at" },
accepted: '$accepted'
},
total: {
$sum: '$total'
}
}
}
])
会产生如下结果:
[
{"_id":{"year":2017,"month":4,"accepted":"expired"},"total":0},
{"_id":{"year":2017,"month":4,"accepted":"Expired"},"total":25},
{"_id":{"year":2017,"month":4,"accepted":"Pending"},"total":390},
{"_id":{"year":2017,"month":5,"accepted":"Pending"},"total":1400}
]
我如何得到这样的结果:
[
{"_id":{"year":2017,"month":4},"expired":0},
{"_id":{"year":2017,"month":4},"Expired":25},
{"_id":{"year":2017,"month":4},"Pending":390},
{"_id":{"year":2017,"month":5},"Pending":1400}
]
我是否必须使用常规javascript进行修改,而不是依赖Mongodb来完成工作
答案 0 :(得分:1)
你真的“应该”处理结果,在接收代码中命名这些键,而不是依靠数据库来执行此操作。虽然最新的当前版本可能会有一些争吵。
这至少需要 MongoDB 3.4 :
db.Booking.aggregate([
{ "$match": { "created_at": { "$exists": true } } },
{ "$sort": { "created_at": -1 } },
{ "$group": {
"_id": {
"year": { "$year": "$created_at" },
"month": { "$month": "$created_at" },
"accepted": {
"$concat": [
{ "$toUpper": { "$substrCP": [ "$accepted", 0, 1 ] } },
{ "$toLower": { "$substrCP": [ "$accepted", 1, { "$strLenCP": "$accepted" } ] } }
]
}
},
"total": { "$sum": "$total" }
}},
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$concatArrays": [
[
{ "k": "_id", "v": { "year": "$_id.year", "month": "$_id.month" } },
{ "k": "$_id.accepted", "v": "$total" }
]
]
}
}
}}
])
大部分内容由$arrayToObject
和$replaceRoot
管道阶段处理。可以注意到$concatArrays
这里的使用似乎是必要的,因为$arrayToObject
抱怨如果只是提供数组文字作为参数。因此,我们使用一个运算符,它从它自己的给定表达式返回一个数组,而$concatArrays
似乎是最简短的表示法。
这似乎是一个错误(有待确认),但可能只是作为预期用法使用,因为对于大多数聚合运算符,数组[]
参数通常用于多个参数。
此外,您的常规$group
语句中似乎存在问题,因为"accepted"
值的大小写会有所不同,MongoDB会对其进行不同的处理。您可以使用$toLower
和$toUpper
等运算符来更正此问题。在这里,我使用一些现代运算符将一致的首字母大写为大写,其余为小写,但基本的大小写函数在支持.aggregate()
的所有版本中都可用。
当然,后期处理非常简单,可以在任何版本中完成。对于MongoDB shell中的JavaScript,它简单如下:
db.Booking.aggregate([
{ "$match": { "created_at": { "$exists": true } } },
{ "$sort": { "created_at": -1 } },
{ "$group": {
"_id": {
"year": { "$year": "$created_at" },
"month": { "$month": "$created_at" },
"accepted": {
"$concat": [
{ "$toUpper": { "$substrCP": [ "$accepted", 0, 1 ] } },
{ "$toLower": { "$substrCP": [ "$accepted", 1, { "$strLenCP": "$accepted" } ] } }
]
}
},
"total": { "$sum": "$total" }
}}
]).forEach(doc => {
doc[doc._id.accepted] = doc.total;
delete doc._id.accepted;
delete doc.total;
printjson(doc)
})
两者产生相同的东西:
{ "_id" : { "year" : 2017, "month" : 4 }, "Expired" : 25 }
{ "_id" : { "year" : 2017, "month" : 4 }, "Pending" : 390 }
{ "_id" : { "year" : 2017, "month" : 5 }, "Pending" : 1400 }
如果你实际上想要在分组结果中“同时”使用“待定”和“过期”键,那么这就更简单了:
db.Booking.aggregate([
{ "$match": { "created_at": { "$exists": true } } },
{ "$group": {
"_id": {
"year": { "$year": "$created_at" },
"month": { "$month": "$created_at" }
},
"Expired": {
"$sum": {
"$cond": [
{ "$eq": [ { "$toLower": "$accepted" }, "expired" ] },
"$total",
0
]
}
},
"Pending": {
"$sum": {
"$cond": [
{ "$eq": [ { "$toLower": "$accepted" }, "pending" ] },
"$total",
0
]
}
}
}},
{ "$sort": { "_id": 1 } }
])
返回:
{ "_id" : { "year" : 2017, "month" : 4 }, "Expired" : 25, "Pending" : 390 }
{ "_id" : { "year" : 2017, "month" : 5 }, "Expired" : 0, "Pending" : 1400 }
由于文档中的键有“固定”格式,但它们没有变化,因此这是一个简单的布局,我们只需使用$cond
来决定在$sum
下每个键下累积哪些值。 3}}