【问题标题】:Mongodb condition on foreign collection with lookup带有查找的外部集合的MongoDB条件
【发布时间】:2021-12-11 15:42:47
【问题描述】:

我在 MongoDB 4.0 (pymongo) 中有三个集合

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 直接在 docs 集合中,但对于其他两个 user_type,我需要使用 $lookup 在外部集合中添加条件。我尝试了两种方法(我使用 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) 联系时,我也有来自其他部门和小组的“星期五”文档。如果您知道它发生的原因或其他解决方案,谢谢

【问题讨论】:

    标签: mongodb mongodb-query pymongo


    【解决方案1】:

    您可以将查找的文档分为 3 种情况:

    1. 用户可以自己查看的文档
    2. 如果他/她是 admin_writer 用户可以按部门查看的文档
    3. 如果他/她是管理员,用户可以查看的文档(即所有文档) 可查看文档的最终列表将是上述 3 种情况的结合。

    用户可以自己查看的文档

    一个简单的$lookup 就足够了

    {
        "$lookup": {
          "from": "docs",
          "localField": "_id",
          "foreignField": "user_id",
          "as": "selfDocs"
        }
    }
    

    如果他/她是 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"
        }
    }
    

    如果用户是管理员则可以查看的文档(即所有文档)

    $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 供您参考。

    【讨论】:

    • 感谢您的回答,就我而言,Julia 是一名管理员,但仅限于她的小组(次)。她看不到“星期五”,因为它是由在“先驱”小组工作的阿诺德写的)
    • @yassinekhelifa 我已经更新了为adminDocs查找属于同一组用户的文档的解决方案
    • 非常感谢@ray
    猜你喜欢
    • 2018-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-11
    • 2017-02-22
    • 2021-06-20
    • 2018-12-28
    相关资源
    最近更新 更多