Mongodb 外国 collection 条件与查找
Mongodb condition on foreign collection with lookup
我在 MongoDB 4.0 (pymongo)
中有三个 collection
users: [
{_id: "aaa", name: "John", user_type: "writer", department: {value: "1", label:"press"}},
{_id: "bbb", name: "Charles", user_type: "writer", department: {value: "1", label:"press"}},
{_id: "ccc", name: "Jessy", user_type: "admin_writer", department: {value: "1", label:"press"}},
{_id: "ddd", name: "Tim", user_type: "writer", department: {value: "2", label:"news"}},
{_id: "eee", name: "Max", user_type: "admin_writer", department: {value: "2", label:"news"}},
{_id: "fff", name: "Julia", user_type: "admin", department: {value: "2", label:"news"}},
{_id: "ggg", name: "Arnold", user_type: "writer", department: {value: "3", label:"infos"}}
]
departments: [
{_id: 1, name: "press", group: "times"},
{_id: 2, name: "news", group: "times"},
{_id: 3, name: "infos", group: "herald"}
]
docs: [
{_id: 1, name: "monday", user_id: "aaa"},
{_id: 2, name: "tuesday", user_id: "bbb"},
{_id: 3, name: "wednesday", user_id: "ddd"},
{_id: 4, name: "thursday", user_id: "ddd"},
{_id: 5, name: "friday", user_id: "ggg"}
]
在我的示例中,用户可以编写文档并在部门工作。部门由团体(社会)定义。作者只能看到他的文档,admin_writer 可以看到他部门的所有文档,包括他写的文档,管理员可以看到组中的所有文档,即使他在特定部门工作。
当用户登录应用程序时,我需要在仪表板中包含所有文档的列表
例如,结果将是:
John (writer) : [{name: "monday"}]
Jessy (admin_writer) : [{name: "monday"}, {name: "tuesday"}]
Max (admin_writer) : [{name: "wednesday"}, {name: "thursday"}]
Julia (admin) : [{name: "monday"}, {name: "tuesday"},{name: "wednesday"}, {name: "thursday"}]
Arnold (writer) : [{name: "friday"}]
为此,我管理每个特定 user_type 的查询。对于作家,我使用 $match 因为 user_id 直接在文档 collections 中,但对于另外两个 user_type,我需要使用 $lookup 在 foreign collection 中添加条件。我尝试了两种方法(我使用 Pymongo):
user_department = 1
db.docs.aggregate([{
'$lookup': {
'from': "users",
'localField': "user_id",
'foreignField': "_id",
'as': "user"
}
},{
"$addFields": {
"user": {
"$arrayElemAt": [
{
"$filter": {
"input": "$user",
"as": "userId",
"cond": {
"$eq": ["$$userId.department.value", user_department]
}
}
}, 0
]
}
}
}, {'$project: {'name': 1}]);
或
db.docs.aggregate([{
"$lookup": {
"from": "users",
"let": {
'id': "$user_id"
},
"pipeline": [
{
'$match': {
'$expr': {
'$eq': [
"$_id",
"$$id"
]},
"_id": user_department
}
}
],
"as": "user"
}
}]);
在我的两次尝试中,我没有得到预期的结果。当我与 admin_writer (Jessy) 联系时,我还有来自其他部门和小组的“星期五”文档。如果您对发生这种情况的原因或其他解决方案有任何想法,谢谢
您可以将查找的文档分为 3 种情况:
- 用户可以通过himself/herself
查看的文档
- 如果he/she为admin_writer
,用户可以按部门查看的文档
- 如果he/she是管理员,用户可以查看的文档(即所有文档)
最终的可查看文档列表将是上述 3 种情况的并集。
用户可以通过himself/herself
查看的文档
一个琐碎的$lookup
就足够了
{
"$lookup": {
"from": "docs",
"localField": "_id",
"foreignField": "user_id",
"as": "selfDocs"
}
}
如果he/she为admin_writer
,用户可以按部门查看的文档
您首先需要获取同一部门中用户的完整列表。添加 $and
条件和子管道中的查找以检查 user_type 和所有部门用户的文档
{
"$lookup": {
"from": "users",
"localField": "department.value",
"foreignField": "department.value",
"as": "deptUsers"
}
},
{
"$lookup": {
"from": "docs",
"let": {
userType: "$user_type",
deptUsers: "$deptUsers"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$userType",
"admin_writer"
]
},
{
$in: [
"$user_id",
"$$deptUsers._id"
]
}
]
}
}
}
],
"as": "deptDocs"
}
}
如果he/she是管理员,用户可以查看的文档(即所有文档)
$lookup
部门查询用户所属组。然后使用用户组查找组内的所有用户。对 user_type: "admin"
进行条件检查,文档属于同一组中的用户。
{
"$lookup": {
"from": "departments",
let: {
dept: "$department.value"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$dept",
{
$toString: "$_id"
}
]
}
}
}
],
"as": "group"
}
},
{
"$unwind": "$group"
},
{
"$set": {
"group": "$group.group"
}
},
{
"$lookup": {
"from": "users",
"let": {
group: "$group"
},
"pipeline": [
{
"$lookup": {
"from": "departments",
let: {
d: "$department.value"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$d",
{
$toString: "$_id"
}
]
}
}
}
],
"as": "gp"
}
},
{
"$unwind": "$gp"
},
{
$match: {
$expr: {
$eq: [
"$gp.group",
"$$group"
]
}
}
},
{
"$project": {
"_id": 1
}
}
],
"as": "groupUsers"
}
},
{
"$lookup": {
"from": "docs",
"let": {
userType: "$user_type",
groupUsers: "$groupUsers"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$userType",
"admin"
]
},
{
$in: [
"$user_id",
"$$groupUsers._id"
]
}
]
}
}
}
],
"as": "adminDocs"
}
}
使用$setUnion
将它们链接起来
allDocs: {
"$setUnion": [
"$selfDocs",
"$deptDocs",
"$adminDocs"
]
}
这里是Mongo playground供您参考。
我在 MongoDB 4.0 (pymongo)
中有三个 collectionusers: [
{_id: "aaa", name: "John", user_type: "writer", department: {value: "1", label:"press"}},
{_id: "bbb", name: "Charles", user_type: "writer", department: {value: "1", label:"press"}},
{_id: "ccc", name: "Jessy", user_type: "admin_writer", department: {value: "1", label:"press"}},
{_id: "ddd", name: "Tim", user_type: "writer", department: {value: "2", label:"news"}},
{_id: "eee", name: "Max", user_type: "admin_writer", department: {value: "2", label:"news"}},
{_id: "fff", name: "Julia", user_type: "admin", department: {value: "2", label:"news"}},
{_id: "ggg", name: "Arnold", user_type: "writer", department: {value: "3", label:"infos"}}
]
departments: [
{_id: 1, name: "press", group: "times"},
{_id: 2, name: "news", group: "times"},
{_id: 3, name: "infos", group: "herald"}
]
docs: [
{_id: 1, name: "monday", user_id: "aaa"},
{_id: 2, name: "tuesday", user_id: "bbb"},
{_id: 3, name: "wednesday", user_id: "ddd"},
{_id: 4, name: "thursday", user_id: "ddd"},
{_id: 5, name: "friday", user_id: "ggg"}
]
在我的示例中,用户可以编写文档并在部门工作。部门由团体(社会)定义。作者只能看到他的文档,admin_writer 可以看到他部门的所有文档,包括他写的文档,管理员可以看到组中的所有文档,即使他在特定部门工作。
当用户登录应用程序时,我需要在仪表板中包含所有文档的列表
例如,结果将是:
John (writer) : [{name: "monday"}]
Jessy (admin_writer) : [{name: "monday"}, {name: "tuesday"}]
Max (admin_writer) : [{name: "wednesday"}, {name: "thursday"}]
Julia (admin) : [{name: "monday"}, {name: "tuesday"},{name: "wednesday"}, {name: "thursday"}]
Arnold (writer) : [{name: "friday"}]
为此,我管理每个特定 user_type 的查询。对于作家,我使用 $match 因为 user_id 直接在文档 collections 中,但对于另外两个 user_type,我需要使用 $lookup 在 foreign collection 中添加条件。我尝试了两种方法(我使用 Pymongo):
user_department = 1
db.docs.aggregate([{
'$lookup': {
'from': "users",
'localField': "user_id",
'foreignField': "_id",
'as': "user"
}
},{
"$addFields": {
"user": {
"$arrayElemAt": [
{
"$filter": {
"input": "$user",
"as": "userId",
"cond": {
"$eq": ["$$userId.department.value", user_department]
}
}
}, 0
]
}
}
}, {'$project: {'name': 1}]);
或
db.docs.aggregate([{
"$lookup": {
"from": "users",
"let": {
'id': "$user_id"
},
"pipeline": [
{
'$match': {
'$expr': {
'$eq': [
"$_id",
"$$id"
]},
"_id": user_department
}
}
],
"as": "user"
}
}]);
在我的两次尝试中,我没有得到预期的结果。当我与 admin_writer (Jessy) 联系时,我还有来自其他部门和小组的“星期五”文档。如果您对发生这种情况的原因或其他解决方案有任何想法,谢谢
您可以将查找的文档分为 3 种情况:
- 用户可以通过himself/herself 查看的文档
- 如果he/she为admin_writer ,用户可以按部门查看的文档
- 如果he/she是管理员,用户可以查看的文档(即所有文档) 最终的可查看文档列表将是上述 3 种情况的并集。
用户可以通过himself/herself
查看的文档一个琐碎的$lookup
就足够了
{
"$lookup": {
"from": "docs",
"localField": "_id",
"foreignField": "user_id",
"as": "selfDocs"
}
}
如果he/she为admin_writer
,用户可以按部门查看的文档您首先需要获取同一部门中用户的完整列表。添加 $and
条件和子管道中的查找以检查 user_type 和所有部门用户的文档
{
"$lookup": {
"from": "users",
"localField": "department.value",
"foreignField": "department.value",
"as": "deptUsers"
}
},
{
"$lookup": {
"from": "docs",
"let": {
userType: "$user_type",
deptUsers: "$deptUsers"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$userType",
"admin_writer"
]
},
{
$in: [
"$user_id",
"$$deptUsers._id"
]
}
]
}
}
}
],
"as": "deptDocs"
}
}
如果he/she是管理员,用户可以查看的文档(即所有文档)
$lookup
部门查询用户所属组。然后使用用户组查找组内的所有用户。对 user_type: "admin"
进行条件检查,文档属于同一组中的用户。
{
"$lookup": {
"from": "departments",
let: {
dept: "$department.value"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$dept",
{
$toString: "$_id"
}
]
}
}
}
],
"as": "group"
}
},
{
"$unwind": "$group"
},
{
"$set": {
"group": "$group.group"
}
},
{
"$lookup": {
"from": "users",
"let": {
group: "$group"
},
"pipeline": [
{
"$lookup": {
"from": "departments",
let: {
d: "$department.value"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$d",
{
$toString: "$_id"
}
]
}
}
}
],
"as": "gp"
}
},
{
"$unwind": "$gp"
},
{
$match: {
$expr: {
$eq: [
"$gp.group",
"$$group"
]
}
}
},
{
"$project": {
"_id": 1
}
}
],
"as": "groupUsers"
}
},
{
"$lookup": {
"from": "docs",
"let": {
userType: "$user_type",
groupUsers: "$groupUsers"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$userType",
"admin"
]
},
{
$in: [
"$user_id",
"$$groupUsers._id"
]
}
]
}
}
}
],
"as": "adminDocs"
}
}
使用$setUnion
将它们链接起来
allDocs: {
"$setUnion": [
"$selfDocs",
"$deptDocs",
"$adminDocs"
]
}
这里是Mongo playground供您参考。