MongoDB查询:如何仅返回嵌套文档中符合条件的子文档?
我有一个包含运动员ID、赛季以及运动员参与的各类赛事和成绩列表的数据集,结构如下:
[ { 'id':'123', 'season':'2019', 'event_type1':[ {'Date':'20 Oct 2019', 'Location':'somewhere1','result':'182'}, {'Date':'20 Oct 2019', 'Location':'somewhere2','result':'184'} ], 'event_type2':[ {'Date':'20 Oct 2019', 'Location':'somewhere1','result':'182'}, {'Date':'20 Oct 2019', 'Location':'somewhere2','result':'184'} ] } ]
目前我能通过以下查询语句,找出所有event_type1中至少存在一个Location为somewhere1的文档:
query = { '$and': [ {'season': 2019}, {'event_type1':{ '$elemMatch' : {'Location':'somewhere1'}}} ] } db.col.find(query)
但我希望仅返回event_type1中Location为somewhere1的子文档,而不是包含符合条件子文档的整个父文档。目前只能通过遍历返回的字典来处理,未找到更优方法。
补充信息:我的场景中event_type名称包含空格,且不同运动员的event_type不尽相同,尝试$unwind方法未成功。
可以直接检索出目标子文档,根据你的场景(动态event_type名称、带空格),通过MongoDB聚合管道实现,分两种情况说明:
1. 针对已知名称的event_type(含带空格的名称)
如果明确要查询的event_type名称,用$project配合$filter提取数组中符合条件的元素,再通过$unwind展开、$replaceRoot将子文档设为根返回:
db.col.aggregate([ // 筛选赛季符合条件且目标event_type存在匹配子文档的父文档 { $match: { 'season': '2019', 'event_type1.Location': 'somewhere1' } }, // 过滤event_type1数组,仅保留Location为somewhere1的元素 { $project: { matched_events: { $filter: { input: '$event_type1', cond: { $eq: ['$$this.Location', 'somewhere1'] } } } } }, // 展开匹配到的子文档数组,每个元素成为单独文档 { $unwind: '$matched_events' }, // 将子文档设为根文档,直接返回目标内容 { $replaceRoot: { newRoot: '$matched_events' } } ])
如果event_type名称带空格(如Track and Field),只需用引号包裹字段名:'Track and Field.Location',$filter的input改为'$Track and Field'即可。
2. 针对未知/动态的event_type名称
如果不同运动员的event_type各不相同,无法提前指定字段名,用$objectToArray将文档转为键值对数组,统一遍历处理所有event_type字段:
db.col.aggregate([ // 筛选赛季符合条件的文档 { $match: { 'season': '2019' } }, // 将文档转为键值对数组,方便遍历所有字段 { $project: { events: { $objectToArray: '$$ROOT' } } }, // 展开所有键值对 { $unwind: '$events' }, // 筛选出值为数组的字段(即各类event_type),且数组中存在目标子文档 { $match: { 'events.v': { $type: 'array' }, 'events.v.Location': 'somewhere1' } }, // 过滤每个event_type数组中的目标子文档 { $project: { matched_events: { $filter: { input: '$events.v', cond: { $eq: ['$$this.Location', 'somewhere1'] } } } } }, // 展开匹配到的子文档数组 { $unwind: '$matched_events' }, // 返回子文档作为根文档 { $replaceRoot: { newRoot: '$matched_events' } } ])
这个方法会自动遍历文档中所有数组类型的字段,找出所有包含Location: 'somewhere1'的子文档并返回。
内容的提问来源于stack exchange,提问作者TheArmbreaker

