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

ReactiveMongo计数查询耗时10-12秒,改用aggregateWith报错如何解决?

错误原因分析
  1. 反序列化类型不匹配:MongoDB聚合的$count阶段返回的是结构化文档,格式为{"count": 计数值},你的代码直接指定aggregateWith[Long],框架尝试将整个JSON文档直接转换为Long类型,触发JSON验证失败的报错。
  2. 功能阶段缺失:原count方法支持skip、limit参数,你的聚合实现没有添加对应的$skip、$limit阶段,实际运行逻辑和原需求不一致。
  3. 配置未对齐:原实现指定了ReadConcern.Majority读关注,你写的聚合查询没有配置对应参数,会默认使用集合的全局读关注,一致性级别可能和原逻辑不符。
正确聚合计数实现

首先定义一个简单的结果类用于接收聚合返回的结构化数据:

import play.api.libs.json.Json

private case class CountResult(count: Long)
private object CountResult {
  implicit val format = Json.format[CountResult]
}

然后修改count方法的实现,补全所有阶段和配置:

def count(criteria: Option[JsObject], skip: Int, limit: Option[Int]): Future[Long] =
  (for {
    col <- collection
    // 构建聚合阶段列表
    stages = List(
      Some(framework.Match(criteria.getOrElse(JsObject.empty))),
      // 仅当skip>0时添加skip阶段
      if (skip > 0) Some(framework.Skip(skip)) else None,
      // 仅当limit有值且>0时添加limit阶段
      limit.flatMap(l => if (l > 0) Some(framework.Limit(l)) else None),
      Some(framework.Count("count"))
    ).flatten
    result <- col.aggregateWith[CountResult](readConcern = ReadConcern.Majority) { framework =>
      (stages.head, stages.tail)
    }.head
  } yield result.count) recoverWith {
    case error =>
      logger.error(
        s"failed to count by [${criteria.getOrElse(JsObject.empty)}]" +
        s" with error: [${error.getMessage}]"
      , error)
      Future.failed(MongoExceptionBuilder.buildError(error))
  }
额外性能优化建议
  • 索引是计数查询性能优化的核心,确保Match阶段用到的所有查询字段都已经创建合适的索引,无索引的情况下不管用原生count还是聚合查询都会触发全表扫描,性能无法提升。
  • 如果是无过滤条件的全集合计数,且你的集合没有硬删除操作,可以直接读取集合元数据的计数结果,性能远高于遍历计数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 02:24:02