You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 10:25:24