使用 MongoDB 将两个集合合并为一个集合

今天,我们将使用 $lookup 聚合阶段、pipeline$unwind 运算符、$project 过滤阶段和 MongoDB Compass 将两个集合合并为一个集合。

使用 MongoDB 将两个集合合并为一个集合

我们有不同的方法可以使用 MongoDB 将两个集合组合成一个集合。其中一些在下面给出,我们将在本教程中介绍。

  1. 使用 $lookup 聚合阶段加入两个集合
  2. 使用 pipeline 运算符根据指定条件连接两个集合
  3. 在将数组附加到结果文档之前,使用 $unwind 运算符对数组进行展平
  4. 在聚合查询中使用 $project 过滤阶段来连接两个集合
  5. 使用指南针连接两个集合(MongoDB 的图形界面)

对于上述所有场景,我们必须有一个包含两个集合(与 MySQL 中的表相同)的数据库,其中填充了文档(与 MySQL 中的记录相同)。我们使用以下查询完成了这项工作;你也可以这样做。

创建两个名为 usersInformationuserAddress 的集合,它们位于 users 数据库中。此外,使用以下文档填充它们。

创建数据库和集合:

> use users
> db.createCollection('userInformation')
> db.createCollection('userAddress')

用两个文档填充 userInformation 集合:

> db.userInformation.insertMany(
[
{
fullname: 'Mehvish Ashiq',
age: 30,
gender: 'Female',
nationality: 'Pakistani'
},
{
fullname: 'James Daniel',
age: 45,
sex: 'male',
nationality: 'Canadian'
}
]
)

用两个文档填充 userAddress 集合:

> db.userAddress.insertMany(
[
{
fullname: 'Mehvish Ashiq',
block_number: 22,
street: 'Johar Town Street',
city: 'Lahore'
},
{
fullname: 'James Daniel',
block_number: 30,
street: 'Saint-Denis Street',
city: 'Montreal'
}
]
)

我们使用 insertMany() 函数插入多个文档。现在,我们可以使用下面的命令来查看两个集合的数据。

在下面的代码片段中,pretty() 方法显示了干净且格式化的输出,这在 shell 上很容易理解。

显示来自 userInformation 的文档:

> db.userInformation.find().pretty()

输出:

{
        "_id" : ObjectId("628bc4a45c544feccff5a566"),
        "fullname" : "Mehvish Ashiq",
        "age" : 30,
        "gender" : "Female",
        "nationality" : "Pakistani"
}
{
        "_id" : ObjectId("628bc4a45c544feccff5a567"),
        "fullname" : "James Daniel",
        "age" : 45,
        "sex" : "male",
        "nationality" : "Canadian"
}

显示来自 userAddress 的文档:

> db.userAddress.find().pretty()

输出:

{
        "_id" : ObjectId("628bc4ae5c544feccff5a568"),
        "fullname" : "Mehvish Ashiq",
        "block_number" : 22,
        "street" : "Johar Town Street",
        "city" : "Lahore"
}
{
        "_id" : ObjectId("628bc4ae5c544feccff5a569"),
        "fullname" : "James Daniel",
        "block_number" : 30,
        "street" : "Saint-Denis Street",
        "city" : "Montreal"
}

两个集合必须在同一个数据库中才能使用 $lookup 聚合阶段。一旦两个集合都准备好了,我们可以根据我们的场景使用各种查询来连接两个集合的数据。

使用 $lookup 聚合阶段将两个集合合二为一

示例代码:

> db.userInformation.aggregate([
{ $lookup:
{
from: 'userAddress',
localField: 'fullname',
foreignField: 'fullname',
as: 'address'
}
}
]).pretty();

输出:

{
        "_id" : ObjectId("628bc4a45c544feccff5a566"),
        "fullname" : "Mehvish Ashiq",
        "age" : 30,
        "gender" : "Female",
        "nationality" : "Pakistani",
        "address" : [
                {
                        "_id" : ObjectId("628bc4ae5c544feccff5a568"),
                        "fullname" : "Mehvish Ashiq",
                        "block_number" : 22,
                        "street" : "Johar Town Street",
                        "city" : "Lahore"
                }
        ]
}
{
        "_id" : ObjectId("628bc4a45c544feccff5a567"),
        "fullname" : "James Daniel",
        "age" : 45,
        "sex" : "male",
        "nationality" : "Canadian",
        "address" : [
                {
                        "_id" : ObjectId("628bc4ae5c544feccff5a569"),
                        "fullname" : "James Daniel",
                        "block_number" : 30,
                        "street" : "Saint-Denis Street",
                        "city" : "Montreal"
                }
        ]
}

在 MongoDB 数据库中,$lookup 聚合阶段执行与其他集合的左外连接,并从连接的文档中过滤信息(数据)。例如,我们使用查询来获取所有用户的信息及其地址。

$lookup 函数接受四个字段。首先是 from 字段,我们在其中指定应该与另一个集合连接的集合。

第二个是 localField 字段。它是 from 字段中指定的集合的输入文档的属性(字段)之一。

它用于对集合文档中的 localFieldforeignField 执行匹配。

类似地,名为 foreignField 的第三个字段也对集合文档中的 foreignFieldlocalField 执行相等匹配。

我们为第四个字段 as 写下新数组的名称。有关 $lookup 聚合阶段的说明,请参见以下说明。

使用 MongoDB 将两个集合合并为一个集合

使用 pipeline 运算符根据指定条件将两个集合合并为一个

示例代码:

> db.userInformation.aggregate([{
$lookup:{
from: 'userAddress',
let: {full_name: '$fullname'},
pipeline: [{
$match: {
$expr: {
$eq: ['$fullname', '$$full_name']
}
}
}],
as: 'addressInfo'
}
}]).pretty()

输出:

{
        "_id" : ObjectId("628bc4a45c544feccff5a566"),
        "fullname" : "Mehvish Ashiq",
        "age" : 30,
        "gender" : "Female",
        "nationality" : "Pakistani",
        "addressInfo" : [
                {
                        "_id" : ObjectId("628bc4ae5c544feccff5a568"),
                        "fullname" : "Mehvish Ashiq",
                        "block_number" : 22,
                        "street" : "Johar Town Street",
                        "city" : "Lahore"
                }
        ]
}
{
        "_id" : ObjectId("628bc4a45c544feccff5a567"),
        "fullname" : "James Daniel",
        "age" : 45,
        "sex" : "male",
        "nationality" : "Canadian",
        "addressInfo" : [
                {
                        "_id" : ObjectId("628bc4ae5c544feccff5a569"),
                        "fullname" : "James Daniel",
                        "block_number" : 30,
                        "street" : "Saint-Denis Street",
                        "city" : "Montreal"
                }
        ]
}

当我们想要基于特定条件连接两个集合时,我们可以使用带有 $lookuppipeline 运算符(就像我们在 MySQL 中使用 WHERE 子句一样)。

例如,我们正在加入来自 userAddressfullname 等于 userInformation 中的 fullname 的集合。

在附加到结果文档之前使用 $unwind 运算符来平面数组

示例代码:

> db.userInformation.aggregate([
{ $lookup:
{
from: 'userAddress',
localField: 'fullname',
foreignField: 'fullname',
as: 'address'
}
},
{
$unwind: '$address'
}
]).pretty();

输出:

{
        "_id" : ObjectId("628bc4a45c544feccff5a566"),
        "fullname" : "Mehvish Ashiq",
        "age" : 30,
        "gender" : "Female",
        "nationality" : "Pakistani",
        "address" : {
                "_id" : ObjectId("628bc4ae5c544feccff5a568"),
                "fullname" : "Mehvish Ashiq",
                "block_number" : 22,
                "street" : "Johar Town Street",
                "city" : "Lahore"
        }
}
{
        "_id" : ObjectId("628bc4a45c544feccff5a567"),
        "fullname" : "James Daniel",
        "age" : 45,
        "sex" : "male",
        "nationality" : "Canadian",
        "address" : {
                "_id" : ObjectId("628bc4ae5c544feccff5a569"),
                "fullname" : "James Daniel",
                "block_number" : 30,
                "street" : "Saint-Denis Street",
                "city" : "Montreal"
        }
}

$unwind 运算符什么也不做,只是在将数组附加到结果文档之前将其展平。 $unwind 运算符的根本区别在于它将具有单个元素的数组转换为扁平对象,即元素本身。

请记住,此元素的名称不会更改。当元素为数组形式时,它与以前相同。

使用和不使用 $unwind 运算符执行上述查询并观察 address 字段。

在聚合查询中使用 $project 过滤器阶段将两个集合合二为一

在使用 $project 加入集合之前,让我们了解它的重要性。例如,如果我们不想将名为 userAddress 的整个集合与 userInformation 连接起来,我们只希望连接 citystreet 字段。

在这种情况下,我们需要使用 $addFields 阶段。我们使用此阶段将数组/对象中的任何字段或多个字段加入/分配到文档的根级别。

因此,我们执行以下查询以从 userAddress 集合中检索 citystreet

示例代码:

> db.userInformation.aggregate([
{ $lookup:
{
from: 'userAddress',
localField: 'fullname',
foreignField: 'fullname',
as: 'address'
}
},
{
$unwind: '$address'
},
{
$addFields: {
street: '$address.street',
city: '$address.city'
}
}
]).pretty();

输出:

{
        "_id" : ObjectId("628bc4a45c544feccff5a566"),
        "fullname" : "Mehvish Ashiq",
        "age" : 30,
        "gender" : "Female",
        "nationality" : "Pakistani",
        "address" : {
                "_id" : ObjectId("628bc4ae5c544feccff5a568"),
                "fullname" : "Mehvish Ashiq",
                "block_number" : 22,
                "street" : "Johar Town Street",
                "city" : "Lahore"
        },
        "street" : "Johar Town Street",
        "city" : "Lahore"
}
{
        "_id" : ObjectId("628bc4a45c544feccff5a567"),
        "fullname" : "James Daniel",
        "age" : 45,
        "sex" : "male",
        "nationality" : "Canadian",
        "address" : {
                "_id" : ObjectId("628bc4ae5c544feccff5a569"),
                "fullname" : "James Daniel",
                "block_number" : 30,
                "street" : "Saint-Denis Street",
                "city" : "Montreal"
        },
        "street" : "Saint-Denis Street",
        "city" : "Montreal"
}

仔细关注上面给出的输出。我们得到了街道城市吗?是的,我们在文档的根级别获得了 streetcity,但也有我们现在不需要的 address 对象。

这就是 $project 过滤阶段的用武之地。它指定我们应该在结果文档中包含哪些字段。

请参阅以下查询以获得更好的理解。

示例代码:

> db.userInformation.aggregate([
{ $lookup:
{
from: 'userAddress',
localField: 'fullname',
foreignField: 'fullname',
as: 'address'
}
},
{
$unwind: '$address'
},
{
$addFields: {
street: '$address.street',
city: '$address.city'
}
},
{
$project: {
fullname: 1,
age: 1,
gender: 1,
street: 1,
city: 1
}
}
]).pretty();

输出:

{
        "_id" : ObjectId("628bc4a45c544feccff5a566"),
        "fullname" : "Mehvish Ashiq",
        "age" : 30,
        "gender" : "Female",
        "street" : "Johar Town Street",
        "city" : "Lahore"
}
{
        "_id" : ObjectId("628bc4a45c544feccff5a567"),
        "fullname" : "James Daniel",
        "age" : 45,
        "street" : "Saint-Denis Street",
        "city" : "Montreal"
}

如你所见,我们现在没有 address 对象,但它的两个字段(streetcity)被分配给文档的根级别。

使用 Compass 连接两个集合(MongoDB 的图形界面)

使用图形界面进行聚合很容易。我们只需要在 $lookup 聚合阶段执行以下步骤。

  1. 打开 MongoDBCompass 并连接到服务器。

  2. 如果需要,创建一个全新的数据库和两个集合。我们使用使用 Mongo shell 创建的相同数据库和集合。

  3. 打开你的集合,如下所示。

    使用 MongoDB 将两个集合合并为一个集合

  4. 根据你的项目要求添加阶段;我们添加了 $lookup 聚合阶段。更新 $lookup 字段并在右侧查看所需的结果。

    使用 MongoDB 将两个集合合并为一个集合