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

Java分页查询MongoDB能否仅调用一次数据库?

能否通过单次MongoDB调用同时获取分页数据与总文档数?

当pageSize设为10、pageNumber设为0时,若MongoDB中符合查询条件的文档共有50条,我们需要同时返回两个结果:

  • total_documents:总文档数50
  • msList:前10条文档的NSDTO详情

目前的实现通过两次MongoDB调用完成,代码如下:

Query query = new Query();
List<Criteria> criteria = new ArrayList<>();
criteria.add(Criteria.where("di.ai").in(new ArrayList<>(Arrays.asList("123"))));
query.addCriteria(new Criteria().andOperator(criteria.toArray(new Criteria[criteria.size()])));
Long total_documents = this.mongoTemplate.count(query, NSDTO.class, MONGODB_MESSAGE_COLLECTION);
Pageable pageable = PageRequest.of((pageNumber - 1), pageSize);
query.with(pageable);    
List<NSDTO> nsList= this.mongoTemplate.find(query, NSDTO.class, MONGODB_MESSAGE_COLLECTION);

可以通过单次MongoDB调用实现需求,利用MongoDB的聚合框架中的$facet阶段,在一个聚合请求中同时计算总文档数和分页数据。

实现代码示例

// 构建查询条件
Criteria criteria = Criteria.where("di.ai").in(Arrays.asList("123"));

// 定义聚合管道
Aggregation aggregation = Aggregation.newAggregation(
    // 匹配查询条件
    Aggregation.match(criteria),
    // 分面查询:同时执行两个子管道
    Aggregation.facet(
        // 子管道1:计算总文档数
        Aggregation.newAggregation(
            Aggregation.count().as("total")
        ).withOutput("total_documents"),
        // 子管道2:分页获取数据(pageNumber从0开始时,直接用pageNumber*pageSize计算偏移量)
        Aggregation.newAggregation(
            Aggregation.skip(pageNumber * pageSize),
            Aggregation.limit(pageSize),
            // 映射字段,确保和NSDTO的属性对应(可根据实际DTO结构调整)
            Aggregation.project()
                .and("field1").as("field1")
                .and("di.ai").as("di.ai")
                // 补充其他需要映射的字段
        ).withOutput("msList")
    )
);

// 执行聚合查询
AggregationResults<Map<String, Object>> results = mongoTemplate.aggregate(
    aggregation,
    MONGODB_MESSAGE_COLLECTION,
    Map.class
);

// 解析返回结果
Map<String, Object> resultMap = results.getUniqueMappedResult();
List<Map<String, Object>> totalResult = (List<Map<String, Object>>) resultMap.get("total_documents");
Long total_documents = ((Number) totalResult.get(0).get("total")).longValue();

// 将文档列表转换为NSDTO对象列表
List<NSDTO> msList = mongoTemplate.getConverter().convertToList(
    NSDTO.class,
    (List<Document>) resultMap.get("msList")
);

关键说明

  1. $facet阶段允许在一个聚合请求中定义多个独立子管道,分别完成总计数、分页查询的逻辑,最终将所有子管道的结果合并到同一返回文档中,彻底避免两次MongoDB调用。
  2. 原代码中PageRequest.of((pageNumber - 1), pageSize)存在逻辑问题:当pageNumber为0时,会生成skip(-10)的非法指令。如果分页页码从0开始,直接用pageNumber * pageSize计算偏移量即可;若页码从1开始,才需要用(pageNumber - 1)*pageSize。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 03:42:38