如何在 mongodb 中使用 $lookup 加入多个集合

Posted

技术标签:

【中文标题】如何在 mongodb 中使用 $lookup 加入多个集合【英文标题】:How to join multiple collections with $lookup in mongodb 【发布时间】:2016-06-19 05:44:23 【问题描述】:

我想使用聚合 $lookup 在 MongoDB 中加入两个以上的集合。是否可以加入?举几个例子吧。

这里有三个集合:

users:

    
    "_id" : ObjectId("5684f3c454b1fd6926c324fd"),
    "email" : "admin@gmail.com",
    "userId" : "AD",
    "userName" : "admin"

userinfo:


    "_id" : ObjectId("56d82612b63f1c31cf906003"),
    "userId" : "AD",
    "phone" : "0000000000"

userrole:


    "_id" : ObjectId("56d82612b63f1c31cf906003"),
    "userId" : "AD",
    "role" : "admin"

【问题讨论】:

Multiple join conditions using the $lookup operator的可能重复 How do I query referenced objects in MongoDB?的可能重复 【参考方案1】:

第一次查找找到 p.cid = categories._id 的所有产品,同样是第二次查找 查找 p.sid = subcategories._id 的所有产品。

让 dataQuery :any = await ProductModel.aggregate([ $lookup: 来自:“类别”, 本地字段:“cid”, 外国字段:“_id”, 作为:“产品” , $unwind: "$products" , $查找: 来自:“子类别”, 本地字段:“sid”, 外国字段:“_id”, 如:“产品列表” , $unwind: "$productList" , $项目: 产品列表:0

  ]);

【讨论】:

【参考方案2】:

首先添加集合,然后对这些集合应用查找。不要使用$unwind as unwind 将简单地将每个集合的所有文档分开。所以应用简单的查找,然后使用$project 进行投影。 这是 mongoDB 查询:

db.userInfo.aggregate([
    
        $lookup: 
           from: "userRole",
           localField: "userId",
           foreignField: "userId",
           as: "userRole"
        
    ,
    
        $lookup: 
            from: "userInfo",
            localField: "userId",
            foreignField: "userId",
            as: "userInfo"
        
    ,
    $project: 
        "_id":0,
        "userRole._id":0,
        "userInfo._id":0
        
         ])

这是输出:

/* 1 */ 
    "userId" : "AD",
    "phone" : "0000000000",
    "userRole" : [ 
        
            "userId" : "AD",
            "role" : "admin"
        
    ],
    "userInfo" : [ 
        
            "userId" : "AD",
            "phone" : "0000000000"
        
    ] 

谢谢。

【讨论】:

如果一个集合中有多个文档,那么所有文档都将显示在数组中。 非常感谢您的回答。这个回答帮助我理解了聚合。你刚刚拯救了我的一天【参考方案3】:

Mongodb 3.2 及更高版本支持的加入功能。您可以通过聚合查询来使用联接。 你可以用下面的例子来做:

db.users.aggregate([

    // Join with user_info table
    
        $lookup:
            from: "userinfo",       // other table name
            localField: "userId",   // name of users table field
            foreignField: "userId", // name of userinfo table field
            as: "user_info"         // alias for userinfo table
        
    ,
       $unwind:"$user_info" ,     // $unwind used for getting data in object or for one record only

    // Join with user_role table
    
        $lookup:
            from: "userrole", 
            localField: "userId", 
            foreignField: "userId",
            as: "user_role"
        
    ,
       $unwind:"$user_role" ,

    // define some conditions here 
    
        $match:
            $and:["userName" : "admin"]
        
    ,

    // define which fields are you want to fetch
       
        $project:
            _id : 1,
            email : 1,
            userName : 1,
            userPhone : "$user_info.phone",
            role : "$user_role.role",
         
    
]);

这将给出如下结果:


    "_id" : ObjectId("5684f3c454b1fd6926c324fd"),
    "email" : "admin@gmail.com",
    "userName" : "admin",
    "userPhone" : "0000000000",
    "role" : "admin"

希望这会对您或其他人有所帮助。

谢谢

【讨论】:

如果您想从其他表中获取数组中的数据,而不仅仅是从该表中删除 $unwind 意味着删除 " $unwind:"$user_role" " 来自从 user_role 中获取数组中数据的查询 这对我很有帮助;特别是在投影中使用 $unwind 和子对象引用 嗨,阿米特,它看起来不错,但它没有解决我的问题,这里是链接,请回复:***.com/questions/61188497/… 在您的查询中对两个连接表尝试 $unwind。 @azEnItH 嘿,阿米特,也许你可以回答你漂亮的回答的后续问题? -> ***.com/questions/67138310/…【参考方案4】:

根据documentation,$lookup 只能加入一个外部集合。

您可以做的是将userInfouserRole 组合在一个集合中,因为提供的示例基于关系数据库模式。 Mongo 是 noSQL 数据库 - 这需要不同的文档管理方法。

请在下面找到结合 userInfo 和 userRole 的两步查询 - 创建用于上次查询的新临时集合以显示组合数据。 在最后一个查询中,有一个选项可以使用 $out 并使用合并的数据创建新集合以供以后使用。

创建集合

db.sivaUser.insert(
    
    "_id" : ObjectId("5684f3c454b1fd6926c324fd"),
        "email" : "admin@gmail.com",
        "userId" : "AD",
        "userName" : "admin"
)

//"userinfo"
db.sivaUserInfo.insert(

    "_id" : ObjectId("56d82612b63f1c31cf906003"),
    "userId" : "AD",
    "phone" : "0000000000"
)

//"userrole"
db.sivaUserRole.insert(

    "_id" : ObjectId("56d82612b63f1c31cf906003"),
    "userId" : "AD",
    "role" : "admin"
)

“加入”他们所有人:-)

db.sivaUserInfo.aggregate([
    $lookup:
        
           from: "sivaUserRole",
           localField: "userId",
           foreignField: "userId",
           as: "userRole"
        
    ,
    
        $unwind:"$userRole"
    ,
    
        $project:
            "_id":1,
            "userId" : 1,
            "phone" : 1,
            "role" :"$userRole.role"
        
    ,
    
        $out:"sivaUserTmp"
    
])


db.sivaUserTmp.aggregate([
    $lookup:
        
           from: "sivaUser",
           localField: "userId",
           foreignField: "userId",
           as: "user"
        
    ,
    
        $unwind:"$user"
    ,
    
        $project:
            "_id":1,
            "userId" : 1,
            "phone" : 1,
            "role" :1,
            "email" : "$user.email",
            "userName" : "$user.userName"
        
    
])

【讨论】:

嗨,Profesor,您的代码看起来不错,但没有解决我的问题我提供了我的问题链接,请帮助我:***.com/questions/61188497/… 有谁知道这句话是否仍然正确:$lookup can join only one external collection?我在文档链接中没有发现任何限制。谢谢【参考方案5】:

您实际上可以链接多个 $lookup 阶段。根据 profesor79 共享的集合名称,您可以这样做:

db.sivaUserInfo.aggregate([
    
        $lookup: 
           from: "sivaUserRole",
           localField: "userId",
           foreignField: "userId",
           as: "userRole"
        
    ,
    
        $unwind: "$userRole"
    ,
    
        $lookup: 
            from: "sivaUserInfo",
            localField: "userId",
            foreignField: "userId",
            as: "userInfo"
        
    ,
    
        $unwind: "$userInfo"
    
])

这将返回以下结构:


    "_id" : ObjectId("56d82612b63f1c31cf906003"),
    "userId" : "AD",
    "phone" : "0000000000",
    "userRole" : 
        "_id" : ObjectId("56d82612b63f1c31cf906003"),
        "userId" : "AD",
        "role" : "admin"
    ,
    "userInfo" : 
        "_id" : ObjectId("56d82612b63f1c31cf906003"),
        "userId" : "AD",
        "phone" : "0000000000"
    

也许这可以被认为是一种反模式,因为 MongoDB 并不是关系型的,但它很有用。

【讨论】:

如果我们想将 userinfo 显示为用户角色中的一个数组怎么办?如何做到这一点 并不是一个关系数据库,但每个文档都有 16MB 的上限,这迫使您将无限数组分散到进一步的集合中...catch 22

以上是关于如何在 mongodb 中使用 $lookup 加入多个集合的主要内容,如果未能解决你的问题,请参考以下文章

如何在 mongodb 中应用 $lookup 条件?

如何在 Node.js 中高效/快速地执行数组连接,类似于 MongoDB $lookup?

如何使用 MongoDB 聚合 `$lookup` 作为 `findOne()`

如何在 mongodb 中使用 $lookup 从该表中获取与另一个表连接的数据

如何在 mongoDB 的第二级实现`lookup`? [复制]

如何在 mongoDB 的第二级实现`lookup`? [复制]