MongoDB同一集合多查询关联实现特定数据筛选方案问询
MongoDB 4.2 聚合筛选问题
需求说明
现有集合数据(MongoDB 4.2):
[ {"a":1,"b":1,"c":1,"d":1}, {"a":2,"b":1,"c":1,"d":8}, {"a":3,"b":1,"c":0,"d":1}, {"a":4,"b":2,"c":1,"d":1}, {"a":5,"b":2,"c":0,"d":1}, {"a":6,"b":3,"c":0,"d":2}, {"a":7,"b":4,"c":1,"d":1}, {"a":8,"b":5,"c":0,"d":4}, {"a":9,"b":6,"c":1,"d":1} ]
需要筛选出满足以下条件的文档:
c=1且d=1- 该文档对应的
b值在集合中,所有c=1的记录仅存在一条
期望结果:
[ {"a":4,"b":2,"c":1,"d":1}, {"a":7,"b":4,"c":1,"d":1}, {"a":9,"b":6,"c":1,"d":1} ]
现有思路与困惑
计划分三步实现:
- 找出原集合中
d=1且c=1的所有记录; - 找出原集合中存在多条
c=1记录的b值; - 从第一步结果中移除包含第二步
b值的记录。
在SQL中可以通过子查询或关联查询实现,但在MongoDB中使用$lookup时遇到困难,不清楚如何模拟SQL的关联操作,尤其是关联的是聚合结果而非物理集合,核心问题:
- 如何得到期望结果?
- 能否模拟SQL的实现方式?
- 能否基于同一集合的聚合结果进行关联或筛选?
解决方案
方法一:模拟SQL关联查询($lookup管道模式)
利用MongoDB 3.6+支持的$lookup管道模式,直接在关联阶段对同一集合做聚合统计,完美对应SQL的子查询关联逻辑:
db.your_collection.aggregate([ // 第一步:筛选c=1且d=1的记录 { $match: { c: 1, d: 1 } }, // 第二步:关联同一集合,统计当前b对应的c=1记录数 { $lookup: { from: "your_collection", // 替换为你的集合名称 let: { current_b: "$b" }, pipeline: [ { $match: { $expr: { $eq: ["$b", "$$current_b"] } } }, { $match: { c: 1 } }, { $count: "c_count" } ], as: "b_stat" } }, // 展开统计结果(每个b对应唯一统计值) { $unwind: "$b_stat" }, // 第三步:筛选出c_count=1的记录 { $match: { "b_stat.c_count": 1 } }, // 移除统计字段,恢复原文档结构 { $project: { b_stat: 0 } } ])
方法二:先统计符合条件的b值,再筛选目标记录
先通过聚合缩小b值范围,再筛选目标文档,数据量大时更高效:
db.your_collection.aggregate([ // 第一阶段:按b分组,统计每组c=1的记录数 { $group: { _id: "$b", c_count: { $sum: { $cond: [{ $eq: ["$c", 1] }, 1, 0] } } } }, // 筛选出c_count=1的b值 { $match: { c_count: 1 } }, // 将符合条件的b值收集为数组 { $group: { _id: null, valid_bs: { $push: "$_id" } } }, // 关联原集合,筛选c=1且d=1且b在有效列表中的记录 { $lookup: { from: "your_collection", let: { valid_bs: "$valid_bs" }, pipeline: [ { $match: { c: 1, d: 1 } }, { $match: { $expr: { $in: ["$b", "$$valid_bs"] } } } ], as: "result" } }, // 展开结果数组并恢复原文档结构 { $unwind: "$result" }, { $replaceRoot: { newRoot: "$result" } } ])
方法三:单分组聚合+条件过滤(最简洁)
通过一次分组同时完成统计和目标文档收集,逻辑更紧凑:
db.your_collection.aggregate([ { $group: { _id: "$b", // 统计该b组内c=1的总记录数 c_total: { $sum: { $cond: [{ $eq: ["$c", 1] }, 1, 0] } }, // 收集该b组内符合c=1且d=1的文档(不符合的存为null) target_docs: { $push: { $cond: [{ $and: [{ $eq: ["$c", 1] }, { $eq: ["$d", 1] }] }, "$$ROOT", null] } } } }, // 筛选c_total=1的分组 { $match: { c_total: 1 } }, // 过滤掉target_docs中的null值 { $set: { target_docs: { $filter: { input: "$target_docs", cond: { $ne: ["$$this", null] } } } } }, // 展开目标文档并恢复原结构 { $unwind: "$target_docs" }, { $replaceRoot: { newRoot: "$target_docs" } } ])
关键说明
$lookup的管道模式支持在关联时执行任意聚合操作,无需提前创建视图或物理集合,完全可以模拟SQL的子查询关联;- 三种方法均兼容MongoDB 4.2版本,可根据数据量和业务场景选择:方法二更适合大数据量场景,方法三逻辑最简洁;
内容的提问来源于stack exchange,提问作者migs
相关产品推荐
相关产品推荐

