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

Spring Mongo聚合按carId分组取min值并保留所有字段的方案

需求与问题

需要获取每个carId对应车辆的最快速度(即raceResulttime最小)的完整记录。当前使用Spring Mongo聚合框架按carId分组并取raceResulttime最小值后,返回结果仅保留carId和raceResulttime,其他字段均为null。尝试过的方案要么将字段存入数组,要么无法保证carId唯一。

集合数据

Race{raceId='64c2350402708c36b821e5bc', raceNum=430, trackNum='2', carUser='null', carId='ZZ9992', raceStart=null, raceEnd=null, raceResulttime=2.262371, raceDatetime=Thu Jul 27 17:12:36 HKT 2023, venue='HK', rank='2', isDnf=false, raceSpeed=0.0}, 
Race{raceId='64c2355f02708c36b821e5bf', raceNum=430, trackNum='2', carUser='null', carId='ZZ9992', raceStart=null, raceEnd=null, raceResulttime=2.262371, raceDatetime=Thu Jul 27 17:14:07 HKT 2023, venue='HK', rank='2', isDnf=false, raceSpeed=0.0}, 
Race{raceId='64c2357002708c36b821e5c2', raceNum=431, trackNum='2', carUser='null', carId='ZZ9992', raceStart=null, raceEnd=null, raceResulttime=2.262371, raceDatetime=Thu Jul 27 17:14:24 HKT 2023, venue='HK', rank='2', isDnf=false, raceSpeed=0.0}, 
Race{raceId='64c2357e02708c36b821e5c5', raceNum=432, trackNum='2', carUser='null', carId='ZZ9992', raceStart=null, raceEnd=null, raceResulttime=2.262371, raceDatetime=Thu Jul 27 17:14:38 HKT 2023, venue='HK', rank='2', isDnf=false, raceSpeed=0.0}, 
Race{raceId='64c2358d02708c36b821e5c8', raceNum=433, trackNum='2', carUser='null', carId='ZZ9992', raceStart=null, raceEnd=null, raceResulttime=2.262371, raceDatetime=Thu Jul 27 17:14:53 HKT 2023, venue='HK', rank='2', isDnf=false, raceSpeed=0.0}, 
Race{raceId='64c23282bc05ab7f8acd7c2a', raceNum=421, trackNum='1', carUser='null', carId='ZZ9991', raceStart=null, raceEnd=null, raceResulttime=4.298986, raceDatetime=Thu Jul 27 17:01:54 HKT 2023, venue='HK', rank='3', isDnf=false, raceSpeed=0.8806175}, 
Race{raceId='64c23294bc05ab7f8acd7c2c', raceNum=422, trackNum='1', carUser='null', carId='ZZ9991', raceStart=null, raceEnd=null, raceResulttime=4.298986, raceDatetime=Thu Jul 27 17:02:12 HKT 2023, venue='HK', rank='3', isDnf=false, raceSpeed=0.8806175}

现有聚合代码

@Override
public void groupByField() {
    // Match stage
    MatchOperation matchStage = Aggregation.match(new Criteria("raceResulttime").gt(0)
            .and("isDnf").is(false)
            .and("raceNum").gt(0)
    );
    GroupOperation groupStage = group("carId")
            .min("raceResulttime").as("raceResulttime");
    SortOperation sortStage = Aggregation.sort(Sort.Direction.ASC, "raceResulttime");
    ProjectionOperation projectionOperation = Aggregation.project("raceResulttime","rank","raceSpeed","raceNum").and("carId").previousOperation();
    // Aggregation
    Aggregation aggregation = newAggregation(matchStage, groupStage, sortStage,projectionOperation);
    AggregationResults<Race> resultss = mongoTemplate.aggregate(aggregation, "race", Race.class);
    for ( Race result : resultss)
        System.out.println("4 - resultss - "+ result);
    System.out.println("5" + resultss.getMappedResults());
}

当前返回结果

- resultss - Race{raceId='null', raceNum=null, trackNum='null', carUser='null', carId='ZZ9992', raceStart=null, raceEnd=null, raceResulttime=1.243837, raceDatetime=null, venue='null', rank='null', isDnf=false, raceSpeed=null}
4 - resultss - Race{raceId='null', raceNum=null, trackNum='null', carUser='null', carId='ZZ9991', raceStart=null, raceEnd=null, raceResulttime=1.391792, raceDatetime=null, venue='null', rank='null', isDnf=false, raceSpeed=null}
4 - resultss - Race{raceId='null', raceNum=null, trackNum='null', carUser='null', carId='ZZ9990', raceStart=null, raceEnd=null, raceResulttime=1.483507, raceDatetime=null, venue='null', rank='null', isDnf=false, raceSpeed=null}
4 - resultss - Race{raceId='null', raceNum=null, trackNum='null', carUser='null', carId='ZZ9995', raceStart=null, raceEnd=null, raceResulttime=1.493411, raceDatetime=null, venue='null', rank='null', isDnf=false, raceSpeed=null}
4 - resultss - Race{raceId='null', raceNum=null, trackNum='null', carUser='null', carId='ZZ9996', raceStart=null, raceEnd=null, raceResulttime=1.661812, raceDatetime=null, venue='null', rank='null', isDnf=false, raceSpeed=null}
解决方案

核心思路:先按carId和raceResulttime升序排序,确保每组内最快的记录排在首位;再按carId分组,取每组第一条完整记录,即可保留该车辆最快速度对应的所有字段。

修改后的聚合代码

@Override
public void groupByField() {
    // 过滤有效记录
    MatchOperation matchStage = Aggregation.match(new Criteria("raceResulttime").gt(0)
            .and("isDnf").is(false)
            .and("raceNum").gt(0)
    );
    
    // 先按carId分组排序,再按raceResulttime升序(最快记录排前面)
    SortOperation sortStage = Aggregation.sort(Sort.Direction.ASC, "carId", "raceResulttime");
    
    // 按carId分组,取每组第一条完整记录
    GroupOperation groupStage = Aggregation.group("carId")
            .first(Aggregation.ROOT).as("fastestRace");
    
    // 提取完整的Race对象,排除自动生成的_id
    ProjectionOperation projectionStage = Aggregation.project("fastestRace")
            .andExclude("_id");
    
    // 构建聚合管道
    Aggregation aggregation = Aggregation.newAggregation(matchStage, sortStage, groupStage, projectionStage);
    
    // 执行聚合,先获取Document结果
    AggregationResults<Document> results = mongoTemplate.aggregate(aggregation, "race", Document.class);
    
    // 将Document转换为Race对象
    List<Race> fastestRaces = results.getMappedResults().stream()
            .map(doc -> mongoTemplate.getConverter().read(Race.class, doc.get("fastestRace", Document.class)))
            .collect(Collectors.toList());
    
    // 输出结果
    for (Race race : fastestRaces) {
        System.out.println("最快记录:" + race);
    }
}

效果说明

修改后,每个carId仅返回一条记录,包含该车辆最快速度对应的所有字段(raceId、raceNum、trackNum等),且保证carId唯一,完全符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:22:06