我必须做一个特殊的mongoDB查询。
我有一个包含以下文档的集合:
{
"_id" : ObjectId("53c7fd86d624b06abc76e8f6"),
"works" : [
{
"code" : "A001",
"name" : "Cambiar bombilla",
"orderId" : "53c7fd86d624b06abc76e8f6",
"price" : 1400,
"ID" : 1,
"lazyLoaded" : true,
"status" : 0,
"Date" : ISODate("2014-07-21T10:31:55.063Z"),
"TechnicianId" : "538efd918163b19307c59e8e",
"_id" : ObjectId("53ccec1bf2bf4d5952b2f205")
},
{
"code" : "A005",
"name" : "Cambiar bombilla 5",
"price" : 1050,
"type" : "Bombillas",
"TechnicianId" : "5383577a994be8b9a9e3f01e",
"_id" : ObjectId("53ccfdbdf2bf4d5952b2f206")
},
{
"code" : "A004",
"name" : "Cambiar bombilla 4",
"price" : 1010,
"type" : "Bombillas",
"TechnicianId" : "5383577a994be8b9a9e3f01e",
"date" : "2014-07-21T11:50:52.702Z",
"orderId" : "53c7fd86d624b06abc76e8f6",
"_id" : ObjectId("53ccfe9c109c100000ad688a")
},
{
"code" : "A002",
"name" : "Cambiar bombilla 2",
"price" : 1030,
"type" : "Bombillas",
"TechnicianId" : "5383577a994be8b9a9e3f01e",
"date" : "2014-07-21T11:57:37.065Z",
"orderId" : "53c7fd86d624b06abc76e8f6",
"_id" : ObjectId("53cd0036109c100000ad688b")
},
{
"code" : "A003",
"name" : "Cambiar bombilla 3",
"price" : 1050,
"type" : "Bombillas",
"TechnicianId" : "5383577a994be8b9a9e3f01e",
"date" : "2014-07-21T11:59:35.586Z",
"orderId" : "53c7fd86d624b06abc76e8f6",
"_id" : ObjectId("53cd00a7109c100000ad688c")
}
],
"Items" : [
{
"_id" : "534ba71f394835a7e51dd938",
"total":50
"qty" : 2
},
{
"_id" : "534664b081362062015d1b77",
"qty" : 2,
"total":30
}
]}
现在,我想查询以获得TechnicianId = 5383577a994be8b9a9e3f01e的作品,这些作品的“价格”总和扣除items.total的总和,这些文件扣除总和的总工程量items.total。
对于这个例子,我想要这样的东西:
{
"result" : [
{
"_id" : ObjectId("53c7fd86d624b06abc76e8f6"),
"works.totalsumfortech" : 1050+1010+1030+1050 - (50+30),//Sum of works of this technicianId deducting the items.total qty
"works.total":1400+1050+1010+1030+1050-(50+30)//Summ of all works of document deducting the items.total qty
}
],
"ok" : 1
}
这是我当前的查询,但我没有得到预期的结果..
db.orders.aggregate([
{ "$match": {
"$and": [
{"OrderState": {$in:['Review','Archived']}},
{'works.TechnicianId':'5383577a994be8b9a9e3f01e'}
]
}},
{$group: {
_id: "$_id",
'total': {$subtract:[{$sum: { $cond: [ { $eq: [ "$works.TechnicianId", "5383577a994be8b9a9e3f01e" ] } , 2, 1 ]},{$sum: '$Items.total'}]
'total': {$subtract:[{$sum: '$works.price'},{$sum: '$Items.total'}]
}
}]);
答案 0 :(得分:10)
使用MongoDB的现代版本,这实际上要容易得多。在特定情况下,没有实际的"聚合"跨文档,尽管返回的数据显着减少,并且文档内的聚合也是如此。适用。
对此的现代考虑允许从数组中的数据进行这种重新整形和选择,而无需借助$unwind
和$group
来处理:
db.getCollection('orders').aggregate([
{ "$match": {
//"OrderState": { "$in":["Review","Archived"]},
"works.TechnicianId":"5383577a994be8b9a9e3f01e"
}},
{ "$project": {
"works": {
"$let": {
"vars": {
"techTotal": {
"$sum": {
"$map": {
"input": {
"$filter": {
"input": "$works",
"cond": {
"$eq": [
"$$this.TechnicianId",
"5383577a994be8b9a9e3f01e"
]
}
}
},
"in": "$$this.price"
}
}
},
"items_total": { "$sum": "$Items.total" },
"worksTotal": { "$sum": "$works.price" }
},
"in": {
"totalSumForTech": {
"$subtract": [ "$$techTotal", "$$items_total" ]
},
"total": {
"$subtract": [ "$$worksTotal", "$$items_total" ]
}
}
}
}
}}
])
自最初提出以来的更改是$sum
接受"数组"作为累加器的传统角色之外,在$project
或类似阶段上下文中使用时的输入。而不是"放松"一个数组,你可以做{ "$sum": "$Items.total" }
这样的事情,它通过内部符号从指定的属性返回一个值数组,然后"减少"这些值来自$sum
。这是它自身的重大改进。
其他改进包括$map
和$filter
。应用后者以便仅将数组的匹配条目返回到给定条件,并且对于前者允许"重塑"数组内容。两者都是其他编程语言中用于处理数组的常用方法,并且在这里具有基本相同的功能。
这意味着您可以根据需要从与技术人员匹配的"price"
数组中提取"works"
值,然后使用$sum
作为"值数组&# 34;以与前面描述的相同方式。
另一个加法是$let
,它允许用"变量"声明一个块。在该区块内使用。在这种情况下,我们可以计算这些"总数"从数组中,然后将$subtract
应用于计算值,以便得出最终结果。
与早期版本相比,您可以在不分离聚合管道阶段的情况下实现这一目标。当然,$$items_total
可以在这里用来代替重复完整的语句来计算。此外,计算的一般分离使得最终输出块更容易阅读。所以它真的只是"使用变量"就像你在常规编程中一样。
这里的巨大收益是简单地$match
和$project
,而不是整个管道阶段链只是为了得到每个文档的最终计算结果。
如前所述,在MongoDB聚合中使用数组时,需要使用$unwind
。除非您这样做,否则聚合操作不适用于每个数组元素。
这里的其他问题是在$group
管道阶段内的所有"顶级"操作需要group aggregation operators。不允许使用与$subtract
不同的内容,因此您需要尽可能在$sum
之内或在其他管道阶段执行这些操作:
db.orders.aggregate([
// Match documents "and" is implied in MongoDB. Not required unless
// against the same field
{ "$match": {
"OrderState": { "$in":["Review","Archived"]},
"works.TechnicianId":"5383577a994be8b9a9e3f01e"
}},
// Unwind Items first
{ "$unwind": "$Items" },
// Group to get that total
{ "$group": {
"_id": "$_id",
"works": { "$first": "$works" },
"items_total": { "$sum": "$Items.total" }
}},
// Unwind works to "de-normalize"
{ "$unwind": "$works" },
// Group conditionally on "TechnicianId" and the full total
{ "$group": {
"_id": "$_id",
"techTotal": {
"$sum": {
"$cond": [
{ "$eq": [
"$works.TechnicianId",
"5383577a994be8b9a9e3f01e"
]},
"$works.price",
0
]
}
},
"worksTotal": { "$sum": "$works.price" },
"items_total": { "$first": "$items_total" }
}},
// Project to do math and other re-shaping
{ "$project": {
"works": {
"totalSumForTech": {
"$subtract": [ "$techTotal", "$items_total" ]
},
"total": {
"$subtract": [ "$worksTotal", "$items_total" ]
}
}
}}
])
在示例文档上(虽然我需要删除$match
,因为样本中不存在该数据),结果是:
{
"_id" : ObjectId("53c7fd86d624b06abc76e8f6"),
"works" : {
"totalSumForTech" : 4060,
"total" : 5460
}
}