我对 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,日期为空。我怎样才能使聚合工作?
您的查询不起作用,因为您尝试按“发票日期”进行分组,“发票日期”是一个值,而不是一个字段。
可以通过从
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"
}
}
}
])
抱歉,我不是 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);
而不是
$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" } }
])