MongoDB 聚合管道的文档关联查询($lookup)及管道合并($unionWith)

目前为止,我们已经介绍了一部分聚合管道中的管道参数:

        $match:文档过滤

        $group:文档分组,并介绍了分组中的常用操作:$addToSet,$avg,$sum,$min,$max等。

        $addFields:添加字段,等效于$set

        $unset:移除字段

        $project:字段投影

        $sort:文档排序

        $skip:跳过N条文档

        $limit:获取N条文档

        $sample:随机抽取N条文档

        $unwind:分解文档

如果需要进一步了解可以参考:
MongoDB 聚合管道的文档筛选及分组统计https://blog.csdn.net/m1729339749/article/details/130034658MongoDB 聚合管道的字段投影https://blog.csdn.net/m1729339749/article/details/130055110MongoDB 聚合管道的文档操作https://blog.csdn.net/m1729339749/article/details/130066663本篇我们介绍聚合管道的集合关联及合并操作:

一、准备工作

初始化零食数据

db.snacks.insertMany([
    { "_id": 1,  name: "薯片" },
    { "_id": 2,  name: "牛肉干" },
    { "_id": 3,  name: "可口可乐" },
    { "_id": 4,  name: "旺仔牛奶" }
])

初始化零食型号及储量

db.snacksType.insertMany([
    { "_id": 1, size: "S", quantity: 10, price: 8, expirationTime: ISODate( "2023-08-08T00:00:00Z" ), "snacks_id": 1 },
    { "_id": 2,  size: "L", quantity: 8, price: 12, expirationTime: ISODate( "2023-08-08T00:00:00Z" ), "snacks_id": 1 },
    { "_id": 3,  size: "L", quantity: 5, price: 30, expirationTime: ISODate( "2023-10-10T00:00:00Z" ), "snacks_id": 2 },
    { "_id": 4,  size: "S", quantity: 10, price: 3, expirationTime: ISODate( "2025-01-06T00:00:00Z" ), "snacks_id": 3 },
    { "_id": 5,  size: "L", quantity: 6, price: 10, expirationTime: ISODate( "2025-01-06T00:00:00Z" ), "snacks_id": 3 },
    { "_id": 6,  size: "L", quantity: 10, price: 5, expirationTime: ISODate( "2023-08-10T00:00:00Z" ), "snacks_id": 4}
])

二、使用字段关联查询($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>
     }
}

from:指的是关联的集合

localField:指的是当前管道中的字段

foreignField:指的是关联的集合中的字段

as:输出的数组字段;此字段将作为当前管道中的字段,字段的值来源于关联的集合中。

例子:在零食文档中关联上零食对应的所有型号信息

db.snacks.aggregate([
    {
        $lookup: {
            from: "snacksType",
            localField: "_id",
            foreignField: "snacks_id",
            as: "types"
        }
    }
])

我们对上面的聚合查询语法进行解释:

使用snacks集合中的字段_id与snacksType集合中的字段snacks_id进行关联查询snacksType集合中的文档,并将查询的结果作为当前管道中types字段的值。

聚合查询的结果如下:

{
	"_id" : 1,
	"name" : "薯片",
	"types" : [
		{
			"_id" : 1,
			"size" : "S",
			"quantity" : 10,
			"price" : 8,
			"expirationTime" : ISODate("2023-08-08T00:00:00Z"),
			"snacks_id" : 1
		},
		{
			"_id" : 2,
			"size" : "L",
			"quantity" : 8,
			"price" : 12,
			"expirationTime" : ISODate("2023-08-08T00:00:00Z"),
			"snacks_id" : 1
		}
	]
}
{
	"_id" : 2,
	"name" : "牛肉干",
	"types" : [
		{
			"_id" : 3,
			"size" : "L",
			"quantity" : 5,
			"price" : 30,
			"expirationTime" : ISODate("2023-10-10T00:00:00Z"),
			"snacks_id" : 2
		}
	]
}
{
	"_id" : 3,
	"name" : "可口可乐",
	"types" : [
		{
			"_id" : 4,
			"size" : "S",
			"quantity" : 10,
			"price" : 3,
			"expirationTime" : ISODate("2025-01-06T00:00:00Z"),
			"snacks_id" : 3
		},
		{
			"_id" : 5,
			"size" : "L",
			"quantity" : 6,
			"price" : 10,
			"expirationTime" : ISODate("2025-01-06T00:00:00Z"),
			"snacks_id" : 3
		}
	]
}
{
	"_id" : 4,
	"name" : "旺仔牛奶",
	"types" : [
		{
			"_id" : 6,
			"size" : "L",
			"quantity" : 10,
			"price" : 5,
			"expirationTime" : ISODate("2023-08-10T00:00:00Z"),
			"snacks_id" : 4
		}
	]
}

可以看出每个零食文档中都增加了一个数组字段types,里面包含了零食对应的型号

三、使用管道关联查询($lookup)

语法:

{
   $lookup:
      {
         from: <joined collection>,
         let: { <var_1>: <expression>, …, <var_n>: <expression> },
         pipeline: [ <pipeline to run on joined collection> ],
         as: <output array field>
      }
}

from:值的是关联的集合

let:可选,定义变量,这些变量可以在参数pipeline中使用

pipeline:指的是运行在关联的集合上的管道

as:输出的数组字段;此字段将作为当前管道中的字段,字段的值来源于关联的集合执行管道操作后输出的文档。

例子:在零食文档中关联上零食对应的型号、价格、储量、总价值信息

第一步:我们使用管道查询型号集合中的型号、总价值信息

db.snacksType.aggregate([
    {
        $project: {
            "_id": 0,
            "size": 1,
            "totalValue": { $multiply: [ "$price", "$quantity" ] }
        }
    }
])

执行的结果如下:

{ "size" : "S", "totalValue" : 80 }
{ "size" : "L", "totalValue" : 96 }
{ "size" : "L", "totalValue" : 150 }
{ "size" : "S", "totalValue" : 30 }
{ "size" : "L", "totalValue" : 60 }
{ "size" : "L", "totalValue" : 50 }

 第二步:根据零食关联型号信息

db.snacks.aggregate([
    {
        $lookup: {
            from: "snacksType",
            let: { "snackId": "$_id" },
            pipeline: [
                { 
                    $match: { 
                        $expr: { 
                            $eq: [ "$snacks_id", "$$snackId" ] 
                        } 
                    } 
                },
                {
                    $project: {
                        "_id": 0,
                        "size": 1,
                        "totalValue": { $multiply: [ "$price", "$quantity" ] }
                    }
                }
            ],
            as: "types"
        }
    }
])

我们对上面的聚合查询语法进行解释:

定义了snackId变量用于存储零食的编号(_id);在集合snacksType对应的管道上使用字段snacks_id对文档进行过滤,然后再进行字段投影。

定义的变量在使用时需要使用 $$ + 变量名;如果使用变量时,需要使用$expr运算符(在$expr运算符内部使用变量)

执行的结果如下:

{
	"_id" : 1,
	"name" : "薯片",
	"types" : [
		{
			"size" : "S",
			"totalValue" : 80
		},
		{
			"size" : "L",
			"totalValue" : 96
		}
	]
}
{
	"_id" : 2,
	"name" : "牛肉干",
	"types" : [
		{
			"size" : "L",
			"totalValue" : 150
		}
	]
}
{
	"_id" : 3,
	"name" : "可口可乐",
	"types" : [
		{
			"size" : "S",
			"totalValue" : 30
		},
		{
			"size" : "L",
			"totalValue" : 60
		}
	]
}
{
	"_id" : 4,
	"name" : "旺仔牛奶",
	"types" : [
		{
			"size" : "L",
			"totalValue" : 50
		}
	]
}

四、合并集合管道

语法:{ $unionWith: { coll: "<collection>", pipeline: [ <stage1>, ... ] } }

合并两个集合的管道

<collection>代表的是集合或者视图

[ <stage1>, ... ] 代表的是对<collection>进行处理的多个阶段组成的管道

例子:合并零食与型号集合中的文档

db.snacks.aggregate([
    {
        $project: { "name": 1, "_id": 0 }
    },
    {
        $unionWith: {
            coll: "snacksType",
            pipeline: [
                 { 
                    $project: { "size": "1", "_id": 0 } 
                 } 
            ]
        }
    }
])

我们对聚合查询进行一下解释:

(1) 使用$project 对snacks集合中的文档进行投影,投影后只保留了name字段

(2) 使用$project 对snacksType集合中的文档进行投影,投影后只保留了size字段

(3) 使用$unionWith 对两个集合的管道进行合并

执行的结果如下:

{ "name" : "薯片" }
{ "name" : "牛肉干" }
{ "name" : "可口可乐" }
{ "name" : "旺仔牛奶" }
{ "size" : "S" }
{ "size" : "L" }
{ "size" : "L" }
{ "size" : "S" }
{ "size" : "L" }
{ "size" : "L" }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值