具有特定条件计数的 Mongodb 聚合并按输出投影的日期范围过滤不能按预期工作

Posted

技术标签:

【中文标题】具有特定条件计数的 Mongodb 聚合并按输出投影的日期范围过滤不能按预期工作【英文标题】:Mongo DB Aggregation with Count for Specific Conditions and filter by Date Range which Outputs a Projection Doesn't work as Expected 【发布时间】:2021-12-15 15:07:21 【问题描述】:

请考虑一下我是 MongoDB 的初学者,我需要以某种复杂的查询格式从 MongoDB 数据库中检索数据。我已经参考了社区中发布的几个问题和答案,但是由于某些条件等的一些复杂的计数操作,我的预期查询非常复杂。但是我能够设法将数据检索到我期待的非常相似的查询结果.但是我仍然无法用自己的方式获得预期的查询结果。如果有人可以帮助找到有关此问题的解决方案,那将是非常可观的。

ProjectionOperation projection = project("createdAt", "status")
            .and(DateOperators.DateToString.dateOf("createdAt").toString("%Y-%m-%d")).as("otpDate")
            .and(ConditionalOperators.when(ComparisonOperators.Eq.valueOf("status").equalToValue("VERIFIED")).then(1).otherwise(0)).as("verifyStatus");

// Group by otpDate created in projection to get total otp count by date-wise
GroupOperation totalCount = group("otpDate").count().as("totalCount");

// Group by verifyStatus  created in projection to get total verified OTPs by status "VERIFIED"
GroupOperation loggedInCount = group( "verifyStatus").sum("verifyStatus").as("loggedIn");

// Filter data for given specific date range
MatchOperation match = match(Criteria.where("createdAt").gte(from).lte(to));

// Sort the result to ascending order by _id 
SortOperation sortOperation = sort(Sort.Direction.ASC, "_id");

final TypedAggregation<Otp> aggregation = newAggregation(
            Otp.class, match, projection, totalCount, loggedInCount, sortOperation);

mongoTemplate.aggregate(aggregation, OtpProjDto.class).getMappedResults();

请使用此代码找到我在下面提到的 Projection Dto、Expected 和 Actual Results。

OtpProjDto.java

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.Serializable;
import java.math.BigInteger;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class OtpProjDto implements Serializable 

    private String createdAt;
    private BigInteger totalCount;
    private BigInteger loggedIn;

预期结果:

db.otp.aggregate([

    $match: 

        "createdAt": 
            $gte: new ISODate("2021-03-10"),
            $lte: new ISODate("2021-03-31")
        
    
,

    $group: 
        _id: 
            $dateToString: 
                format: "%Y-%m-%d",
                date: "$createdAt"
            
        ,
        "totalCount": 
            "$sum": 1
        ,
        "logged_in": 
            "$sum": 
                "$cond": ["$eq": ["$status", "VERIFIED"], 1, 0]
            
        
    
,

    $sort: 
        _id: 1
    

]);

实际结果:

当我用这两个组运行我上面提到的完全相同的代码时,它给了我这样的错误:

但是,如果我一次运行一个分组的相同代码,它工作得非常好:

    通过注释掉代码GroupOperation loggedInCount = group("verifyStatus").sum("verifyStatus").as("loggedIn");中的这一行,结果是:

     
      "aggregate": "__collection__",
      "pipeline": [
         "$match": 
             "createdAt": 
                 "$gte": "$java": 2021 - 03 - 10,
                 "$lte": "$java": 2021 - 03 - 31
              
          
       , 
         "$project": 
             "createdAt": 1,
             "status": 1,
             "otpDate": "$dateToString": "format": "%Y-%m-%d", "date": "$createdAt",
             "verifyStatus": "$cond": "if": "$eq": ["$status", "VERIFIED"], "then": 1, "else": 0
          
      , 
      "$group":
         
             "_id": "$otpDate",
             "totalCount": "$sum": 1
         
      ,
      
         "$sort": "_id": 1
      ]
     
    

    通过注释掉代码GroupOperation totalCount = group("otpDate").count().as("totalCount")中的这一行,结果是:

     
     "aggregate" : "__collection__",
     "pipeline": [
         "$match": 
             "createdAt": 
                 "$gte": "$java": 2021 - 03 - 10,
                 "$lte": "$java": 2021 - 03 - 31
             
          
     , 
         "$project": 
             "createdAt": 1,
             "status": 1,
             "otpDate": "$dateToString": "format": "%Y-%m-%d", "date": "$createdAt",
             "verifyStatus": "$cond": "if": "$eq": ["$status", "VERIFIED"], "then": 1, "else": 0
          
      , 
         "$group": 
             "_id": "$verifyStatus", 
             "loggedIn": "$sum": "$verifyStatus"
      
      , 
         "$sort": "_id": 1
      ];
     
    

我认为问题出在多重分组上。如果有人可以帮助找到解决此问题的方法,我们将非常感激。

【问题讨论】:

【参考方案1】:

通过参考文档和文章进行一些研究并花费一些时间使用上述代码后,我能够找到上述问题的解决方案。

问题正是我所怀疑的,这是尝试添加两个组(多个)按不同列分组的问题。因此,我主要通过使用push() 方法将第二组计数结果与我使用的第一组连接起来解决了这个问题。所以,这解决了我的问题。

ProjectionOperation projection = project("createdAt", "status")
            .and(DateOperators.DateToString.dateOf("createdAt").toString("%Y-%m-%d")).as("otpDate")
            .and(AccumulatorOperators.Sum.sumOf(ConditionalOperators.when(ComparisonOperators.Eq.valueOf("status")
                    .equalToValue("VERIFIED")).then(1).otherwise(0))).as("verifyStatus"); // <--- (1) This Line Changed

// Joined the two group result into one result
GroupOperation totalCount = group("otpDate").count().as("totalCount")
            .push("verifyStatus").as("loggedIn"); // <--- (2) This Line Changed

// Filter-data for given specific-dates-range
MatchOperation match = match(Criteria.where("createdAt").gte(from).lte(to));

// Sort the result to ascending order by _id
SortOperation sortOperation = sort(Sort.Direction.ASC, "_id");

final TypedAggregation<Otp> aggregation = newAggregation(
            Otp.class, match, projection, totalCount, sortOperation);

mongoTemplate.aggregate(aggregation, OtpGenerationDataDto.class).getMappedResults()

当前实际结果:

    
    "aggregate": "__collection__",
    "pipeline": [
        "$match": 
            "createdAt": 
                "$gte": "$java": 2021 - 03 - 10,
                "$lte": "$java": 2021 - 03 - 31
            
        
    , 
        "$project": 
            "createdAt": 1,
            "status": 1,
            "otpDate": "$dateToString": "format": "%Y-%m-%d", "date": "$createdAt",
            "verifyStatus": "$sum": "$cond": "if": "$eq": ["$status", "VERIFIED"], "then": 1, "else": 0
        
    , 
        "$group": 
            "_id": "$otpDate",
            "totalCount": "$sum": 1,
            "loggedIn": "$push": "$verifyStatus"
        
    , 
        "$sort": "_id": 1
    ];

【讨论】:

以上是关于具有特定条件计数的 Mongodb 聚合并按输出投影的日期范围过滤不能按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB聚合使用表达式运算符(函数)分组按条件计数统计案例一则

Mongodb:按元素分组并根据条件显示子文档计数并按日期对文档进行排序

MongoDB(猫鼬)聚合计数集合中特定 ObjectID 的实例

MongoDB $reduce(aggregation) 组与数组中嵌套文档的总和并按组计数

检索与 $group 聚合中的指定条件匹配的计数

计数字段包含具有聚合mongodb的集合中的数据