MongoDB使用$in过滤指定role的items元素异常问题求助
MongoDB嵌套数组按角色筛选items元素问题解决
你当前的问题是使用db.menu.find({ "items": { "$elemMatch": { "roles": {$in: ["ROL_ARTICULOS"] } } } })查询时,会返回整个文档的所有items元素,无法只保留roles包含指定角色的items。比如你想筛选含ROL_CONTADOR的items,但返回结果里包含了Ventas这个不符合条件的元素。
原查询的问题
原查询中的$elemMatch只是用来匹配整个文档是否存在符合条件的items元素,只要文档里有一个items元素满足条件,整个文档就会被返回,而且不会过滤掉items数组里不符合条件的元素。
解决方案:使用聚合管道过滤嵌套数组
要实现只保留符合角色条件的items元素,需要使用MongoDB的聚合管道,通过$filter操作符来过滤嵌套数组。以下是针对单角色和多角色场景的实现:
场景1:筛选单个角色(比如ROL_CONTADOR)
db.menu.aggregate([ // 第一步:先筛选出至少有一个items元素符合角色条件的文档,缩小处理范围 { $match: { "items.roles": { $in: ["ROL_CONTADOR"] } } }, // 第二步:过滤items数组,只保留roles包含目标角色的元素 { $addFields: { items: { $filter: { input: "$items", as: "item", cond: { $in: ["ROL_CONTADOR", "$$item.roles"] } } } } }, // 可选步骤:过滤掉过滤后items为空的文档(如果存在此类情况) { $match: { items: { $ne: [] } } } ])
场景2:筛选多个角色(比如用户拥有ROL_CONTADOR和ROL_VENTAS)
如果用户有多个角色,需要判断items的roles数组和用户角色数组是否存在交集,此时可以结合$map和$anyElementTrue实现:
const userRoles = ["ROL_CONTADOR", "ROL_VENTAS"]; db.menu.aggregate([ { $match: { "items.roles": { $in: userRoles } } }, { $addFields: { items: { $filter: { input: "$items", as: "item", cond: { $anyElementTrue: { $map: { input: userRoles, as: "role", in: { $in: ["$$role", "$$item.roles"] } } } } } } } }, { $match: { items: { $ne: [] } } } ])
代码说明
$match阶段:先筛选出包含符合条件items的文档,避免对所有文档进行数组过滤,提升性能。$addFields阶段:用$filter遍历items数组,根据cond条件保留符合要求的元素。多角色场景下,$map会遍历用户的每个角色,判断是否存在于当前item的roles中,$anyElementTrue只要有一个角色匹配就保留该item。- 最后一个
$match:移除过滤后items为空的文档(如果不需要可以省略)。
内容的提问来源于stack exchange,提问作者Jonathan Medina Cerón
相关产品推荐
相关产品推荐

