MongoDB多层嵌套数组对象字段聚合投影查询优化方案
MongoDB 3.6 聚合提取嵌套数组字段优化方案
问题背景
现有集合文档结构如下:
[ { "name": "Report1", "specifications": [ { "parameters": [ { "name": "feature", "value": ["13"] }, { "name": "security", "value": ["XXXX-695"] }, { "name": "imageURL", "value": ["football.jpg"] } ] } ] }, { "name": "Report2", "specifications": [ { "parameters": [ { "name": "feature", "value": ["67"] }, { "name": "imageURL", "value": ["basketball.jpg"] }, { "name": "security", "value": ["XXXX-123"] } ] } ] } ]
需求为提取parameters.name = "imageURL"对应的specifications[0].parameters.value[0]字段值,期望返回结构:
[ { "imageparam": "football.jpg", "name": "Report1" }, { "imageparam": "basketball.jpg", "name": "Report2" } ]
当前环境为MongoDB 3.6.3 + MongoDB Compass,原实现通过5个连续$project阶段完成需求,聚合代码如下:
db.collection.aggregate([ { $project: { _id: 0, name: 1, specifications: {$arrayElemAt: ["$specifications", 0]} } }, { $project: { name: 1, imageparam: { $filter: { input: '$specifications.parameters', as: 'param', cond: { $eq: ['$$param.name', 'imageURL'] } } } } }, { $project: { name: 1, imageparam: {$arrayElemAt: ["$imageparam",0]} } }, { $project: { name: 1, imageparam: "$imageparam.value" } }, { $project: { name: 1, imageparam: {$arrayElemAt: ["$imageparam",0]} } } ])
需要更高效、简洁的实现方案。
优化实现
原方案连续使用5个$project阶段存在冗余,多阶段投影会带来额外的文档遍历、序列化开销,MongoDB 3.6完全支持将多层数组操作合并到单阶段完成,优化后代码如下:
db.collection.aggregate([ // 可选优化阶段:提前过滤无效文档,可命中索引减少后续计算量 { $match: { "specifications.parameters.name": "imageURL" } }, { $project: { _id: 0, name: 1, imageparam: { $let: { vars: { // 匹配specifications首元素下parameters数组中name为imageURL的参数项 targetParam: { $arrayElemAt: [ { $filter: { input: { $arrayElemAt: ["$specifications.parameters", 0] }, as: "param", cond: { $eq: ["$$param.name", "imageURL"] } } }, 0 ] } }, // 取匹配到的参数value数组首元素作为最终结果 in: { $arrayElemAt: ["$$targetParam.value", 0] } } } } } ])
方案优势
- 执行效率更高:管道阶段从5个压缩到最少1个投影阶段,减少多阶段处理的额外开销;可选的
$match阶段支持前置索引过滤,进一步降低计算数据量 - 代码更简洁:通过
$let定义临时变量承载中间计算结果,逻辑集中,不需要多次投影传递中间字段 - 兼容性完全匹配:所有用到的聚合操作符在MongoDB 3.2版本即已支持,可直接在MongoDB Compass的聚合管道编辑器中配置使用,返回结果与预期完全一致
内容的提问来源于stack exchange,提问作者Halil Oymacı
相关产品推荐
相关产品推荐

