MongoDB使用条件求和进行聚合

时间:2014-07-23 10:09:53

标签: mongodb mongodb-query aggregation-framework

我必须做一个特殊的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'}]
         }
         }]);

1 个答案:

答案 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
    }
}