如何在MongoDB的find查询$expr中通过$reduce获取唯一计数?
MongoDB查询:统计数组中去重后val为true的id数量≥2的文档
数据结构
集合中文档包含values对象数组,每个元素有id和val字段,示例数据如下:
[ { "values": [ { "id": "123", "val": true }, { "id": "456", "val": true }, { "id": "789", "val": false } ] }, { "values": [ { "id": "123", "val": true }, { "id": "123", "val": true }, { "id": "123", "val": false } ] }, { "values": [ { "id": "234", "val": false }, { "id": "567", "val": false } ] } ]
原查询及结果
此前已实现查询val为true且数组中该实例数量≥2的文档,查询语句如下:
db.collection.find({ values: { "$elemMatch": { val: true } }, $expr: { $gte: [ { $reduce: { input: "$values", initialValue: 0, in: { $sum: [ "$$value", { $cond: [ { $eq: ["$$this.val", true] }, 1, 0 ] } ] } } }, 2 ] } })
该查询返回结果:
[ { "values": [ { "id": "123", "val": true }, { "id": "456", "val": true }, { "id": "789", "val": false } ] }, { "values": [ { "id": "123", "val": true }, { "id": "123", "val": true }, { "id": "123", "val": false } ] } ]
需求更新
现在需要调整统计逻辑:仅计算不同id中val为true的数量,要求该数量≥2,最终只保留第一个符合条件的文档(即上述结果中的第一个文档)。已知$group可用于去重,但不确定如何整合到现有查询中,且实际场景需保留$elemMatch作为最优过滤方案。
解决方案
我们可以在$expr中先对values数组里val为true的元素按id去重,再统计唯一id的数量,判断是否≥2。以下提供两种实现方式:
方式一:$filter + $map + $addToSet + $size
先过滤出val为true的元素,提取对应的id后去重,再统计去重后的数量:
db.collection.find({ values: { "$elemMatch": { val: true } }, $expr: { $gte: [ { $size: { $addToSet: { $map: { input: { $filter: { input: "$values", cond: { $eq: ["$$this.val", true] } } }, as: "item", in: "$$item.id" } } } }, 2 ] } })
方式二:$reduce维护唯一id集合
用$reduce遍历数组,只收集val为true且未统计过的id,最后统计集合长度:
db.collection.find({ values: { "$elemMatch": { val: true } }, $expr: { $gte: [ { $size: { $reduce: { input: "$values", initialValue: [], in: { $cond: [ { $and: [{ $eq: ["$$this.val", true] }, { $not: { $in: ["$$this.id", "$$value"] } }] }, { $concatArrays: ["$$value", ["$$this.id"]] }, "$$value" ] } } } }, 2 ] } })
两种方式均保留了$elemMatch的前置过滤(用于快速排除无val: true元素的文档,提升查询性能),最终只会返回第一个符合去重后数量≥2条件的文档。
内容的提问来源于stack exchange,提问作者saddiqs1
相关产品推荐
相关产品推荐

