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

基于MongoDB/SpringBoot实现登录用户与其他用户的距离排序聚合查询

解决方案与优化指南

一、按距离排序的MongoDB聚合查询可行性分析与实现

可行性确认

$geoNear是MongoDB官方原生支持的地理空间聚合阶段,完全匹配你的需求:

  • 依赖2dsphere索引是硬性要求,只要提前为经纬度字段创建该索引,就能启用地理距离计算能力
  • 可通过配置distanceField参数生成虚拟距离字段,聚合结果会自动包含该字段
  • 支持前置过滤:结合query参数可先筛选目标用户,再执行距离计算与排序

具体实现步骤

  1. 创建2dsphere索引
    假设用户实体的经纬度字段为location(类型为org.springframework.data.geo.Point),通过Java代码创建索引:
// 使用MongoTemplate创建索引
mongoTemplate.indexOps(User.class)
    .ensureIndex(new GeospatialIndex("location").typed(GeospatialIndexType.GEO_2DSPHERE));
  1. 聚合查询代码实现
    以下是按登录用户坐标排序其他用户的完整聚合示例:
// 登录用户的经纬度坐标
Point currentUserLocation = new Point(116.397, 39.908);
String currentUserId = "user_123";

// 构建聚合流水线
Aggregation aggregation = Aggregation.newAggregation(
    // 地理距离计算阶段,生成虚拟distance字段
    Aggregation.geoNear(
        NearQuery.near(currentUserLocation)
            .maxDistance(10000) // 可选:设置最大筛选距离(单位:米)
            .spherical(true),
        "distance"
    ),
    // 可选:排除当前用户
    Aggregation.match(Criteria.where("userId").ne(currentUserId)),
    // 可选:覆盖默认排序($geoNear默认按距离升序)
    Aggregation.sort(Sort.by(Sort.Direction.ASC, "distance")),
    // 分页阶段(后续结合可选分页逻辑)
    Aggregation.skip(pageable.getOffset()),
    Aggregation.limit(pageable.getPageSize())
);

// 执行聚合,映射到包含distance字段的DTO
AggregationResults<UserDistanceDTO> results = mongoTemplate.aggregate(
    aggregation,
    User.class,
    UserDistanceDTO.class
);
List<UserDistanceDTO> sortedUsers = results.getMappedResults();

注:UserDistanceDTO需包含原User实体的所有字段,并新增Double distance属性用于接收虚拟距离值。

二、代码优化方案

1. 整合重复API(getUsers、getFavs等)

抽象通用查询服务,封装过滤、排序、分页的核心逻辑,避免重复造轮子:

// 通用查询接口
public interface GenericQueryService<T> {
    List<T> query(Criteria criteria, Sort sort, Pageable pageable);
}

// 通用实现类
@Service
public class GenericQueryServiceImpl<T> implements GenericQueryService<T> {
    private final MongoTemplate mongoTemplate;
    private final Class<T> entityClass;

    public GenericQueryServiceImpl(MongoTemplate mongoTemplate, Class<T> entityClass) {
        this.mongoTemplate = mongoTemplate;
        this.entityClass = entityClass;
    }

    @Override
    public List<T> query(Criteria criteria, Sort sort, Pageable pageable) {
        Query query = new Query(criteria);
        if (sort != null) query.with(sort);
        if (pageable != null && pageable.isPaged()) query.with(pageable);
        return mongoTemplate.find(query, entityClass);
    }
}

// 业务服务复用示例
@Service
public class UserService {
    private final GenericQueryService<User> userQueryService;

    public UserService(MongoTemplate mongoTemplate) {
        this.userQueryService = new GenericQueryServiceImpl<>(mongoTemplate, User.class);
    }

    public List<User> getUsers(Criteria criteria, Sort sort, Pageable pageable) {
        return userQueryService.query(criteria, sort, pageable);
    }
}

@Service
public class FavService {
    private final GenericQueryService<Fav> favQueryService;

    public FavService(MongoTemplate mongoTemplate) {
        this.favQueryService = new GenericQueryServiceImpl<>(mongoTemplate, Fav.class);
    }

    public List<Fav> getFavs(Criteria criteria, Sort sort, Pageable pageable) {
        return favQueryService.query(criteria, sort, pageable);
    }
}

2. 实现可选分页

通过判断分页参数的状态,支持全量/分页数据切换:

// 修改通用查询服务的分页逻辑
@Override
public List<T> query(Criteria criteria, Sort sort, Pageable pageable) {
    Query query = new Query(criteria);
    if (sort != null) query.with(sort);
    // 当pageable为null,或pageSize=0时返回全量数据
    if (pageable != null && pageable.getPageSize() > 0) {
        query.with(pageable);
    }
    return mongoTemplate.find(query, entityClass);
}

// 调用示例
// 获取全量数据
List<User> allUsers = userService.getUsers(criteria, sort, null);
// 或显式指定全量
List<User> allUsers = userService.getUsers(criteria, sort, PageRequest.of(0, 0));

// 获取分页数据
List<User> pageUsers = userService.getUsers(criteria, sort, PageRequest.of(0, 10));

3. 支持多条件AND/OR组合过滤

利用MongoDB的Criteria类实现动态条件组合:

// AND组合条件
Criteria andCriteria = new Criteria()
    .andOperator(
        Criteria.where("lastLoginAt").gte(LocalDateTime.now().minusDays(7)),
        Criteria.where("status").is("ACTIVE")
    );

// OR组合条件
Criteria orCriteria = new Criteria()
    .orOperator(
        Criteria.where("createdAt").gte(LocalDateTime.now().minusMonths(1)),
        Criteria.where("role").is("ADMIN")
    );

// AND嵌套OR的复杂条件
Criteria complexCriteria = new Criteria()
    .andOperator(
        Criteria.where("status").is("ACTIVE"),
        new Criteria().orOperator(
            Criteria.where("lastLoginAt").gte(LocalDateTime.now().minusDays(7)),
            Criteria.where("role").is("ADMIN")
        )
    );

// 执行查询
List<User> users = userService.getUsers(complexCriteria, Sort.by("createdAt"), pageable);

内容的提问来源于stack exchange,提问作者The Old County

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:17:45