具有特定条件计数的 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 的实例