Spring Boot中用MongoTemplate聚合查询指定日期上月最晚createdAt记录
用MongoTemplate聚合获取指定日期上个月最晚的createdAt值
需求说明
给定指定日期(例如2022-11-22),需筛选出集合中上个月(示例中为2022-10-01至2022-10-31)内的所有记录,最终取出其中createdAt字段的最大值。
实现方案
通过MongoTemplate构建聚合管道,核心包含三个关键阶段:
- 匹配阶段($match):过滤出
createdAt落在目标时间范围内的记录 - 聚合/排序阶段:要么通过
$group直接求最大值,要么按createdAt降序排序后取第一条记录 - 结果映射:将聚合结果转换为可直接使用的日期对象
代码实现
方式一:通过$group聚合求最大值
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.aggregation.GroupOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.query.Criteria; import java.time.LocalDate; import java.time.ZoneId; import java.util.Date; public Date getLastMonthLatestCreatedAt(MongoTemplate mongoTemplate, LocalDate specifiedDate) { // 计算上个月的起始和结束日期 LocalDate lastMonthStart = specifiedDate.minusMonths(1).withDayOfMonth(1); LocalDate lastMonthEnd = specifiedDate.minusMonths(1).withDayOfMonth(specifiedDate.minusMonths(1).lengthOfMonth()); // 转换为MongoDB兼容的Date类型 Date startDate = Date.from(lastMonthStart.atStartOfDay(ZoneId.systemDefault()).toInstant()); Date endDate = Date.from(lastMonthEnd.atTime(23,59,59).atZone(ZoneId.systemDefault()).toInstant()); // 构建匹配条件:筛选出createdAt在目标区间内的记录 MatchOperation match = Aggregation.match(Criteria.where("createdAt").gte(startDate).lte(endDate)); // 分组计算最大的createdAt GroupOperation group = Aggregation.group().max("createdAt").as("latestCreatedAt"); // 组装聚合管道并执行 Aggregation aggregation = Aggregation.newAggregation(match, group); AggregationResults<LatestDateResult> results = mongoTemplate.aggregate(aggregation, "your-collection-name", LatestDateResult.class); // 返回结果,无匹配记录时返回null return results.getUniqueMappedResult() != null ? results.getUniqueMappedResult().getLatestCreatedAt() : null; } // 用于接收聚合结果的DTO class LatestDateResult { private Date latestCreatedAt; // Getter & Setter public Date getLatestCreatedAt() { return latestCreatedAt; } public void setLatestCreatedAt(Date latestCreatedAt) { this.latestCreatedAt = latestCreatedAt; } }
方式二:排序后取第一条记录
如果需要同时获取该记录的其他字段,可采用此方案:
import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.aggregation.LimitOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.aggregation.SortOperation; import org.springframework.data.mongodb.core.query.Criteria; import java.time.LocalDate; import java.time.ZoneId; import java.util.Date; public Date getLastMonthLatestCreatedAt(MongoTemplate mongoTemplate, LocalDate specifiedDate, Class<YourEntity> entityClass) { // 计算日期范围,同方式一 LocalDate lastMonthStart = specifiedDate.minusMonths(1).withDayOfMonth(1); LocalDate lastMonthEnd = specifiedDate.minusMonths(1).withDayOfMonth(specifiedDate.minusMonths(1).lengthOfMonth()); Date startDate = Date.from(lastMonthStart.atStartOfDay(ZoneId.systemDefault()).toInstant()); Date endDate = Date.from(lastMonthEnd.atTime(23,59,59).atZone(ZoneId.systemDefault()).toInstant()); MatchOperation match = Aggregation.match(Criteria.where("createdAt").gte(startDate).lte(endDate)); // 按createdAt降序排序,让最晚的记录排在最前 SortOperation sort = Aggregation.sort(Sort.by(Sort.Direction.DESC, "createdAt")); // 只取第一条记录 LimitOperation limit = Aggregation.limit(1); Aggregation aggregation = Aggregation.newAggregation(match, sort, limit); AggregationResults<YourEntity> results = mongoTemplate.aggregate(aggregation, "your-collection-name", entityClass); return results.getUniqueMappedResult() != null ? results.getUniqueMappedResult().getCreatedAt() : null; } // 替换为你自己的实体类 class YourEntity { private Date createdAt; // Getter & Setter public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } }
关键细节说明
- 日期计算:使用Java 8的
LocalDate工具类精准计算月份范围,避免手动处理不同月份天数的问题 - 性能优化:
$match阶段优先过滤数据,减少后续聚合/排序处理的数据量 - 结果兼容:两种方案都处理了无匹配记录的情况,返回
null避免空指针异常
内容的提问来源于stack exchange,提问作者Moussa ELaQ
相关产品推荐
相关产品推荐

