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

Spring中MongoDB查询如何按日期字段的日和月排序(忽略年份)

问题:MongoDB按出生日期的月日排序(忽略无效年份)

需要对MongoDB查询结果按出生日期(dob)字段排序,但该字段存储的年份为无效值(不再要求用户提供出生年份),期望排序效果如下:

1. 2004-03-05        7. 2005-01-28
2. 2001-03-03        2. 2001-03-03
3. 2003-12-08        1. 2004-03-05
4. 2015-10-08   ->   6. 1999-07-16
5. 1999-09-24        5. 1999-09-24
6. 1999-07-16        4. 2015-10-08
7. 2005-01-28        3. 2003-12-08

当前实现是先按sortingField(此处为dob)排序,再按_id保证顺序稳定,代码如下:

List<Sort.Order> sortList = new ArrayList<>();

sortList.add(
    new Sort.Order(
        sortDirection.compareToIgnoreCase("d") == 0
                ? Sort.Direction.DESC
                : Sort.Direction.ASC,
        sortingField));

sortList.add(new Sort.Order(Sort.Direction.ASC, "_id"));

query.with(Sort.by(sortList));

List<Customer> customers = mongoTemplate.find(query, Customer.class);

请问是否可以实现按日和月排序的需求?


解决方案

可以实现,以下是两种常用方案,可根据数据量和性能要求选择:

方式一:数据库端聚合排序(推荐,性能更优)

利用MongoDB聚合管道,先从dob字段中提取月份和日期,再基于这两个字段排序,最后返回原文档结构。这种方式在数据库端完成排序,适合大数据量场景。

代码示例:

// 确定排序方向
Sort.Direction dateSortDir = sortDirection.compareToIgnoreCase("d") == 0 
    ? Sort.Direction.DESC 
    : Sort.Direction.ASC;

// 构建聚合管道
Aggregation aggregation = Aggregation.newAggregation(
    // 投影提取月份、日期及原文档字段
    Aggregation.project()
        .andInclude("_id", "dob" /* 其他需要保留的字段 */)
        .andExpression("$month", "$dob").as("month")
        .andExpression("$dayOfMonth", "$dob").as("day"),
    // 按月份、日期排序,最后按_id保证顺序稳定
    Aggregation.sort(
        dateSortDir, "month", "day",
        Sort.Direction.ASC, "_id"
    ),
    // 移除临时生成的month、day字段,恢复原文档结构
    Aggregation.project().andExclude("month", "day")
);

// 执行聚合查询
List<Customer> customers = mongoTemplate.aggregate(
    aggregation, "customer", Customer.class
).getMappedResults();

方式二:内存中排序(适合小数据量)

如果数据量较小,可以先查询所有文档,再在Java内存中通过自定义比较器,仅针对dob的月和日进行排序。

代码示例:

// 先查询所有数据(无需数据库端排序)
Query query = new Query();
List<Customer> customers = mongoTemplate.find(query, Customer.class);

// 确定最终排序方向
Sort.Direction finalDateSortDir = sortDirection.compareToIgnoreCase("d") == 0 
    ? Sort.Direction.DESC 
    : Sort.Direction.ASC;

// 自定义排序规则:先比月份,再比日期,最后比_id
Collections.sort(customers, (c1, c2) -> {
    LocalDate dob1 = c1.getDob();
    LocalDate dob2 = c2.getDob();
    
    // 月份比较
    int monthCompare = Integer.compare(dob1.getMonthValue(), dob2.getMonthValue());
    if (monthCompare != 0) {
        return finalDateSortDir == Sort.Direction.ASC ? monthCompare : -monthCompare;
    }
    // 日期比较
    int dayCompare = Integer.compare(dob1.getDayOfMonth(), dob2.getDayOfMonth());
    if (dayCompare != 0) {
        return finalDateSortDir == Sort.Direction.ASC ? dayCompare : -dayCompare;
    }
    // 最后按_id保证排序稳定
    return c1.getId().compareTo(c2.getId());
});

内容的提问来源于stack exchange,提问作者LaplacesCat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:43:24