从数组聚合总计

时间:2017-07-27 16:25:36

标签: mongodb aggregation-framework

我在使用MongoDb分组数据时遇到了问题。

我有一些项目有一些进/出动作,我想计算一个项目的摘要,但是动作没有正确计算。

这是我的样本数据,一个包含两个股票和一些动作的小清单......

/* 1 */
{
    "TemplateName" : "SAALottoStagionatura",
    "idStock" : 31789,
    "idWarehouse" : 191,
    "StockCode" : "71529902",   
    "Marks" : [ 
        {
            "idMark" : 20145,
            "idWarehouse" : 191,
            "idStock" : 31789,
            "ProgressivoDocumento" : 486,
            "Year" : 2016,
            "RefDate" : ISODate("2016-03-28T22:00:00.000Z"),
            "MarkedItems" : 72
        }, 
        {
            "idMark" : 20156,
            "idWarehouse" : 191,
            "idStock" : 31789,
            "ProgressivoDocumento" : 497,
            "Year" : 2016,
            "RefDate" : ISODate("2016-03-30T22:00:00.000Z"),
            "MarkedItems" : 144
        }, 
        {
            "idMark" : 23424,
            "idWarehouse" : 191,
            "idStock" : 31789,
            "ProgressivoDocumento" : 840,
            "Year" : 2016,
            "RefDate" : ISODate("2016-06-12T22:00:00.000Z"),
            "MarkedItems" : 3
        }
    ],
    "Details" : [ 
        {
            "idLSDetail" : 42781,
            "idStock" : 31789,
            "idStockOrig" : 54502,
            "StockCode" : "71529902",
            "Items" : 4532
        }
    ],   
    "MovementsOut" : [ 
        {
            "idMovementDetail" : 633,
            "idMovement" : 511,
            "MovedItems" : 3528 ,
            "idStockOrig" : null,
            "idStock" : 31789
        }
    ],
    "MovementsIn" : [ 
        {
            "idMovementDetail" : 715,
            "idMovement" : 570,
            "MovedItems" : 3528,
            "idStockOrig" : null,
            "idStock" : 33678
        }
    ]
}


/* 2 */
{
    "TemplateName" : "SAALottoStagionatura",
    "idStock" : 33678,
    "idWarehouse" : 190,
    "StockCode" : "71529902",
    "Marks" : [],
    "Details" : [ 
        {
            "idLSDetail" : 45206,
            "idStock" : 33678,
            "idStockOrig" : 56684,
            "StockCode" : "71529902",
            "Items" : 3528 

        }
    ],
   "MovementsOut" : [ 
        {
           "idMovementDetail" : 715,
            "idMovement" : 570,
            "MovedItems" : 3528,
            "idStockOrig" : null,
            "idStock" : 33678
        }
    ],
    "TrasferimentiInEntrata" : []
}

在我的查询中,我尝试对动作进行分组

db.getCollection('Test')
.aggregate(
 [
      {$match: {"idWarehouse": 191, StockCode: "71529902" } }, 
      {$unwind: "$Details"}, 
      {$unwind: "$Marks"},       
      {$unwind: "$MovementsIn"},     
      {$unwind: "$MovementsOut"},     
       {
        $group : {
           _id : {  
               StockCode: "$idStock",
               StockCode: "$StockCode" 
               },

           tot: { $sum: "$Details.Items" },
           cer: { $sum: "$Marks.MarkedItems" },
           in: { $sum: "$MovementsIn.MovedItems" },
           out: { $sum: "$MovementsOut.MovedItems" }   
        }
      } 
   ]
)

我的期望应该是这个

{
    "_id" : {
        "StockCode" : "71529902"
    },
    "tot" : 13596,
    "cer" : 219,
    "in" : 3528,
    "out" : 7056
}

然而,我总是得到进出的全部运动总和(10584)。我哪里错了?

1 个答案:

答案 0 :(得分:1)

实际上,在3.2版以来的任何现代MongoDB版本中,你只需使用" double barelled"调用$sum,而不是$unwind

db.getCollection('Test').aggregate([
  { "$group": {
    "_id": {
      "StockCode": "$StockCode"
    },
    "tot": { "$sum": { "$sum": "$Details.Items" } },
    "cer": { "$sum": { "$sum": "$Marks.MarkedItems" } },
    "in": { "$sum": { "$sum": "$MovementsIn.MovedItems" } },
    "out": { "$sum": { "$sum": "$MovementsOut.MovedItems" } }
  }}
])

这是因为从那个版本开始,当你在"$Details.Items"等数组中注明一个元素时,投影结果是一个"值数组"在指定的路径找到。第二个补充是$sum也"汇总数组",因此它在组中调用$sum数组内容,然后$sum作为& #34;累加器"文件之间。

在针对您问题中的两个文档运行时返回结果:

/* 1 */
{
    "_id" : {
        "StockCode" : "71529902"
    },
    "tot" : 8060.0,
    "cer" : 219.0,
    "in" : 3528.0,
    "out" : 7056.0
}

在诸如MongoDB 2.6之类的早期版本中,您可以避免使用"笛卡尔产品"这是多个数组上$unwind的结果,它将数组合并为一个,如果标识符和值实际上是唯一的,则可能使用$setUnion

db.getCollection('Test').aggregate([
  { "$project": {
    "StockCode": 1,
    "combined": { 
      "$setUnion": [
        { "$map": {
          "input": { "$ifNull": [ "$Details", [] ] },
          "as": "el",
          "in": { "id": "$idLSDetail", "k": "Details", "v": "$$el.Items" }
        }},
        { "$map": {
          "input": { "$ifNull": [ "$Marks", [] ] },
          "as": "el",
          "in": { "id": "$idMark", "k": "Marks", "v": "$$el.MarkedItems" }
        }},
        { "$map": {
          "input": { "$ifNull": [ "$MovementsIn", [] ] },
          "as": "el",
          "in": { "id": "$idMovementDetail", "k": "MoveIn", "v": "$$el.MovedItems" }
        }},  
        { "$map": {
          "input": { "$ifNull": [ "$MovementsOut", [] ] },
          "as": "el",
          "in": { "id": "$idMovementDetail", "k": "MoveOut", "v": "$$el.MovedItems" }
        }}
      ]
    }
  }},
  { "$unwind": "$combined" },
  { "$group": {
    "_id": {
      "StockCode": "$StockCode" 
    },
    "tot": {
      "$sum": {
        "$cond": {
          "if": { "$eq": [ "$combined.k", "Details" ] },
          "then": "$combined.v",
          "else": 0  
        }  
      }  
    },
    "cer": {
      "$sum": {
        "$cond": {
          "if": { "$eq": [ "$combined.k", "Marks" ] },
          "then": "$combined.v",
          "else": 0  
        }  
      }  
    },
    "in": {
      "$sum": {
        "$cond": {
          "if": { "$eq": [ "$combined.k", "MoveIn" ] },
          "then": "$combined.v",
          "else": 0  
        }  
      }  
    },
    "out": {
      "$sum": {
        "$cond": {
          "if": { "$eq": [ "$combined.k", "MoveOut" ] },
          "then": "$combined.v",
          "else": 0  
        }  
      }  
    }
  }}
])

在旧版本中或者确实在哪里"唯一性"是不可能的,你可以分别$unwind每个数组然后再$group,然后重复这个过程,直到数组减少为#34;。然后你可以$group作为文件的最终版本。

但是如上所述,您需要注意,因为并非所有文档都包含所有数组,因此需要对其进行处理。在上面的示例中,我们可以提供一个空的"数组而不是null(在初始$sum中并不关心)。但是,如果你在每个人身上单独使用$unwind,那么如果没有任何内容或空的话会遇到问题:

 db.getCollection('Test').aggregate([
  { "$project": {
     "StockCode": 1,
     "Details": { 
       "$cond": [
          { "$eq": [{ "$size": { "$ifNull": [ "$Details", [] ] } }, 0] },
          [null],
          "$Details"
        ] 
     },
     "Marks": { 
       "$cond": [
          { "$eq": [{ "$size": { "$ifNull": [ "$Marks", [] ] } }, 0] },
          [null],
          "$Marks"
        ] 
     },
     "MovementsIn": { 
       "$cond": [
          { "$eq": [{ "$size": { "$ifNull": [ "$MovementsIn", [] ] } }, 0] },
          [null],
          "$MovementsIn"
        ] 
     },
     "MovementsOut": { 
       "$cond": [
          { "$eq": [{ "$size": { "$ifNull": [ "$MovementsOut", [] ] } }, 0] },
          [null],
          "$MovementsOut"
        ] 
     }
  }},
  { "$unwind": "$Details" },
  { "$group": {
    "_id": "$_id",
    "StockCode": { "$first": "$StockCode" },
    "tot": { "$sum": "$Details.Items" },
    "Marks": { "$first": "$Marks" },
    "MovementsIn": { "$first": "$MovementsIn" },
    "MovementsOut": { "$first": "$MovementsOut" }    
  }},
  { "$unwind": "$Marks" },
  { "$group": {
    "_id": "$_id",
    "StockCode": { "$first": "$StockCode" },
    "tot": { "$first": "$tot" },
    "cer": { "$sum": "$Marks.MarkedItems" },
    "MovementsIn": { "$first": "$MovementsIn" },
    "MovementsOut": { "$first": "$MovementsOut" }    
  }},
  { "$unwind": "$MovementsIn" },
  { "$group": {
    "_id": "$_id",
    "StockCode": { "$first": "$StockCode" },
    "tot": { "$first": "$tot" },
    "cer": { "$first": "$cer" },
    "in": { "$sum": "$MovementsIn.MovedItems" },
    "MovementsOut": { "$first": "$MovementsOut" }    
  }},
  { "$unwind": "$MovementsOut" },
  { "$group": {
    "_id": "$_id",
    "StockCode": { "$first": "$StockCode" },
    "tot": { "$first": "$tot" },
    "cer": { "$first": "$cer" },
    "in": { "$first": "$in" },
    "out": { "$sum": "$MovementsOut.MovedItems" }    
  }},
  { "$group": {
    "_id": {
      "StockCode": "$StockCode",
    },
    "tot": { "$sum": "$tot" },
    "cer": { "$sum": "$cer" },
    "in": { "$sum": "$in" },
    "out": { "$sum": "$out" }
  }}
])

因此,如果需要替换数组,则需要$ifNull$size进行测试。