如何用MongoDB聚合管道按合法提示组合分组统计
MongoDB 4.4.22 聚合管道实现合法提示组合统计
现有文档结构
[ { _id: ObjectId("6481b9762f11910c77d977ae"), hints: [ { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint1" }, { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint2" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint3" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint4" } ] }, { _id: ObjectId("6168b9751000006e9d436bd1"), hints: [ { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint1" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint3" } ] } ]
需求说明
需要统计每个合法提示组合的出现记录数,规则如下:
- 合法组合要求同一
creatorId不能重复出现 - 默认每条记录包含所有创作者的提示,无需处理部分组合的情况
- 提示的唯一性由
creatorId+textValue共同决定,textValue相同但creatorId不同的提示算不同提示
问题
能否仅通过MongoDB聚合管道实现该需求?尝试过多种管道写法,但卡在单条记录中同一创作者存在多个提示的情况,无法生成符合要求的组合统计。
期望结果
[ { _id: [ { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint1" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint3" } ], count: 2 }, { _id: [ { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint1" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint4" } ], count: 1 }, { _id: [ { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint2" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint3" } ], count: 1 }, { _id: [ { creatorId: "093c90be-8f19-45de-89ab-20070ef6d3f5", textValue: "hint2" }, { creatorId: "575b9c75-c89d-4505-aec5-b001d8836845", textValue: "hint4" } ], count: 1 } ]
解决方案:聚合管道实现
可以通过聚合管道完成,核心思路是先按creatorId分组整理提示,再生成所有合法组合,最后统计组合出现次数。具体管道如下:
db.collection.aggregate([ // 1. 按creatorId分组,将同一创作者的提示整理到数组中 { $unwind: "$hints" }, { $group: { _id: { docId: "$_id", creatorId: "$hints.creatorId" }, hints: { $push: "$hints" } } }, { $group: { _id: "$_id.docId", creatorHints: { $push: { creatorId: "$_id.creatorId", hints: "$hints" } } } }, // 2. 递归生成所有创作者提示的笛卡尔积(即合法组合) { $addFields: { combinations: { $reduce: { input: "$creatorHints", initialValue: [[]], in: { $concatArrays: { $map: { input: "$$value", as: "existing", in: { $map: { input: "$$this.hints", as: "hint", in: { $concatArrays: ["$$existing", ["$$hint"]] } } } } } } } } } }, // 3. 展开所有组合,为统计做准备 { $unwind: "$combinations" }, // 4. 对组合排序,确保相同内容的组合结构一致(避免因顺序不同被视为不同组合) { $addFields: { sortedCombination: { $sortArray: { input: "$combinations", sortBy: { creatorId: 1, textValue: 1 } } } } }, // 5. 按排序后的组合分组统计出现次数 { $group: { _id: "$sortedCombination", count: { $sum: 1 } } }, // 6. 调整输出字段顺序(可选) { $project: { _id: 1, count: 1 } } ])
步骤解释
- 按创作者整理提示:先拆分
hints数组,再按文档ID+创作者ID分组,把同一创作者在同一条记录里的所有提示整理到单独的数组中,得到每个文档下各创作者的提示集合。 - 生成笛卡尔积组合:用
$reduce和$map嵌套,递归生成所有创作者提示的笛卡尔积,这样每个组合里每个创作者只会出现一次,符合合法组合要求。 - 组合排序去重:对每个组合按
creatorId和textValue排序,确保内容相同但顺序不同的组合被视为同一个,避免统计错误。 - 统计组合次数:按排序后的组合分组,统计每个组合出现的记录数,得到最终结果。
内容的提问来源于stack exchange,提问作者Shaked Vitkon
相关产品推荐
相关产品推荐

