java - 在 Spring Data MongoDB Aggregation 中对数组进行分组后,计数操作返回 1 而不是 0

标签 java mongodb aggregation-framework spring-data-mongodb

我需要使用具有类似模型的 Spring Data MongoDB 创建高级聚合:

@Getter
@Setter
@Document
public class Library {

  @Id
  @JsonSerialize(using = ToStringSerializer.class)
  private ObjectId id;

  private Address address;

  private String workingHours;

  ...

}

@Getter
@Setter
@Document
public class Book {

  @Id
  @JsonSerialize(using = ToStringSerializer.class)
  private ObjectId id;

  private Boolean published;

  private Boolean hidden;

  private String title;

  @JsonSerialize(using = ToStringSerializer.class)
  private ObjectId libraryId;

  ...

}

pom.xml

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-mongodb</artifactId>
        <version>2.2.0</version>
</dependency>

图书馆 Collection :

{ 
    "_id" : ObjectId("5f45440ee89590218e83a697"), 
    "workingHours" : "8:00 PM - 8:00 AM",
    "address" : DBRef("addresses", ObjectId("5f4544198da452a5523e3d11"))
}

书籍 Collection :

{ 
    "_id" : ObjectId("5f454423be823729015661ed"), 
    "published": true,
    "hidden": false,
    "title": "The Hobbit, or There and Back Again"
    "libraryId": ObjectId("5f45440ee89590218e83a697")
},
{ 
    "_id" : ObjectId("5f45445b876d08649b88ed5a"), 
    "published": true,
    "hidden": false,
    "title": "Harry Potter and the Philosopher's Stone"
    "libraryId": ObjectId("5f45440ee89590218e83a697")
},
{ 
    "_id" : ObjectId("5f45446c7e33ca70363f629a"), 
    "published": true,
    "hidden": false,
    "title": "Harry Potter and the Cursed Child"
    "libraryId": ObjectId("5f45440ee89590218e83a697")
},
{ 
    "_id" : ObjectId("5f45447285f9b3e4cb8739ad"), 
    "published": true,
    "hidden": false,
    "title": "Fantastic Beasts and Where to Find Them"
    "libraryId": ObjectId("5f45440ee89590218e83a697")
},
{ 
    "_id" : ObjectId("5f45449fc121a20afa4fbb96"), 
    "published": false,
    "hidden": false,
    "title": "Universal Parks & Resorts"
    "libraryId": ObjectId("5f45440ee89590218e83a697")
},
{ 
    "_id" : ObjectId("5f4544a5f13839bbe89edb23"), 
    "published": false,
    "hidden": true,
    "title": "Ministry of Dawn"
    "libraryId": ObjectId("5f45440ee89590218e83a697")
}

根据用户的上下文,我必须返回不同数量的书籍,这些书籍可以根据 startsWith()like() 原则进行过滤。

假设我有 4 本书,其中一本是普通用户添加的,一本是隐藏的。

  1. 管理员应该了解所有这些,因此他会将 booksCount 视为 6
  2. 普通用户只能看到自己发布或添加的内容,因此他会看到 booksCount5
  3. 将来可能还会出现其他一些情况。

我想出了这样的聚合:

Criteria criteria = Criteria.where("_id").ne(null).and("address.city").is("Chicago");

MatchOperation matchOperation = Aggregation.match(criteria);
            
LookupOperation lookupOperation = LookupOperation.newLookup().from("books").localField("_id").foreignField("topicId").as("books");

UnwindOperation unwindOperation = Aggregation.unwind("books", true);

MatchOperation secondMatchOperation = Aggregation.match(Criteria.where("books.published").is(Boolean.TRUE).orOperator(Criteria.where("creator.userId").is(context.getUserId()));

AggregationOperation group = Aggregation.group("_id")
            .first("_id").as("id")
            .first("published").as("published")
            .first("title").as("title")
            .push("books").as("books").count().as("booksCount");

Aggregation aggregation = !isAdministrator() ?
Aggregation.newAggregation(matchOperation, lookupOperation, unwindOperation, secondMatchOperation, group) : 
Aggregation.newAggregation(matchOperation, lookupOperation, unwindOperation, group);
            
mongoTemplate.aggregate(aggregation, "libraries", Document.class).getRawResults().get("results");

一切正常,而不是 count() 操作。

  1. 如果 books 数组大小为 0,则始终返回 1。
  2. 如果 books 数组大小大于 0,则返回正确的数量。
  3. 我尝试使用 newBuilder(GroupOps.SUM, null, 0) 而不是 count(),但现在它始终返回 0。
  4. 如果我使用newBuilder(GroupOps.SUM, null, 2),它会返回size + 2。我不知道发生了什么。

我的问题:

  1. 谁能告诉我我做错了什么以及如何纠正?
  2. 此外,我需要将“booksCount”从Integer解析为String。小组赛可以吗?

提前谢谢您。

最佳答案

发生这种情况是因为Aggregation.unwind("books", true);。当没有连接时,此语句将保留为文档,除非您将其设置为 Aggregation.unwind("books")。默认行为是false。那么当你计数时,该文档就被算作一个文档。这就是为什么它为您提供 1 作为输出。 Example with wrong output

所以你能做的是,你可以在下一阶段计算大小。

 project("_id", "published", "title", "books")
   .and(ArrayOperators.Size.lengthOfArray(ConditionalOperators.ifNull("books").then(Collections.emptyList()))).as("booksCount")

工作中Mongo playground with correct answer

关于java - 在 Spring Data MongoDB Aggregation 中对数组进行分组后,计数操作返回 1 而不是 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63597076/

相关文章:

node.js - 错误 : "Double colon in host identifer"

mongodb - 更改 Controller 中的数据库

javascript - 无法使用backbone.js从mongodb数据库中删除记录

node.js - 每天或每月间隔的交易总和

mongodb - 蒙戈错误: The $cond accumulator is a unary operator

java - log4j 不使用 websphere 登录 AIX

java - 使用 Java 访问方法和参数历史记录

javascript - 使用 MongoDB 将多个数组聚合成一个大数组

java - 从 Java 远程登录

java - 使用 Math.pow 方法