【问题标题】:Spring Data MongoDB Aggregation - rename _id in projectionSpring Data MongoDB Aggregation - 在投影中重命名_id
【发布时间】:2020-06-19 23:11:15
【问题描述】:

在 mongo (3.6) shell 中,我可以执行以下聚合来投影并将“_id”重命名为其他名称:

db.collection.aggregate([
  { $group : { _id : ... },
  { $project : {   '_id': 0,        // exclude the id field
                   'name' : '$_id', // project the "old" id value to the "name" field
                   ...
               }
  }
])

我需要使用 spring-data-mongo 翻译这个(通过 Spring Boot 2.3.1)。 我可以使用以下 ProjectionOperation 排除 id 字段:

project().andExclude("_id")

但到目前为止,我无法重命名 id 字段。 以下均无效:

  • 我的第一个猜测

      project().and("_id").as("name")
    
  • 如建议here

      project().andExpression("_id").as("name") 
    

应该没那么难,但我不知道我错过了什么。


编辑:数据样本和完整的聚合管道重现失败

带有以下文件:

{ 
    "_id" : ObjectId("5a0c7a3135587511c9247db4"), 
    "_class" : "task", 
    "category" : "green", 
    "status" : "OK"
}
{ 
    "_id" : ObjectId("5a0cd21d35587511c9247db8"), 
    "_class" : "task", 
    "category" : "red", 
    "status" : "KO"
}

域对象:

@Document(collection = "tasks")
@TypeAlias("task")
public class Task {

    @Id
    private String id;
    private String status;
    private String category;

    // getters/setters omitted
}

还有以下聚合:

Aggregation a = Aggregation.newAggregation(
                group("status", "category").count().as("count"),
                group("_id.category").push(
                        new Document("k", "$_id.status").append("v", "$count"))
                        .as("counts"),
                project().and(arrayToObject("$counts")).as("counts"),
                // this final stage fails with: a java.lang.IllegalArgumentException: Invalid reference '_id'!
                project().and("_id").as("name").andExclude("_id")
        );

mongotemplate.aggregate(a, "tasks", org.bson.Document.class)

在最后一个管道阶段之前,聚合给出:

[ { "_id" : "green", "counts" : { "OK" : 1 } },
  { "_id" : "red", "counts" : { "KO" : 1 } } ]

所以我们应该能够将“_id”投影到“name”,但是我们得到了这个:

java.lang.IllegalArgumentException: Invalid reference '_id'!
    at org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext.getReference(ExposedFieldsAggregationOperationContext.java:114) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext.getReference(ExposedFieldsAggregationOperationContext.java:77) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.ProjectionOperation$ProjectionOperationBuilder$FieldProjection.renderFieldValue(ProjectionOperation.java:1445) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.ProjectionOperation$ProjectionOperationBuilder$FieldProjection.toDocument(ProjectionOperation.java:1432) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.ProjectionOperation.toDocument(ProjectionOperation.java:261) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.AggregationOperation.toPipelineStages(AggregationOperation.java:55) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.AggregationOperationRenderer.toDocument(AggregationOperationRenderer.java:56) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.aggregation.Aggregation.toPipeline(Aggregation.java:721) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.AggregationUtil.createPipeline(AggregationUtil.java:95) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2118) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2093) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1992) ~[spring-data-mongodb-3.0.1.RELEASE.jar:3.0.1.RELEASE]

我已经找到了一种解决方法(见下文),但我仍然很好奇为什么上面显示的聚合失败。我不明白为什么“_id”引用没有正确公开。

成功的解决方法:

Aggregation a = Aggregation.newAggregation(
            group("status", "category").count().as("count"),
            group("_id.category").push(
                    new Document("k", "$_id.status").append("v", "$count"))
                    .as("counts"),
            project().and(arrayToObject("$counts")).as("counts"),
            // the following works
            addFields().addFieldWithValue("name", "$_id").build(),
            project().andExclude("_id").andExclude("counts")
    );

对于在无法确定将谁将 MongoDB 聚合转换为 Spring Data Mongo 聚合时需要解决方法的任何人:https://stackoverflow.com/a/59726492/5873923

【问题讨论】:

    标签: aggregation-framework spring-data-mongodb


    【解决方案1】:

    试试这个:

    project("_id").and(arrayToObject("$counts")).as("counts"),
    project().andExclude("_id").and("_id").as("name")
    

    编辑:

    Spring-Mongo 对您的管道进行了一些验证,并期望 _id 出现在您的前一个阶段,这就是 java.lang.IllegalArgumentException: Invalid reference '_id'! 的原因

    Aggregation agg = Aggregation.newAggregation(
            group("status", "category").count().as("count"),
            group("_id.category").push(new Document("k", "$_id.status").append("v", "$count")).as("counts"),
            project("_id").and(ArrayOperators.arrayOf("$counts").toObject()).as("counts"),
            project().and("_id").as("name").andExclude("_id"));
    
    AggregationResults<Document> results = mongoTemplate.aggregate(agg, "tasks", Document.class);
    results.getMappedResults().forEach(System.out::println);
    

    ---输出---

    Document{{name=green}}
    Document{{name=red}}
    

    【讨论】:

    • 这是我首先测试的。但正如我在帖子中所说,and("_id").as("name") 部分没有提供预期的结果。
    • 当我使用project().andExclude("_id").and("_id").as("name") 时,某些东西在途中崩溃了...我会在睡个好觉后深入研究它;)
    • 您的示例(以及许多其他示例)确实按预期工作,但请查看我的更新以了解失败的棘手示例。
    • @MarcTarin 你如何执行聚合 (mongotemplate.aggregate(what parameters here?))?
    • 我用域对象和对 mongotemplate.aggregate(...) 的调用更新了我的帖子:mongotemplate.aggregate(a, "tasks", org.bson.Document.class)
    猜你喜欢
    • 2018-03-29
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-05
    • 2020-07-01
    相关资源
    最近更新 更多