【问题标题】:MongoDB multiple Lookup into same collectionMongoDB多次查找到同一个集合
【发布时间】:2021-02-04 06:50:17
【问题描述】:

我有两个集合 BillEmployee。账单包含有关每月学生账单的信息,员工包含在学校工作的所有类型的人(会计、教师、维护等)。
Bill 有 billVerifyByclassteacher 字段,它们指向员工的记录。
收单

{
    "_id": ObjectId("ab12dns..."),  //mongoid
    "studentname": "demoUser",
    "class": { "section": "A"},
    "billVerifiedBy": "121212",
    "classteacher": "134239",

}

员工收藏

{
    "_id": ObjectId("121212"), // random number
    "name": "Darn Morphy",
    "department": "Accounts",
    "email": "dantest@test.com",
}

{
    "_id": ObjectId("134239"),
    "name": "Derreck",
    "department": "Faculty",
    "email": "derrect145@test.com",
}

我需要检索与特定账单相关的帐户和教师信息。我正在使用 Mongodb 查找来获取信息。但是,我必须查找同一个表两次,因为 billVerifiedBy 和 classteacher 属于下面给出的同一个 Employee 表。

db.bill.aggregate([  
    {
        $lookup: {"from": "employee", "localField": "billVerifiedBy", "foreignField": "_id", "as": "accounts"}},
    },
    {
        $lookup: {"from": "employee", "localField": "classteacher", "foreignField": "_id", "as": "faculty"}},
    },
    {
        $project: {
            "studentname": 1, 
            "class": 1,
            "verifiedUser": "$accounts.name",
            "verifiedByEmail":"$accounts.email",
            "facultyName": "$faculty.name",
            "facultyEmail": "$faculty.email"
        }
    }
]

我不知道这是否是在单个 Employee 集合中安排 Accounts 和 Faculty 信息的好方法。使用相同的集合查找两次是否正确。或者我应该创建单独的帐户和教师集合并使用它进行查找。请就性能方面提出最佳方法。

【问题讨论】:

  • 您好,我认为查询没问题。如果没有必要,不建议加入 MongoDB 本身,但我认为在你的情况下这是有道理的。
  • 感谢您的回复。我真的不认为使用 NoSQL DB 会阻止 100% 的加入。总是需要某种程度的标准化。我只是好奇是否有其他方法可以防止多次加入同一张表。

标签: sql mongodb join mongodb-query


【解决方案1】:

在 mongodb 中,当您想要连接来自同一个集合的多个文档时,您可以使用 "$lookup" 及其 "pipeline" 和 "let" 选项。它过滤您想要使用已定义变量的文档。

db.getCollection('Bill').aggregate([{
        "$lookup": {
            "as": "lookupUsers",
            "from": "Employee",
            // define variables that you need to use in pipeline to filter documents
            "let": {
                "verifier": "$billVerifiedBy",
                "teacher": "$classteacher"
            },
            "pipeline": [{ // filter employees who you need to filter.
                    "$match": {
                        "$expr": {
                            "$or": [{
                                    "$eq": ["$_id", "$$verifier"]
                                },
                                {
                                    "$eq": ["$_id", "$$teacher"]
                                }
                            ]
                        }
                    }
                },

                { // combine filtered 2 documents in an employee array
                    "$group": {
                        "_id": "",
                        "employee": {
                            "$addToSet": {
                                "_id": "$_id",
                                "name": "$name",
                                "department": "$department",
                                "email": "$email"
                            }
                        }
                    }
                },
                { // takes item from the array by predefined variable.
                    "$project": {
                        "_id": 0,
                        "billVerifiedBy": {
                            "$slice": [{
                                    "$filter": {
                                        "input": "$employee",
                                        "cond": {
                                            "$eq": ["$$this._id", "$$verifier"]
                                        }
                                    }
                                },
                                1
                            ]
                        },
                        "classteacher": {
                            "$slice": [{
                                    "$filter": {
                                        "input": "$employee",
                                        "cond": {
                                            "$eq": ["$$this._id", "$$teacher"]
                                        }
                                    }
                                },
                                1
                            ]
                        }
                    }
                },
                {
                    "$unwind": "$billVerifiedBy"
                },
                {
                    "$unwind": "$classteacher"
                },
            ]
        }
    },
    {
        "$unwind": "$lookupUsers"
    },
]);

输出是这样的:

{
    "_id": ObjectId("602916dcf4450742cdebe38d"),
    "studentname": "demoUser",
    "class": {
        "section": "A"
    },
    "billVerifiedBy": ObjectId("6029172e9ea6c9d4776517ce"),
    "classteacher": ObjectId("6029172e9ea6c9d4776517cf"),
    "lookupUsers": {
        "billVerifiedBy": {
            "_id": ObjectId("6029172e9ea6c9d4776517ce"),
            "name": "Darn Morphy",
            "department": "Accounts",
            "email": "dantest@test.com"
        },
        "classteacher": {
            "_id": ObjectId("6029172e9ea6c9d4776517cf"),
            "name": "Derreck",
            "department": "Faculty",
            "email": "derrect145@test.com"
        }
    }
}

【讨论】:

  • 我可以通过如上所示的两次查找得到你的结果。这种方法比两次查找的好处是什么。因为您的查询通过额外的分组看起来更复杂,在管道内进行过滤。如果在性能或其他方面有好处,我也一定会尝试。
猜你喜欢
  • 2019-01-06
  • 2011-09-24
  • 2020-07-17
  • 2019-03-17
  • 2017-06-14
  • 2016-06-07
  • 2018-10-14
  • 2020-02-16
相关资源
最近更新 更多