MongoDB 聚合 - 嵌套数组的总和值

问题描述 投票:0回答:2

我正在尝试获取数组内特定字段的总和。问题是,该数组也在另一个数组内。我的文件如下(简化):

{
    "nome": "test-1"
    "notas": [{
        "numero_fiscal": "0001",
        "valores": {
            "portal": 1000,
            "arquivo": 1000
        },
        "abatimentos": [{
            "valor": 250,
            "tipo": "TIPO 1"
        }, {
            "valor": 250,
            "tipo": "TIPO 2"
        }]
    }, {
        "numero_fiscal": "0002",
        "valores": {
            "portal": 2000,
            "arquivo": 2000
        },
        "abatimentos": [{
            "valor": 500,
            "tipo": "TIPO 1"
        }, {
            "valor": 500,
            "tipo": "TIPO 2"
        }]
    }]
}

我希望我的输出是这样的:

{
    "_id": "resumo",
    "total_notas": 2, // this is the counter for the array "notas"
    "soma_portal": 3000, // this is the sum of the "valores.portal" of all "notas"
    "soma_arquivo": 3000, // this is the sum of the "valores.arquivo" of all "notas"
    "soma_abatimento": 1500 // this is the sum of all the "abatimentos.valor" from all the "notas"
}

我尝试了以下聚合(除其他外,但结果始终相同):

Notas.aggregate()
.match(my_query)
.unwind('notas') // unwinding 'notas' array
.group({
    '_id': 'resumo',
    'total_notas': { '$sum': 1 },
    'abatimentos': { '$push': '$notas.abatimentos' },
    'soma_portal': { '$sum': { '$notas.valores.portal' } },
    'soma_arquivo': { '$sum': { '$notas.valores.arquivo' } }
})
.unwind('$abatimentos')
.group({
    '_id': '$_id',
    'total_notas': { '$first': '$total_notas' },
    'soma_portal': { '$first': '$soma_portal' },
    'soma_arquivo': { '$first': '$soma_arquivo' },
    'soma_abatimento': { '$sum': '$abatimentos.valor' }
})

这几乎为我提供了我想要的一切,但“soma_abatimento”始终为 0,而不是 1500

我走的路正确吗?或者这是不应该在数据库查询上完成的事情?

arrays mongodb aggregation-framework
2个回答
7
投票

在当前版本中,您需要添加一个双 $unwind,因为正如您所正确识别的那样,在第一个 $group 之后,您在数组中包含一个数组。

这是一个稍微修改过的版本,可以产生预期的结果。

db.Notas.aggregate([{
  $unwind: "$notas"
}, {
  $group: {
    _id: "resumo",
    total_notas: { $sum: 1 },
    soma_portal: { $sum: "$notas.valores.portal" },
    soma_arquivo: { $sum: "$notas.valores.arquivo" },
    abatimentos: { $push: "$notas.abatimentos" }
  }
}, {
  $unwind: "$abatimentos"
}, {
  $unwind: "$abatimentos"
}, {
  $group: {
    _id: "$_id",
    total_notas: { $first: "$total_notas" },
    soma_portal: { $first: "$soma_portal" },
    soma_arquivo: { $first: "$soma_arquivo" },
    soma_abatimentos: { $sum: "$abatimentos.valor" }
  }
}])

如果您能够使用 MongoDB >= 3.4,我会推荐一种不同的方法,该方法将减少文档大小,并消除使用 MongoDB 3.4 中添加的 $reduce 进行多个 $unwind 的需要

db.Notas.aggregate([{
  $unwind: "$notas"
}, {
  $project: {
    _id: 0,
    portal: "$notas.valores.portal",
    arquivo: "$notas.valores.arquivo",
    abatimentos: {
      $reduce: {
        input: "$notas.abatimentos",
        initialValue: 0,
        in: { $add: ["$$value", "$$this.valor"] }
      }
    }
  }
}, {
  $group: {
    _id: "resumo",
    total_notas: { $sum: 1 },
    soma_portal: { $sum: "$portal" },
    soma_arquivo: { $sum: "$arquivo" },
    soma_abatimentos: { $sum: "$abatimentos" }
  }
}])

0
投票

'$notas.abatimentos.valor'
项目
[[250, 250], [500, 500]]
。所以你可以使用
$map

{
  $project: {
    abatimentos: {
      $sum: {
        $map: {
          input: '$notas.abatimentos.valor',
          in: { $sum: '$$this' }
        }
      }
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.