我在使用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)。我哪里错了?
答案 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" }
}}
])