MongoDB中如何匹配数组内指定menu_id并返回对应数组元素
嘿,我懂你的需求了——你想要从tbl_roles集合里,精准取出menu数组中匹配指定menu_id的那个子文档,而不是返回整个角色文档对吧?你当前的查询只是找到了包含目标menu_id的角色文档,但没有过滤掉数组里的其他元素,这就是为啥得不到你想要的结果。
先回顾下你的样本数据:
[ { "_id": ObjectId("611532b19c6e77ad0fd50c1c"), "status": true, "role_name": "DEVELOPER", "descr": "DEVELOPER", "menu": [ { "permissions": [ ObjectId("60eeabbc9ca3e3a4f03ff5ee"), ObjectId("611125cad17b0c71595298d4"), ObjectId("6113e8d69c6e77ad0fd50c08"), ObjectId("61279df3f544f6c2db48b92d"), ObjectId("612a3f061a1fcead8871e821") ], "_id": ObjectId("612a46c41a1fcead8871e83f"), "menu_id": ObjectId("611fe0b60734048a04812020") }, { "permissions": [ ObjectId("60eeabbc9ca3e3a4f03ff5ee"), ObjectId("611125cad17b0c71595298d4"), ObjectId("6113e8d69c6e77ad0fd50c08"), ObjectId("61279df3f544f6c2db48b92d"), ObjectId("612a3f061a1fcead8871e821") ], "_id": ObjectId("612a46c41a1fcead8871e840"), "menu_id": ObjectId("611fee7e0734048a04812022") }, { "permissions": [ ObjectId("60eeabbc9ca3e3a4f03ff5ee"), ObjectId("611125cad17b0c71595298d4"), ObjectId("6113e8d69c6e77ad0fd50c08"), ObjectId("61279df3f544f6c2db48b92d"), ObjectId("612a3f061a1fcead8871e821") ], "_id": ObjectId("612a46c41a1fcead8871e841"), "menu_id": ObjectId("6128c64f9bd697e83dbe47b1") }, { "permissions": [ ObjectId("60eeabbc9ca3e3a4f03ff5ee"), ObjectId("611125cad17b0c71595298d4"), ObjectId("6113e8d69c6e77ad0fd50c08"), ObjectId("61279df3f544f6c2db48b92d"), ObjectId("612a3f061a1fcead8871e821") ], "_id": ObjectId("612a46c41a1fcead8871e842"), "menu_id": ObjectId("6128c5e19bd697e83dbe47ac") } ], "createdAt": ISODate("2021-08-12T14:39:45.505+0000"), "updatedAt": ISODate("2021-08-28T14:23:00.040+0000"), "__v": NumberInt(10) } ]
接下来给你两种解决方案,按需选择:
方法1:使用find配合$elemMatch投影(简单快速)
这个方法适合只需要从单个文档中提取第一个匹配的子文档的场景:
db.getCollection("tbl_roles").find( { "menu.menu_id": ObjectId("611fe0b60734048a04812020") }, { "menu": { $elemMatch: { "menu_id": ObjectId("611fe0b60734048a04812020") } }, "_id": 0 } )
解释:
- 第一个参数是查询条件,筛选出包含目标
menu_id的角色文档 - 第二个参数是投影规则:用
$elemMatch只保留menu数组中匹配指定menu_id的元素,_id: 0是移除根文档的_id字段
返回结果会是这样的格式(外层带menu字段):
{ "menu": [ { "permissions": [ ObjectId("60eeabbc9ca3e3a4f03ff5ee"), ObjectId("611125cad17b0c71595298d4"), ObjectId("6113e8d69c6e77ad0fd50c08"), ObjectId("61279df3f544f6c2db48b92d"), ObjectId("612a3f061a1fcead8871e821") ], "_id": ObjectId("612a46c41a1fcead8871e83f"), "menu_id": ObjectId("611fe0b60734048a04812020") } ] }
方法2:使用聚合管道(完全匹配你要的结果格式)
如果想直接得到子文档作为根结果,用聚合管道更灵活,适合多场景:
db.getCollection("tbl_roles").aggregate([ // 第一步:筛选出包含目标menu_id的角色文档 { $match: { "menu.menu_id": ObjectId("611fe0b60734048a04812020") } }, // 第二步:将menu数组拆分为单个文档 { $unwind: "$menu" }, // 第三步:精准筛选出匹配目标menu_id的子文档 { $match: { "menu.menu_id": ObjectId("611fe0b60734048a04812020") } }, // 第四步:将子文档作为根文档返回 { $replaceRoot: { newRoot: "$menu" } } ])
解释:
$match:先缩小范围,找到包含目标menu_id的角色文档$unwind:把menu数组拆成独立的文档,每个文档对应一个菜单子项- 再次
$match:精准提取出匹配目标menu_id的那个菜单子文档 $replaceRoot:将这个子文档提升为根文档,完全符合你想要的结果格式
这个查询返回的结果就是你期望的:
{ "permissions" : [ ObjectId("60eeabbc9ca3e3a4f03ff5ee"), ObjectId("611125cad17b0c71595298d4"), ObjectId("6113e8d69c6e77ad0fd50c08"), ObjectId("61279df3f544f6c2db48b92d"), ObjectId("612a3f061a1fcead8871e821") ], "_id" : ObjectId("612a46c41a1fcead8871e83f"), "menu_id" : ObjectId("611fe0b60734048a04812020") }
如果你的集合中存在多个角色文档包含同一个menu_id,聚合管道会返回所有匹配的子文档,而find方法会每个角色文档返回一个匹配的子项(外层带menu字段),根据你的需求选择就行~
内容的提问来源于stack exchange,提问作者Slack_ny
相关产品推荐
相关产品推荐

