Mongo 具有特定条件计数的数据库聚合和按输出预测的日期范围过滤未按预期工作

Mongo DB Aggregation with Count for Specific Conditions and filter by Date Range which Outputs a Projection Doesn't work as Expected

请注意,我是 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();

请找到我在下面使用此代码提到的投影 Dto、预期和实际结果。

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
    }
}
]);

实际结果:

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

但是,如果我 运行 一次使用一组相同的代码,它就可以完美地工作:

  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}
      }]
     }
    
  2. 通过从代码 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}
      }];
     }
    

我认为问题出在多重分组上。如果有人能帮助找到解决这个问题的方法,那就太好了。

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

问题正是我所怀疑的,这是尝试添加两个(多个)按不同列分组的问题。因此,我主要通过使用 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}
    }];
}