Spring Boot Mongo DB 查找聚合操作

Posted

技术标签:

【中文标题】Spring Boot Mongo DB 查找聚合操作【英文标题】:Spring Boot Mongo DB Lookup AggregationOperation 【发布时间】:2021-10-02 11:39:07 【问题描述】:

首先我想说的是我刚接触 MongoDB,尤其是 MongoData with Springs。我有以下收藏

类别

@Data
@Document(collection = "categories")
public class Category 
    @Id
    private ObjectId id;
    @CreatedBy
    private ObjectId creatorId;
    @CreatedDate
    private LocalDateTime createdAt;
    @LastModifiedDate
    private LocalDateTime modifiedAt;
    @LastModifiedBy
    private ObjectId modifiedBy;
    private String title;
    private String notes;
    private boolean enabled = true;

活动

@Data
@Document(collection = "activities")
public class Activity 

    @Id
    private ObjectId id;
    @CreatedBy
    private ObjectId creatorId;
    @CreatedDate
    private LocalDateTime createdAt;
    @LastModifiedDate
    private LocalDateTime modifiedAt;
    @LastModifiedBy
    private ObjectId modifiedBy;

    private ObjectId budgetId;
    private ObjectId categoryId;
    private String title;
    private String notes;
    private Double estimatedBudget;
    private boolean enabled;
    private boolean recurring;


我想出了一个简单的查询,它将两个集合连接起来,这符合我的预期,如下所示(......也可能不是超级酷的查询)。这个想法是我想根据 categoryId 将所有活动分组在 Category 内。然后,我只想检索具有给定budgetId 的活动。 我应该能够检索所有类别,无论其中是否存在活动,例如 SQL 中的 LEFT JOIN

db.categories.aggregate([


$lookup: 
        from: "activities",
        localField : "_id",
        foreignField: "categoryId",
        pipeline: [
              $match:  $and:["budgetId": ObjectId("60f8ef1ed7056b730cdce1db") ] 
        ],
        as: "activities"
    ,
,
 
        $match: 
            $and:["enabled": true]
            
        

])

这以某种方式起作用。 那么问题是如何将上述查询转换为 Springs 中的 AggregationOperation?

我有以下内容,但它排除了其中没有活动的类别。

  List<AggregationOperation> operations = new ArrayList<>();


        Criteria criteria = Criteria.where("enabled").is(true);

        operations.add(match(criteria));


        Criteria forBudgetActivitiesCriteria =  Criteria.where("activities.budgetId").is(budgetId);


        operations.add(lookup("activities", "_id", "categoryId", "activities"));
        operations.add(match(forBudgetActivitiesCriteria));

        TypedAggregation<Category> aggregation = newAggregation(Category.class, operations);
        AggregationResults<Category> results = mongoTemplate.aggregate(aggregation, Category.class);

我猜问题出在这里

Criteria.where("activities.budgetId").is(budgetId);

帮助。并提前致谢。

【问题讨论】:

mongoplayground.net/p/wx7V-uCqFnA 是否达到了您的预期输出?如果是这样,我可以帮你在 Spring boot 中翻译 拥有$match 阶段之前 $lookup 将是构建查询的适当(有效且正确)方式(假设该字段来自categories集合)。 @prasad_ 当我在 $lookup 之前使用 $match 时,我最终得到一个空结果 @varman 是的,谢谢。我只删除了 $eq: [ "$enabled", true ] 因为我只想在类别上运行匹配,没有活动。如果您能帮忙翻译,我将不胜感激 【参考方案1】:

聚合是

db.categories.aggregate([
  
    $lookup: 
      from: "activities",
      let:  "cId": "$_id" ,
      pipeline: [
        
          $match: 
            $expr: 
              $and: [
                 $eq: [ "$budgetId", "abc" ] ,
                 $eq: [ "$$cId", "$categoryId" ] ,
                 $eq: [ "$enabled", true ] 
              ]
            
          
        
      ],
      as: "join_activities"
    ,
    
  
])

工作Mongo playground

mongodb中有两个$lookups。一种是spring数据支持的标准查找(LookupOperation类),第二种是相关子查询查找。不幸的是弹簧数据不支持它。但是有一个Trick to convert

我们可以把它写成 Bson 对象

@Autowired
private MongoTemplate mongoTemplate;

public List<YOUR_CONVERTER_CLASS> test(String value) 

    Aggregation aggregation = Aggregation.newAggregation(
        l-> new Document("$lookup",
                new Document("from","activities")
                .append("let",new Document("cId","$_id"))
                .append("pipeline",
                    Arrays.asList(
                        new Document("$match",
                            new Document("$expr",
                                new Document(
                                    "$and",
                                    Arrays.asList(
                                        new Document("$eq",Arrays.asList("$budgetId","abc"))
                                        .append("$eq",Arrays.asList("$$cId","$categoryId"))
                                        .append("$eq",Arrays.asList("$enabled",true))
                                    )
                                )
                            )
                        )
                    )
                ).append("as","join_activities")
        )
    ).withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());

    return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(YOUR_COLLECTION_CLASS.class), YOUR_CONVERTER_CLASS.class).getMappedResults();


注意:这未经测试,而是基于工作的 mongo 游乐场编写的。 Mongodb 5.0 与您在帖子中发布的签名相同。如果你需要转换你的查询,你可以使用技巧和转换,它只是一个键值对

【讨论】:

谢谢@varman,我不知道键值对转换。它帮助我轻松解决了大部分问题 如果有帮助,请查看meta.stackexchange.com/questions/5234/…

以上是关于Spring Boot Mongo DB 查找聚合操作的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Boot 应用程序中将 mongo db 升级到 3.2

如何在 Spring Boot 中将 mongo db update 运算符应用为 $inc

从 MongoDB 聚合到 Spring Boot 聚合框架

MongoDB + SpringBoot 的基础CRUD聚合查询

连接到多个 mongo db 主机并在 spring boot 中使用不同的数据库进行身份验证

Spring Boot + docker +mongo