如何在Spring Data MongoDB中使用多个聚合正确地计算数组元素?

问题描述

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

@Getter
@Setter
@Document
public class City {

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

  private Address description;

  private String name;

  ...

}

@Getter
@Setter
@Document
public class Library {

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

  private Address address;

  private String workingHours;

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

  ...

}

@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>

城市集合:

{ 
    "_id" : ObjectId("5f47878c95f47e209402fe15"),"name" : "Warsaw","description" : "Sample description"
}
{ 
    "_id" : ObjectId("5f4787918b343fff4f52c270"),"name" : "Chicago","description" : "Sample description"
}

图书馆藏书:

{ 
    "_id" : ObjectId("5f45440ee89590218e83a697"),"workingHours" : "8:00 PM - 8:00 AM","address" : DBRef("addresses",ObjectId("5f4544198da452a5523e3d11")),"cityId": ObjectId("5f47878c95f47e209402fe15")
},{ 
    "_id" : ObjectId("5f478725d1507323a80efa31"),ObjectId("5f4787379e72f882e4d26912")),{ 
    "_id" : ObjectId("5f47872f7c4872d4983961f5"),ObjectId("5f47873d5ddedadb3d6ddd6e")),"cityId": ObjectId("5f4787918b343fff4f52c270")
}

图书收藏:

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

根据用户的上下文,我必须返回城市,该城市的图书馆和书籍数量可以根据startsWith()like()原则进行过滤。

假设我在一个城市有2个图书馆,在另一个城市有1个图书馆。

  1. 我需要先使用查找计数库,然后返回librariesCount-分别为21
  2. 我需要在每个图书馆中获取/查找图书,然后将它们计为'booksCount',然后乘以librariesCount以得出城市中booksCount的总数(我们将其称为{{1 }}。

我想到了这样的聚合:

cityBooksCount

借助其中一位stackoverflow用户的帮助,我得以以适当的方式获得Criteria criteria = Criteria.where("_id"); MatchOperation matchOperation = Aggregation.match(criteria); LookupOperation lookupOperation = LookupOperation.newLookup().from("libraries").localField("_id").foreignField("cityId").as("libraries"); UnwindOperation unwindOperation = Aggregation.unwind("libraries",true); LookupOperation secondLookupOperation = LookupOperation.newLookup(). from("books"). localField("libraryIdArray"). foreignField("libraryId"). as("books"); UnwindOperation secondUnwindOperation = Aggregation.unwind("books",true); AggregationOperation group = Aggregation.group("_id") .first("_id").as("id") .first("name").as("name") .first("description").as("description") .push("libraries").as("libraries") .push("books").as("books"); ProjectionOperation projectionOperation = Aggregation.project("id","description","name") .and(VariableOperators.mapItemsOf(ConditionalOperators.ifNull("libraries").then(Collections.emptyList())) .as("library").andApply(aggregationOperationContext -> { Document document = new Document(); document.append("id","$$library._id"); return document; })).as("libraryIdArray") .and(ConvertOperators.valueOf(ArrayOperators.Size.lengthOfArray(ConditionalOperators.ifNull("libraries").then(Collections.emptyList()))).convertToString()).as("librariesCount") .and(ConvertOperators.valueOf(ArrayOperators.Size.lengthOfArray(ConditionalOperators.ifNull("books").then(Collections.emptyList()))).convertToString()).as("cityBooksCount"); Aggregation aggregation = Aggregation.newAggregation(matchOperation,lookupOperation,unwindOperation,secondLookupOperation,secondUnwindOperation,group,projectionOperation); mongoTemplate.aggregate(aggregation,"cities",Document.class).getRawResults().get("results"); 。不幸的是,librariesCount始终指向cityBooksCount

我对MongoDB不太熟悉,但是我知道0 operation is possible on array,所以我尝试将库数组映射到$lookup的列表,但是它不能正常工作。可能我做错了,但我不知道问题出在哪里。我得到了适当数量的具有其他预计领域的城市。

谁能告诉我我在做什么错以及如何纠正它?

谢谢。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)