ReactiveMongo计数查询耗时10-12秒,改用aggregateWith报错如何解决?
错误原因分析
- 反序列化类型不匹配:MongoDB聚合的
$count阶段返回的是结构化文档,格式为{"count": 计数值},你的代码直接指定aggregateWith[Long],框架尝试将整个JSON文档直接转换为Long类型,触发JSON验证失败的报错。 - 功能阶段缺失:原
count方法支持skip、limit参数,你的聚合实现没有添加对应的$skip、$limit阶段,实际运行逻辑和原需求不一致。 - 配置未对齐:原实现指定了
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
相关产品推荐
相关产品推荐

