MongoDB - 映射结构中的分组和求和聚合

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

我对 MongoDB 还很陌生,我需要对集合中所有文档中每个唯一日期的所有发票总数进行分组。这是集合的摘要,其中数据存储在键值对中:

Document 1:
{
  "data": [
    {
      "key": "invoice-date",
      "value": {
        "$date": "2023-05-04T00:00:00.000Z"
      }
    },
    {
      "key": "invoice-total",
      "value": 110.29
    }
  ]
}

Document 2:
{
  "data": [
    {
      "key": "invoice-date",
      "value": {
        "$date": "2023-05-04T00:00:00.000Z"
      }
    },
    {
      "key": "invoice-total",
      "value": 47.18
    }
  ]
}

Document 3:
{
  "data": [
    {
      "key": "invoice-date",
      "value": {
        "$date": "2023-05-05T00:00:00.000Z"
      }
    },
    {
      "key": "invoice-total",
      "value": 22.05
    }
  ]
}

这应该被分组到一个地图中,结果应该如下:

"2023-05-04T00:00:00.000Z": 157.47
"2023-05-05T00:00:00.000Z": 22.05

这是我迄今为止在 Spring 3.1.4 中尝试过的:


        MatchOperation matchStage = Aggregation.match(Criteria.where("data.key").in("invoice-date", "invoice-total"));

        
        GroupOperation groupStage = Aggregation.group("invoice-date")
                        .sum(ConditionalOperators.when(Criteria.where("data.key").is("invoice-total")).then("data.value")
                                        .otherwise(0))
                        .as("total");


        ProjectionOperation projectionStage = Aggregation.project()
                        .andExpression("_id").as("invoice-date")
                        .andInclude("total");


        Aggregation aggregation = Aggregation.newAggregation(
                        matchStage,
                        groupStage,
                        projectionStage);

        return mongoTemplate.aggregate(aggregation, "documents", Map.class);

但是,返回的总计始终为 0,日期为空。我怎样才能使聚合工作?

spring mongodb sum aggregate
2个回答
0
投票

您的查询不起作用,因为您尝试按“发票日期”进行分组,“发票日期”是一个值,而不是一个字段。

可以通过从

data
数组中提取值作为
invoiceDate
invoiceTotal
字段(第二阶段)来解决。

要转换一组日期及其各自的总计,您需要

$group
将所有文档转换为一个文档并转换为键值对。

db.collection.aggregate([
  {
    $match: {
      "data.key": {
        $in: [
          "invoice-date",
          "invoice-total"
        ]
      }
    }
  },
  {
    $set: {
      invoiceDate: {
        $getField: {
          input: {
            $first: {
              $filter: {
                input: "$data",
                cond: {
                  $eq: [
                    "$$this.key",
                    "invoice-date"
                  ]
                }
              }
            }
          },
          field: "value"
        }
      },
      invoiceTotal: {
        $getField: {
          input: {
            $first: {
              $filter: {
                input: "$data",
                cond: {
                  $eq: [
                    "$$this.key",
                    "invoice-total"
                  ]
                }
              }
            }
          },
          field: "value"
        }
      }
    }
  },
  {
    $group: {
      _id: "$invoiceDate",
      total: {
        $sum: "$invoiceTotal"
      }
    }
  },
  {
    $group: {
      _id: null,
      data: {
        $push: {
          k: {
            $toString: "$_id"
          },
          v: "$total"
        }
      }
    }
  },
  {
    $replaceRoot: {
      newRoot: {
        $arrayToObject: "$data"
      }
    }
  }
])

演示@Mongo Playground

抱歉,我不是 Spring/Java MongoDB 开发人员,根据我的研究,Spring 代码应该是:

MatchOperation matchStage = Aggregation.match(Criteria.where("data.key").in("invoice-date", "invoice-total"));

AggregationOperation setStage = new AggregationOperation() {
    @Override
    public Document toDocument(AggregationOperationContext context) {
        return new Document("$set", 
            new Document("invoiceDate", 
                new Document("$getField", 
                    new Document("input", 
                        new Document("$first", 
                            new Document("$filter", 
                                new Document("input", "$data")
                                    .append("cond", 
    new Document("$eq", Arrays.asList("$$this.key", "invoice-date"))))))
                                    .append("field", "value")))
                .append("invoiceTotal", 
                    new Document("$getField", 
                        new Document("input", 
                            new Document("$first", 
                                new Document("$filter", 
                                    new Document("input", "$data")
                                        .append("cond", 
                                            new Document("$eq", Arrays.asList("$$this.key", "invoice-total"))))))
                        .append("field", "value"))));
    }
};

GroupOperation groupStage = Aggregation.group("invoiceDate")
    .sum("invoiceTotal")
    .as("total");

AggregationOperation secondGroupStage = new AggregationOperation() {
    @Override
    public Document toDocument(AggregationOperationContext context) {
        return new Document("$group", 
            new Document("_id",  new BsonNull())
                .append("data", 
                    new Document("$push", 
                        new Document("k", 
                            new Document("$toString", "$_id"))
                .append("v", "$total"))));
    }
};

AggregationOperation replaceRootStage = new AggregationOperation() {
    @Override
    public Document toDocument(AggregationOperationContext context) {
        return new Document("$replaceRoot", 
            new Document("newRoot", 
                new Document("$arrayToObject", "$data")));
    }
};

Aggregation aggregation = Aggregation.newAggregation(
    matchStage,
    setStage,
    groupStage,
    secondGroupStage,
    replaceRootStage);

return mongoTemplate.aggregate(aggregation, "documents", Map.class);

0
投票

而不是

$getField
我会使用
$arrayToObject

db.collection.aggregate([
   { $match: { "data.key": { $in: ["invoice-date", "invoice-total"] } } },
   {
      $set: {
         data: {
            $arrayToObject: {
               $map: {
                  input: "$data",
                  in: { k: "$$this.key", v: "$$this.value" }
               }
            }
         }
      }
   },
   {
      $group: {
         _id: { $toString: "$data.invoice-date" },
         total: { $sum: "$data.invoice-total" }
      }
   },
   {
      $group: {
         _id: null,
         data: { $push: { k: "$_id", v: "$total" } }
      }
   },
   { $replaceWith: { $arrayToObject: "$data" } }
])

蒙戈游乐场

© www.soinside.com 2019 - 2024. All rights reserved.