MongoDB对象部分匹配查询:如何基于权限对象筛选目标文档
MongoDB 基于权限对象部分匹配的查询实现
这是个很常见的权限子集匹配场景,我来帮你一步步实现符合需求的查询逻辑。
需求拆解
你要找的是文档权限完全是给定查询权限子集的文档:
- 文档的
permissions中所有字段,都必须存在于查询对象的permissions里(不能有查询对象没有的权限类型); - 每个存在的权限字段(比如
records)对应的数组,所有元素都必须是查询对象对应字段数组的元素(比如文档的records数组是查询records数组的子集)。
核心实现思路
MongoDB的$setIsSubset操作符是实现这个需求的关键,它可以判断一个数组是否是另一个数组的子集。结合$objectToArray和$map,我们还可以动态检查权限字段的范围。
1. 静态查询(已知权限字段)
如果你的权限字段是固定的(比如只有records和folders),可以直接写静态查询语句:
db.yourCollectionName.find({ $and: [ // 确保文档的权限字段都是查询对象包含的类型 { $expr: { $setIsSubset: [ { $map: { input: { $objectToArray: "$permissions" }, as: "item", in: "$$item.k" } }, ["records", "folders"] ] } }, // 检查records权限:要么没有该字段,要么是查询数组的子集 { $or: [ { "permissions.records": { $exists: false } }, { $expr: { $setIsSubset: ["$permissions.records", ["add", "delete"]] } } ] }, // 检查folders权限:要么没有该字段,要么是查询数组的子集 { $or: [ { "permissions.folders": { $exists: false } }, { $expr: { $setIsSubset: ["$permissions.folders", ["delete"]] } } ] } ] })
2. 动态查询(支持任意权限字段)
如果权限字段是动态变化的,你可以用代码动态生成查询条件(以Node.js为例):
// 你的查询权限对象 const queryPermissions = { permissions: { records: ["add", "delete"], folders: ["delete"] } }; // 提取查询权限的字段列表 const permissionKeys = Object.keys(queryPermissions.permissions); // 构建查询条件数组 const queryConditions = [ // 检查文档权限字段是否都是查询字段的子集 { $expr: { $setIsSubset: [ { $map: { input: { $objectToArray: "$permissions" }, as: "item", in: "$$item.k" } }, permissionKeys ] } }, // 为每个查询权限字段生成子集检查条件 ...Object.entries(queryPermissions.permissions).map(([key, allowedValues]) => ({ $or: [ { [`permissions.${key}`]: { $exists: false } }, { $expr: { $setIsSubset: [`$permissions.${key}`, allowedValues] } } ] })) ]; // 执行查询 db.yourCollectionName.find({ $and: queryConditions });
验证你的示例
用上面的查询语句测试你的示例数据:
- 文档
{ permissions: { records: [ "add" ] } }:权限字段是records(属于查询字段),且["add"]是["add","delete"]的子集,会被返回; - 文档
{ permissions: { records: [ "delete" ], folders: [ "delete" ] } }:两个权限字段都在查询范围内,且各自数组都是对应查询数组的子集,会被返回; - 文档
{ permissions: { records: [ "read" ] } }:["read"]不是查询records数组的子集,会被排除; - 文档
{ permissions: { files: [ "read" ] } }:files不在查询字段列表中,会被排除。
内容的提问来源于stack exchange,提问作者Nathan Dunn
相关产品推荐
相关产品推荐

