【问题标题】:MongoDB multiple lookup on same collectionMongoDB在同一个集合上多次查找
【发布时间】:2019-01-06 01:27:24
【问题描述】:

我有一个子集合,其中有 3 个不同的字段链接到同一个主集合。如果我将它们合并到第 3 个根元素中,则覆盖第一个 2,这是有道理的,因为所有三个 3 查找都指向同一个集合。如果我不合并这些集合,我需要遍历结果集并在客户端一一填充模型字段,这是我想避免的。

有没有办法,我可以合并所有 3 个查找结果,并且可以以某种方式更改字段名称,所以一个不会覆盖到另一个。

下面是我的代码:-

module.exports.getAssignmentByClient = function(req, res) {
var query = url.parse(req.url,true).query;

Assignment.aggregate([
    {
        $match : 
        { 
            clientId: 4, 
        } , 
    },
    {
       $lookup:
        {
            from: "staffs",
            localField: "assignedMember1",
            foreignField: "staffId",
            as: "member1"
        },            
    },
    {
        $lookup:
         {
             from: "staffs",
             localField: "assignedMember2",
             foreignField: "staffId",
             as: "member2"
         },            
    },
    {
        $lookup:
         {
             from: "staffs",
             localField: "assignedLeader",
             foreignField: "staffId",
             as: "leader"
         },            
    },
    {
        $replaceRoot: 
        { 
            newRoot: 
            { 
                $mergeObjects: [ 
                    {$arrayElemAt: [ "$member1", 0 ]}, 
                    {$arrayElemAt: [ "$member2", 0 ]}, 
                    {$arrayElemAt: [ "$leader", 0 ]}, 
                "$$ROOT" ] 
            } 
        }
    }, 
    { 
        $project: 
        { 
            member1: 0,
            member2: 0,
            leader: 0
        } 
    } 
  ],
  function (err, response) {
    if (err) throw err;
    if (!response) {
        res.status(200).json({ success: false, message: 'Somthing went wrong. Please contact admin.'});
    }
    else {
        res.status(200).json({ success: true, message: 'Successfull', data: response});
    }
  });
};

没有合并的结果集:

{
    "success": true,
    "message": "Successfull",
    "data": [
        {
            "_id": "5b5d083cf7edf20be8999746",
            "assignmentId": 25,
            "taskId": 1,
            "assignedLeader": 2,
            "assignedMember1": 1,
            "assignedMember2": 1,
            "priority": "M",
            "startDate": "2018-01-08T05:00:00.000Z",
            "endDate": "2018-09-08T04:00:00.000Z",
            "status": "R",
            "billingMode": "F",
            "fixedAmount": 12,
            "hourlyRate": 0,
            "hours": 0,
            "hourlyAmount": 0,
            "clientId": 4,
            "__v": 0,
            "member1": [
                {
                    "_id": "5b5906a66a6c614f8cb9bd99",
                    "staffId": 1,
                    "staffName": "A",
                    "designation": "E",
                    "phone": 9876,
                    "address1": "US",
                    "address2": "",
                    "email": "a@a.com",
                    "manager": "2",
                    "__v": 0
                }
            ],
            "member2": [
                {
                    "_id": "5b5906a66a6c614f8cb9bd99",
                    "staffId": 1,
                    "staffName": "B",
                    "designation": "E",
                    "phone": 9876,
                    "address1": "US",
                    "address2": "",
                    "email": "b@b.com",
                    "manager": "2",
                    "__v": 0
                }
            ],
            "leader": [
                {
                    "_id": "5b5906cb6a6c614f8cb9bd9a",
                    "staffId": 2,
                    "staffName": "C",
                    "designation": "M",
                    "phone": 2488263783,
                    "address1": "U",
                    "address2": "",
                    "email": "c@c.com",
                    "manager": "",
                    "__v": 0
                }
            ]
        }
    ]
}

带有合并的结果集:- [最后一次查找与领导者替换成员 1 和成员 2]

{
    "success": true,
    "message": "Successfull",
    "data": [
        {
            "_id": "5b5d083cf7edf20be8999746",
            "assignmentId": 25,
            "taskId": 1,
            "assignedLeader": 2,
            "assignedMember1": 1,
            "assignedMember2": 1,
            "priority": "M",
            "startDate": "2018-01-08T05:00:00.000Z",
            "endDate": "2018-09-08T04:00:00.000Z",
            "status": "R",
            "billingMode": "F",
            "fixedAmount": 12,
            "hourlyRate": 0,
            "hours": 0,
            "hourlyAmount": 0,
            "clientId": 4,
            "__v": 0,
            "staffId": 2,
            "staffName": "C",
            "designation": "M",
            "phone": 2488263783,
            "address1": "U",
            "address2": "",
            "email": "c@c.com",
            "manager": "",
        }
    ]
}

更新代码:-

module.exports.getAssignmentByClient = function(req, res) {
var query = url.parse(req.url,true).query;

Assignment.aggregate([
    {
        $match : 
        { 
            clientId: 4, 
        } , 
    },
    {
       $lookup:
        {
            from: "staffs",
            localField: "assignedMember1",
            foreignField: "staffId",
            as: "member1"
        },            
    },
    {
        $lookup:
         {
             from: "staffs",
             localField: "assignedMember2",
             foreignField: "staffId",
             as: "member2"
         },            
    },
    {
        $lookup:
         {
             from: "staffs",
             localField: "assignedLeader",
             foreignField: "staffId",
             as: "leader"
         },            
    },
    {"$replaceRoot":{
        "newRoot":{
          "$reduce":{
            "input":[{"k":"member1","v":"$member1"},{"k":"member2","v":"$member2"},{"k":"leader","v":"$leader"}],
            "initialValue":"$$ROOT",
            "in":{
              "$mergeObjects":[
                {"$let":{
                  "vars":{"v":{"$arrayElemAt":["$$this.v",0]}},
                  "in":{"$arrayToObject":{
                    "$map":{
                      "input":{"$objectToArray":"$$v"},
                      "as":"val",
                      "in":{
                        "k":{"$concat":["$$this.k","-","$$val.k"]},
                        "v":"$$val.v"}
                    }
                  }}
                }},
                "$$value"
              ]
            }
          }
        }
    }},
    { 
        "$project": { 
            "member1": 0, 
            "member2": 0, 
            "leader": 0 
        } 
    } 
  ],
  function (err, response) {
    if (err) throw err;
    if (!response) {
        res.status(200).json({ success: false, message: 'Somthing went wrong. Please contact admin.'});
    }
    else {
        res.status(200).json({ success: true, message: 'Successfull', data: response});
    }
  });

};

带有更新代码的 JSON 结果:-

{
"success": true,
"message": "Successfull",
"data": [
    {
        "leader-_id": "5b5906cb6a6c614f8cb9bd9a",
        "leader-staffId": 2,
        "leader-staffName": "A",
        "leader-designation": "M",
        "leader-phone": 2488263783,
        "leader-address1": "US",
        "leader-address2": "",
        "leader-email": "a@a.com",
        "leader-manager": "",
        "leader-__v": 0,
        "member2-_id": "5b5906a66a6c614f8cb9bd99",
        "member2-staffId": 1,
        "member2-staffName": "B",
        "member2-designation": "E",
        "member2-phone": 9876,
        "member2-address1": "US",
        "member2-address2": "",
        "member2-email": "b@b.com",
        "member2-manager": "2",
        "member2-__v": 0,
        "member1-_id": "5b5906a66a6c614f8cb9bd99",
        "member1-staffId": 1,
        "member1-staffName": "C",
        "member1-designation": "E",
        "member1-phone": 9876,
        "member1-address1": "US",
        "member1-address2": "",
        "member1-email": "c@c.com",
        "member1-manager": "2",
        "member1-__v": 0,
        "_id": "5b5d083cf7edf20be8999746",
        "assignmentId": 25,
        "taskId": 1,
        "assignedLeader": 2,
        "assignedMember1": 1,
        "assignedMember2": 1,
        "priority": "M",
        "startDate": "2018-01-08T05:00:00.000Z",
        "endDate": "2018-09-08T04:00:00.000Z",
        "status": "R",
        "billingMode": "F",
        "fixedAmount": 12,
        "hourlyRate": 0,
        "hours": 0,
        "hourlyAmount": 0,
        "clientId": 4,
        "__v": 0
    },
    {
        "_id": "5b5e0f9ced856f17187c8888",
        "assignmentId": 28,
        "taskId": 2,
        "assignedLeader": "2",
        "assignedMember1": "1",
        "assignedMember2": "1",
        "priority": "H",
        "startDate": "2018-01-08T05:00:00.000Z",
        "endDate": "2018-07-08T04:00:00.000Z",
        "status": "C",
        "billingMode": "F",
        "fixedAmount": 900,
        "hourlyRate": 0,
        "hours": 0,
        "hourlyAmount": 0,
        "clientId": 4,
        "__v": 0
    }
]

}

嵌套集合字段引用 *(我已经嵌套查找 assignmentMaster,然后我想将此集合字段作为匹配字段引用到其他集合中,但它没有返回所需的结果。好像我是无法正确引用 assignmentMaster 字段。)

module.exports.getAssignmentByClient = function(req, res) {
var query = url.parse(req.url,true).query;
var clientId = parseInt(query.clientId);

AssignmentDetail.aggregate([
    {
        $match : 
        { 
            clientId: clientId, 
        } , 
    },
    {
        $lookup:
        {
            from: "assignmentmasters",
            localField: "assignmentId",
            foreignField: "assignmentId",
            as: "assignmentMasterData"
        },            
    },
    { 
        $lookup: {
        from: "lists",
        let: { "status": "$assignmentMasterData" },
        pipeline: [
            { 
                $match: 
                    { $expr: { $and: [ 
                        { $eq: 
                            [ "$listName", "AssignmentStatus" ] 
                        },
                        { $eq: 
                            [ "$listItem", "$$status.status" ] 
                        }
                        ]
                    }
                } 
            },
        ],
        as: "assignmentStatus"
    }},


    {"$replaceRoot":{
        "newRoot":{
          "$reduce":{
            "input":[{"k":"assignmentStatus","v":"$assignmentStatus"},{"k":"assignmentMasterData","v":"$assignmentMasterData"}],
            "initialValue":"$$ROOT",
            "in":{
              "$mergeObjects":[
                {"$let":{
                  "vars":{"v":{"$arrayElemAt":["$$this.v",0]}},
                  "in":{"$arrayToObject":{
                    "$map":{
                      "input":{"$objectToArray":"$$v"},
                      "as":"val",
                      "in":{
                        "k":{"$concat":["$$this.k","-","$$val.k"]},
                        "v":"$$val.v"}
                    }
                  }}
                }},
                "$$value"
              ]
            }
          }
        }
    }},
    { 
        "$project": { 
            "assignmentStatus": 0,
            "assignmentMasterData": 0
        } 
    } 
  ],
  function (err, response) {
    if (err) throw err;
    if (!response) {
        res.status(200).json({ success: false, message: 'Somthing went wrong. Please contact admin.'});
    }
    else {
        res.status(200).json({ success: true, message: 'Successfull', data: response});
    }
  });

};

【问题讨论】:

  • 您能发布所有集合中的示例文档吗...您的 mongodb 版本是什么?
  • 我用结果集更新了我的问题。 Mongo DB 版本是 3.6。

标签: javascript node.js mongodb npm mongoose


【解决方案1】:

您可以使用以下聚合查询为每个查找的所有键加上其别名。

使用$concat 将查找别名添加到使用$map$objectToArray$arrayToObject 运算符的每个查找列的前缀。

下面显示了 member1 的示例。类似地添加所有其他查找别名。

类似

{"$replaceRoot":{
    "newRoot":{
      "$mergeObjects":[
        {"$let":{
          "vars":{"v":{"$arrayElemAt":["$member1",0]}},
          "in":{"$arrayToObject":{
            "$map":{
                "input":{"$objectToArray":"$$v"},
                "as":"val",
                "in":{
                  "k":{"$concat":["member1","-","$$val.k"]},
                  "v":"$$val.v"
                }}
          }}
        }},
       --- other look up alias here 
      ]
   }
}}

如果您不想为所有别名重复相同的代码,您可以创建一个文档数组和$reduce 来创建合并文档。

类似

{"$replaceRoot":{
  "newRoot":{
    "$reduce":{
      "input":[{"k":"member1","v":"$member1"},{"k":"member2","v":"$member2"},{"k":"leader","v":"$leader"}],
      "initialValue":"$$ROOT",
      "in":{
        "$mergeObjects":[
          {"$let":{
            "vars":{"v":{"$arrayElemAt":["$$this.v",0]}},
            "in":{"$arrayToObject":{
              "$map":{
                "input":{"$objectToArray":"$$v"},
                "as":"val",
                "in":{
                  "k":{"$concat":["$$this.k","-","$$val.k"]},
                  "v":"$$val.v"}
              }
            }}
          }},
          "$$value"
        ]
      }
    }
  }
}}

【讨论】:

  • 当我在集合分配中只有一条记录时它可以工作。如果集合有一条记录,它会起作用并将字段名称替换为成员 1、2 和领导者查找。但是如果一个集合有多个记录,它不会连接查找结果。
  • 不确定我是否理解您的评论。据我了解,连接每个作业行的所有查找记录。因此,如果有多个分配行,您应该获得多个连接的行。这不是你想要的吗?
  • 是的,你是对的。对于每个分配行,我想连接查找的结果。现在它对分配的第一行工作正常,但分配的其余行没有任何来自查找的连接行。
  • 那不应该发生。我有机会查看您的查询吗?可以添加到帖子底部吗?
  • 我已经用标签更新代码更新了代码。请查看并感谢您对此进行调查。感谢您的帮助。
【解决方案2】:

有没有办法,我可以合并所有 3 个查找结果,并且可以以某种方式更改字段名称,所以一个不会覆盖到另一个。

您可以在现有的聚合查询 (https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/) 中使用 $concatArrays 连接数组!

试试这个代码:

module.exports.getAssignmentByClient = function(req, res) {
var query = url.parse(req.url,true).query;

Assignment.aggregate([
    {
        $match : 
        { 
            clientId: 4, 
        } , 
    },
    {
       $lookup:
        {
            from: "staffs",
            localField: "assignedMember1",
            foreignField: "staffId",
            as: "member1"
        },            
    },
    {
        $lookup:
         {
             from: "staffs",
             localField: "assignedMember2",
             foreignField: "staffId",
             as: "member2"
         },            
    },
    {
        $lookup:
         {
             from: "staffs",
             localField: "assignedLeader",
             foreignField: "staffId",
             as: "leader"
         },            
    },
    { 
        $project: { 
            items: { 
                $concatArrays: [ "$member1", "$member2", "$leader" ] 
            } 
        } 
    }
  ],
  function (err, response) {
    if (err) throw err;
    if (!response) {
        res.status(200).json({ success: false, message: 'Somthing went wrong. Please contact admin.'});
    }
    else {
        res.status(200).json({ success: true, message: 'Successfull', data: response});
    }
  });
};

希望这能解决您的问题!

【讨论】:

  • 它可以工作,但它再次将所有查找结果添加到新数组中,即项目,而不是根。我希望仅将所有查找结果添加到根目录中。感谢您的回复和调查。
猜你喜欢
  • 1970-01-01
  • 2011-09-24
  • 2020-07-17
  • 2016-06-07
  • 2019-03-17
  • 2017-06-14
  • 1970-01-01
  • 2021-07-23
相关资源
最近更新 更多