使用$lookup
与$mergeObjects
配合使用
以及进行指定多个连接条件
语法 |
说明 |
from |
同一个数据库下等待被Join的集合。 |
localField |
源集合中的match值,如果输入的集合中,某文档没有 localField这个Key(Field),在处理的过程中,会默认为此文档含有 localField:null的键值对。 |
foreignField |
待Join的集合的match值,如果待Join的集合中,文档没有foreignField值,在处理的过程中,会默认为此文档含有 foreignField:null的键值对 |
as |
为输出文档的新增值命名。如果输入的集合中已存在该值,则会覆盖掉 |
注意!!
$lookup是如果涉及关联"_id",注意两个字段的类型,用string类型匹配ObjectId类型是关联不上的
准备数据
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
])
db.inventory.insert([
{ "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },
{ "_id" : 5, "sku": null, description: "Incomplete" },
{ "_id" : 6 }
])
两种用法
1.一种关联
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
2.自定义多种关联
{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the collection to join> ],
as: <output array field>
}
}
orders表为主表 inventory表为从表 根据 orders表的item字段与inventory表的sku进行关联
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
该操作返回以下文档:
{
"_id": 1,
"item": "almonds",
"price": 12,
"quantity": 2,
"inventory_docs": [
{
"_id": 1,
"sku": "almonds",
"description": "product 1",
"instock": 120
}
]
}
{
"_id": 2,
"item": "pecans",
"price": 20,
"quantity": 1,
"inventory_docs": [
{
"_id": 4,
"sku": "pecans",
"description": "product 4",
"instock": 70
}
]
}
{
"_id": 3,
"inventory_docs": [
{
"_id": 5,
"sku": null,
"description": "Incomplete"
},
{
"_id": 6
}
]
}
对应sql的写法
SELECT *, inventory_docs
FROM orders
WHERE inventory_docs IN (SELECT *
FROM inventory
WHERE sku= orders.item);
使用lookup操作数组
准备数据
db.classes.insert([
{
_id: 1,
title: "Reading is ...",
enrollmentlist: ["giraffe2", "pandabear", "artie"],
days: ["M", "W", "F"]
},
{
_id: 2,
title: "But Writing ...",
enrollmentlist: ["giraffe1", "artie"],
days: ["T", "F"]
}
])
db.members.insert( [
{ _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },
{ _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },
{ _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },
{ _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },
{ _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },
{ _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }
])
classes主表 通过enrollmentlist 与members从表进行关联
不同之处是:主表的关联字段是数组的结构 跟从表的关联关系时多对一的关系
db.classes.aggregate([
{
$lookup:
{
from: "members",
localField: "enrollmentlist",
foreignField: "name",
as: "enrollee_info"
}
}
])
返回的集合
{
"_id": 1,
"title": "Reading is ...",
"enrollmentlist": [
"giraffe2",
"pandabear",
"artie"
],
"days": [
"M",
"W",
"F"
],
"enrollee_info": [
{
"_id": 1,
"name": "artie",
"joined": ISODate("2016-05-01T00:00:00.000Z"),
"status": "A"
},
{
"_id": 5,
"name": "pandabear",
"joined": ISODate("2018-12-01T00:00:00.000Z"),
"status": "A"
},
{
"_id": 6,
"name": "giraffe2",
"joined": ISODate("2018-12-01T00:00:00.000Z"),
"status": "D"
}
]
}
{
"_id": 2,
"title": "But Writing ...",
"enrollmentlist": [
"giraffe1",
"artie"
],
"days": [
"T",
"F"
],
"enrollee_info": [
{
"_id": 1,
"name": "artie",
"joined": ISODate("2016-05-01T00:00:00.000Z"),
"status": "A"
},
{
"_id": 3,
"name": "giraffe1",
"joined": ISODate("2017-10-01T00:00:00.000Z"),
"status": "A"
}
]
}
使用$lookup
与$mergeObjects
配合使用
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
])
db.items.insert([
{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },
{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },
{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
])
$replaceRoot:用指定的文档替换输入文档。该操作将替换输入文档中的所有现有字段,包括_id字段。您可以将现有嵌入文档提升到顶层,或创建新文档进行提升
$mergeObjects:将多个文档合并为一个文档。
mergeObjects忽略对象{}。如果mergeObjects的所有操作数都解析为null,那么mergeObject将返回一个空文档{}。
合并对象在合并文档时覆盖字段值。如果要合并的文档包含相同的字段名,则结果文档中的字段具有该字段最后一个合并文档中的值。
$arrayElemAt:返回指定数组索引处的元素。
db.orders.aggregate([
{
$lookup: {
from: "items",
localField: "item",
foreignField: "item",
as: "fromItems"
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
{
$arrayElemAt: ["$fromItems", 0]
}, "$$ROOT"]
}
}
},
{
$project: {
fromItems: 0
}
}
])
该操作返回的集合
{
"_id": 1,
"item": "almonds",
"description": "almond clusters",
"instock": 120,
"price": 12,
"quantity": 2
}
{
"_id": 2,
"item": "pecans",
"description": "candied pecans",
"instock": 60,
"price": 20,
"quantity": 1
}
使用$lookup指定多个联接条件
准备数据
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
])
db.warehouses.insert([
{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },
{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },
{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },
{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },
{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
])
注意:
pipeline中的
m
a
t
c
h
特殊操作需要
match特殊操作需要
match特殊操作需要expr进行条件过滤,如果在使用match没有效果的的情况下可以试试$expr哦
$expr运算符仅使用from集合上的索引进行相等匹配。例如,如果仓库集合上存在索引{stock_item:1,instock:1}:
db.orders.aggregate([
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
])
返回的集合
{
"_id": 1,
"item": "almonds",
"price": 12,
"ordered": 2,
"stockdata": [{
"warehouse": "A",
"instock": 120
}, {
"warehouse": "B",
"instock": 60
}]
}
{
"_id": 2,
"item": "pecans",
"price": 20,
"ordered": 1,
"stockdata": [{
"warehouse": "A",
"instock": 80
}]
}
{
"_id": 3,
"item": "cookies",
"price": 10,
"ordered": 60,
"stockdata": [{
"warehouse": "A",
"instock": 80
}]
}
对应sql的写法
SELECT *, stockdata
FROM orders
WHERE stockdata IN ( SELECT warehouse, instock
FROM warehouses
WHERE stock_item = orders.item
AND instock >= orders.ordered );
允许指定多个连接条件以及不相关的子查询。
准备数据
db.absences.insert([
{ "_id" : 1, "student" : "Ann Aardvark", sickdays: [ new Date ("2018-05-01"),new Date ("2018-08-23") ] },
{ "_id" : 2, "student" : "Zoe Zebra", sickdays: [ new Date ("2018-02-01"),new Date ("2018-05-23") ] },
])
db.holidays.insert([
{ "_id" : 1, year: 2018, name: "New Years", date: new Date("2018-01-01") },
{ "_id" : 2, year: 2018, name: "Pi Day", date: new Date("2018-03-14") },
{ "_id" : 3, year: 2018, name: "Ice Cream Day", date: new Date("2018-07-15") },
{ "_id" : 4, year: 2017, name: "New Years", date: new Date("2017-01-01") },
{ "_id" : 5, year: 2017, name: "Ice Cream Day", date: new Date("2017-07-16") }
])
db.absences.aggregate([
{
$lookup:
{
from: "holidays",
pipeline: [
{ $match: { year: 2018 } },
{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },
{ $replaceRoot: { newRoot: "$date" } }
],
as: "holidays"
}
}
])
返回的集合
{
"_id": 1,
"student": "Ann Aardvark",
"sickdays": [ISODate("2018-05-01T00:00:00Z"), ISODate("2018-08-23T00:00:00Z")],
"holidays": [{
"name": "New Years",
"date": ISODate("2018-01-01T00:00:00Z")
}, {
"name": "Pi Day",
"date": ISODate("2018-03-14T00:00:00Z")
}, {
"name": "Ice Cream Day",
"date": ISODate("2018-07-15T00:00:00Z")
}]
}
{
"_id": 2,
"student": "Zoe Zebra",
"sickdays": [ISODate("2018-02-01T00:00:00Z"), ISODate("2018-05-23T00:00:00Z")],
"holidays": [{
"name": "New Years",
"date": ISODate("2018-01-01T00:00:00Z")
}, {
"name": "Pi Day",
"date": ISODate("2018-03-14T00:00:00Z")
}, {
"name": "Ice Cream Day",
"date": ISODate("2018-07-15T00:00:00Z")
}]
}
对应sql的写法
SELECT *, holidays
FROM absences
WHERE holidays IN (SELECT name, date
FROM holidays
WHERE year = 2018);
内容来自:
mongoDB $lookup (aggregation)官方文档版本4.2
mongoDB $mergeObjects (aggregation)官方文档版本4.2
mongoDB $arrayElemAt (aggregation)官方文档版本4.2
mongoDB $replaceRoot (aggregation)官方文档版本4.2