如何在MongoDB中进行内部连接?

zzwlnbp8  于 2022-11-28  发布在  Go
关注(0)|答案(7)|浏览(221)

有没有可能在MongoDB中做SQL内部连接之类的事情?
我知道聚合管道中有$lookup属性,它相当于SQL中的外部联接,但我想做一些类似于内部联接的操作。
我有三个集合需要合并在一起:

// User Collection
db.User.find({});

// Output:
{
   ID : 1,
   USER_NAME : "John",
   password : "pass"
}
{

   ID : 2,
   USER_NAME : "Andrew",
   PASSWORD : "andrew"
}

// Role Collection
db.ROLE.find({});

// Output:
{
   ID : 1,
   ROLE_NAME : "admin"
},
{
    ID : 2,
    ROLE_NAME : "staff"
}

// USER_ROLE Collection
db.USER_ROLE.find({});

// Output:
{
   ID : 1,
   USER_ID : 1,
   ROLE_ID : 1
}

我有上面的集合,我只想提取与用户及其角色匹配的文档,而不是所有的文档,在MongoDB中如何管理?

yebdmbv4

yebdmbv41#

我自己找到了答案
$unwind为我提供了以下查询

db.USER.aggregate([{
            $lookup: {
                from: "USER_ROLE",
                localField: "ID",
                foreignField: "USER_ID",
                as: "userRole"
            }
        }, {
            $unwind: {
                path: "$userRole",
                preserveNullAndEmptyArrays: false
            }
        }, {
            $lookup: {
                from: "ROLE",
                localField: "userRole.ROLE_ID",
                foreignField: "ID",
                as: "role"
            }
        }, {
            $unwind: {
                path: "$role",
                preserveNullAndEmptyArrays: false
            }
        }, {
            $match: {
                "role.ROLE_NAME": "staff"
            }, {
                $project: {
                    USER_NAME: 1,
                    _id: 0
                }
            }
            ]).pretty()

总之谢谢你的回答

fbcarpbf

fbcarpbf2#

正如Tiramisu所写,这看起来像是架构问题。
您可以通过删除$lookup返回空数组的文档来进行手动内部连接。

....
{$lookup... as myArray},
{$match: {"myArray":{$ne:[]}}},
{$lookup... as myArray2},
{$match: {"myArray2":{$ne:[]}}},

结构描述变更
我个人将进行模式更新,如下所示:

db.User.find({})
{
   ID : 1,
   USER_NAME : "John",
   password : "pass"
   roles:[{ID : 1,  ROLE_NAME : "admin"}]
}

db.ROLE.find({})
{
   ID : 1,
   ROLE_NAME : "admin"
},
laawzig2

laawzig23#

这会有帮助吗

const RolesSchema = new Schema({
  ....

});
const Roles = mongoose.model('Roles', RolesSchema);

const UserSchema = new Schema({
  ...

  roles: [{ type: mongoose.Schema.Types.ObjectId, ref: "Roles" }]
});

在userschema上使用populate还可以减少冗余

cyvaqqii

cyvaqqii4#

您是对的,$lookup属性相当于SQL中的外连接,但是在mongoDB中,您需要额外的聚合阶段,以便在mongo中执行类似的INNER JOIN

db.User.aggregate([{
      $lookup: {
          from: "ROLE",
          localField: "ID",
          foreignField: "ID",
          as: "result"
        }
    },{
       $match: {
           result: {
             $ne: []
           }
        }
    },{
      $addFields: {
           result: {
             $arrayElemAt: ["$result",0]
           }
      }
    },{
       $project: {
          USER_NAME: "$USER_NAME",
          ROLE_NAME: "$result.ROLE_NAME"
       }
    }])

"希望能有所帮助"

iqxoj9l9

iqxoj9l95#

MongoDB**$lookup聚合是解决这个问题的最正式和最优化的方法。但是,如果您使用Node.js**作为服务器端,那么您可以使用下面的小技巧。

CollectionOne.find().then((data0) => {
    if (data0.length > 0) {
        let array = [];
        for (let i = 0; i < data0.length; i++) {
            let x = data0[i]
            let y = x.yourForeignKey;
            array.push({_id: y});
        }
        CollectionTwo.find(
            {$or: array}
        ).then((data1) => {
            res.status(200).json(data1);
        }).catch((error1) => {
            return error1;
        })
    }
}).catch((error0) => {
    return error0;
});

我使用了MongoDB的Array Push()方法和$or运算符。您可以使用$nor运算符代替$or来查找外部连接文档。您还可以使用$ne$nor$or$and等来更改查找算法。

bbmckpt7

bbmckpt76#

$查找聚合

对同一数据库中的集合执行左外部联接,以筛选“联接”集合中的文档进行处理。$lookup阶段向每个输入文档添加一个新的数组字段。新的数组字段包含“联接”集合中的匹配文档。$lookup阶段将这些经过整形的文档传递到下一阶段。
从MongoDB 5.1开始,$lookup可以跨分片集合工作。
若要组合两个不同集合中的元素,请使用$unionWith管道阶段。

语法

$lookup阶段具有以下语法:

具有单一连接条件的相等匹配

要在输入文档中的字段与“联接”集合的文档中的字段之间执行相等匹配,$lookup阶段的语法如下:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

更多详情:https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/

de90aj5v

de90aj5v7#

> show dbs
admin   0.000GB
config  0.000GB
local   0.002GB
> use local
switched to db local
> show collections
startup_log
test1
test2
> db.test2.aggregate([{
...    $lookup: {
...       from: "test1",
...       localField: "id",
...       foreignField: "id",
...       as: "aggTest"
...    }
... }])

相关问题