Mongodb 聚合以从值创建动态键

Mongodb 聚合以从值创建动态键,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我有一个聚合表达式,如下所示: db.Booking.aggregate([ { $match: { created_at: {$exists:true} } }, { $sort : { created_at : -1 } }, { $group: { _id: { year : { $year : "$created_at" }, month : { $month : "$created_

我有一个聚合表达式,如下所示:

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}
]
我如何得到这样的结果:

[
  {"_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来进行修改

您真的“应该”处理结果,在接收代码中命名这些键,而不是依赖数据库来完成这项工作。虽然在最新版本中是可能的,但它确实需要一些争论

这要求MongoDB 3.4至少:

db.Booking.aggregate([
{“$match”:{“created_at”:{“$exists”:true}},
{“$sort”:{“created_at”:-1},
{“$组”:{
“_id”:{
“年”:{“$year”:“$created_at”},
“月”:{“$month”:“$created_at”},
“接受”:{
“$concat”:[
{“$toUpper”:{“$substrCP”:[“$accepted”,0,1]},
{“$toLower”:{“$substrCP”:[“$accepted”,1,{“$strLenCP”:“$accepted”}]}
]
}
},
“总计”:{“$sum”:“$total”}
}},
{“$replaceRoot”:{
“新根”:{
“$arrayToObject”:{
“$concatarray”:[
[
{“k”:“\u id”,“v”:{“年”:“$\u id.year”,“月”:“$\u id.month”},
{“k”:“$\接受id”,“v”:“$total”}
]
]
}
}
}}
])
其中大部分由
$arrayToObject
和管道阶段处理。可能需要注意的是,这里的用法似乎是必要的,因为如果只是将数组文本作为参数提供,则
$arrayToObject
会发出抱怨。因此,我们使用一个运算符,它从它自己的给定表达式返回一个数组,这似乎是最简短的表示法

这似乎是一个bug(有待确认),但可能只是作为预期用途,因为对于大多数聚合运算符,数组
[]
参数通常用于多个参数

此外,您的常规语句中似乎存在问题,因为
的“accepted”
值在大小写中有所不同,MongoDB将对其进行不同的处理。您可以使用诸如和之类的运算符来更正此问题。在这里,我使用了一些现代运算符,使第一个字母的大小写保持一致,但基本的大小写函数在所有支持
.aggregate()
的版本中都可用

当然,后处理非常简单,可以在任何版本中完成。对于来自MongoDB shell的JavaScript,其简单如下:

db.Booking.aggregate([
{“$match”:{“created_at”:{“$exists”:true}},
{“$sort”:{“created_at”:-1},
{“$组”:{
“_id”:{
“年”:{“$year”:“$created_at”},
“月”:{“$month”:“$created_at”},
“接受”:{
“$concat”:[
{“$toUpper”:{“$substrCP”:[“$accepted”,0,1]},
{“$toLower”:{“$substrCP”:[“$accepted”,1,{“$strLenCP”:“$accepted”}]}
]
}
},
“总计”:{“$sum”:“$total”}
}}
]).forEach(doc=>{
单据【单据号已受理】=单据总数;
删除单据。\u id.已接受;
删除单据合计;
printjson(文档)
})
两者都生产相同的产品:

{u id:{“年”:2017,“月”:4},“过期”:25}
{u id:{“年”:2017,“月”:4},“待定”:390}
{u id:{“年”:2017,“月”:5},“待定”:1400}

如果您实际上希望在分组结果中同时使用“挂起”和“过期”键,那么这就简单得多:

db.Booking.aggregate([
{“$match”:{“created_at”:{“$exists”:true}},
{“$组”:{
“_id”:{
“年”:{“$year”:“$created_at”},
“月”:{“$month”:“$created_at”}
},
“过期”:{
“$sum”:{
“$cond”:[
{“$eq”:[{“$toLower”:“$accepted”},“expired”]},
“$total”,
0
]
}
},
“待定”:{
“$sum”:{
“$cond”:[
{“$eq”:[{“$toLower”:“$accepted”},“pending”]},
“$total”,
0
]
}
}
}},
{“$sort”:{“\u id”:1}
])
返回:

{u id:{“年”:2017,“月”:4},“过期”:25,“待定”:390}
{u id:{“年”:2017,“月”:5},“过期”:0,“待定”:1400}

由于文档中的键有一个“固定”格式,因此它们不会发生任何变化,然后这是一个简单的布局,我们只使用它来决定在每个键下累积哪些值。

MongoError:invalid operator'$substrCP'
,但我希望按月份和年份对它进行分组,以便过期和
挂起
object@MuhaiminAbdul请阅读答案。您需要MongoDB 3.4。否则,只需重复光标并使用
$toLower
方法,就像答案所说的那样。@MuhaiminAbdul和您的“问题”正好显示了我给出的输出。@MuhaiminAbdul您去哪里了?你问的问题已经回答了,不是吗?请阅读并说明问题的答案。如果您认为它没有回答问题,请留下评论,解释未解决的问题。