View a markdown version of this page

$lookup - Amazon DocumentDB

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

$lookup

Amazon DocumentDB 中的$lookup聚合阶段允许您在两个集合之间执行左外连接。此操作允许您根据匹配的字段值合并来自多个集合的数据。当您需要将来自相关集合的数据合并到查询结果中时,它特别有用。

参数

  • from:用于执行连接的集合的名称。

  • localField: 输入文档中要与之匹配的字段foreignField。

  • foreignField: from 集合中要与之匹配的文档中的字段localField。

  • as:要添加到包含from集合中匹配文档的输出文档的新字段的名称。

  • pipeline:如果指定了localField和,foreignField则为可选。在集合上运行的聚from合管道。管道从from集合中返回文档。要返回所有文档,请指定一个空管道[]。管道不能包含$out或$merge阶段。请参阅连接条件和相关子查询 (MongoDB Shell)。

  • let:可选。定义pipeline阶段中使用的变量的文档。使用变量表达式访问加入的输入文档中的字段pipeline。要在流水线阶段引用变量,请使用"$$<variable>"语法。请参阅连接条件和相关子查询 (MongoDB Shell)。

示例(MongoDB Shell)

以下示例演示了将集合中的数据连接到orders集合中的customers简单$lookup操作。

创建示例文档

db.customers.insertMany([ { _id: 1, name: "Alice" }, { _id: 2, name: "Bob" }, { _id: 3, name: "Charlie" } ]); db.orders.insertMany([ { _id: 1, customer_id: 1, total: 50 }, { _id: 2, customer_id: 1, total: 100 }, { _id: 3, customer_id: 2, total: 75 } ]);

查询示例

db.customers.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "customer_id", as: "orders" } } ]);

输出

[ { _id: 1, name: 'Alice', orders: [ { _id: 2, customer_id: 1, total: 100 }, { _id: 1, customer_id: 1, total: 50 } ] }, { _id: 3, name: 'Charlie', orders: [] }, { _id: 2, name: 'Bob', orders: [ { _id: 3, customer_id: 2, total: 75 } ] } ]

连接条件和相关子查询 (MongoDB Shell)

8.0.2 版中的新增内容。

除了单一相等匹配之外,$lookup在 Amazon DocumentDB 中,还可以在加入的集合上运行聚合管道。这支持不关联的子查询,前者为每个输入文档运行相同的子查询,也支持关联子查询,后者使用从每个输入文档的字段中定义的变量来返回与该文档相关的结果。

语法

要运行关联子查询,请使用输入文档的字段定义变量let,然后分阶段引用它们:pipeline

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

参数

  • let:定义输入文档字段中的变量的文档。要在流水线阶段引用变量,请使用"$$<variable>"语法。中的阶段可以访问变量pipeline,包括嵌套在中的其他$lookup阶段pipeline。$match阶段需要使用$expr运算符来访问变量。省略let运行不相关的子查询。

  • pipeline:在集合上运行的聚from合管道。管道无法直接访问输入文档的字段;改为使用变量定义变量let并引用变量。要返回所有文档,请指定一个空管道[]。

放置在$match阶段内运算符中的$eq$gt$gte$lt、、、和$lte比较$expr运算符可以在将连接from集合的字段与let变量或常量进行比较时使用集合上的索引。

创建示例文档

db.customers.insertMany([ { _id: 1, name: "Alice", min_total: 60 }, { _id: 2, name: "Bob", min_total: 50 }, { _id: 3, name: "Charlie", min_total: 20 } ]); db.orders.insertMany([ { _id: 1, customer_id: 1, total: 50 }, { _id: 2, customer_id: 1, total: 100 }, { _id: 3, customer_id: 2, total: 75 } ]);

查询示例

以下示例将每位客户与其订单连接起来,并仅保留大total于或等于该客户自身min_total价值的订单。联接条件和每位客户筛选器都是内部$expr的参考let变量:

db.customers.aggregate([ { $lookup: { from: "orders", let: { customer_id: "$_id", min_total: "$min_total" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: [ "$customer_id", "$$customer_id" ] }, { $gte: [ "$total", "$$min_total" ] } ] } } }, { $project: { _id: 0, total: 1 } } ], as: "qualifying_orders" } } ]);

输出

[ { _id: 1, name: 'Alice', min_total: 60, qualifying_orders: [ { total: 100 } ] }, { _id: 2, name: 'Bob', min_total: 50, qualifying_orders: [ { total: 75 } ] }, { _id: 3, name: 'Charlie', min_total: 20, qualifying_orders: [] } ]

对于每个客户,子查询是根据该客户的_id和min_total价值进行评估的,因此每个文档都会收到自己的相关结果。

使用简洁语法关联子查询(MongoDB Shell)

8.0.2 版中的新增内容。

Amazon DocumentDB 还支持简洁的相关子查询语法,该语法将等式匹配与相结合。localField foreignField pipeline简洁的语法消除了在$match阶段中表达$expr运算符内部等式匹配的要求。

语法

{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, let: { <var_1>: <expression>, ..., <var_n>: <expression> }, pipeline: [ <pipeline to run> ], as: <output array field> } }

查询示例

以下示例返回的结果与前面的相关子查询示例相同。_id和customer_id之间的等式匹配用localField和foreignField代替$expr条件来表示:

db.customers.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "customer_id", let: { min_total: "$min_total" }, pipeline: [ { $match: { $expr: { $gte: [ "$total", "$$min_total" ] } } }, { $project: { _id: 0, total: 1 } } ], as: "qualifying_orders" } } ]);

输出

[ { _id: 1, name: 'Alice', min_total: 60, qualifying_orders: [ { total: 100 } ] }, { _id: 2, name: 'Bob', min_total: 50, qualifying_orders: [ { total: 75 } ] }, { _id: 3, name: 'Charlie', min_total: 20, qualifying_orders: [] } ]

相关子查询中支持的阶段

当$lookup管道使用时let,以下阶段可以引用"$$<variable>"变量:

  • $match(使用运$expr算符)

  • $project

  • $addFields

  • $set

  • $group

  • $bucket

  • $bucketAuto

  • $sortByCount

  • $lookup(嵌套)

  • $graphLookup

不计算变量表达式的阶段(例如$sort、$skip、$limit、$count、$sample、$unwind$unset、和)也可以在管道中使用,但不能引用变量。

使用let:$facet、、、、、、$redact$replaceRoot$replaceWith$geoNear$setWindowFields$fill、和 $match with 的$lookup管道不支持以下阶段$text。

限制

  • pipeline不能包括$out或$merge阶段。

代码示例

要查看使用该$lookup命令的代码示例,请选择要使用的语言的选项卡:

Node.js
const { MongoClient } = require('mongodb'); async function example() { const client = await MongoClient.connect('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false'); await client.connect(); const db = client.db('test'); const result = await db.collection('customers').aggregate([ { $lookup: { from: 'orders', localField: '_id', foreignField: 'customer_id', as: 'orders' } } ]).toArray(); console.log(JSON.stringify(result, null, 2)); await client.close(); } example();
Python
from pymongo import MongoClient def example(): client = MongoClient('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false') db = client.test collection = db.customers pipeline = [ { "$lookup": { "from": "orders", "localField": "_id", "foreignField": "customer_id", "as": "orders" } } ] result = collection.aggregate(pipeline) for doc in result: print(doc) client.close() example()

相关的子查询代码示例

要查看将$lookup阶段与相关子查询(使用let和pipeline)一起使用的代码示例,请选择要使用的语言的选项卡:

Node.js
const { MongoClient } = require('mongodb'); async function example() { const client = await MongoClient.connect('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false'); const db = client.db('test'); const customers = db.collection('customers'); await customers.insertMany([ { _id: 1, name: "Alice", min_total: 60 }, { _id: 2, name: "Bob", min_total: 50 }, { _id: 3, name: "Charlie", min_total: 20 } ]); const orders = db.collection('orders'); await orders.insertMany([ { _id: 1, customer_id: 1, total: 50 }, { _id: 2, customer_id: 1, total: 100 }, { _id: 3, customer_id: 2, total: 75 } ]); // Correlated subquery: filter each customer's orders with a per-customer threshold const result = await customers.aggregate([ { $lookup: { from: 'orders', localField: '_id', foreignField: 'customer_id', let: { min_total: '$min_total' }, pipeline: [ { $match: { $expr: { $gte: [ '$total', '$$min_total' ] } } }, { $project: { _id: 0, total: 1 } } ], as: 'qualifying_orders' } } ]).toArray(); console.log(JSON.stringify(result, null, 2)); await client.close(); } example();
Python
from pymongo import MongoClient def example(): client = MongoClient('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false') db = client.test customers = db.customers customers.insert_many([ { "_id": 1, "name": "Alice", "min_total": 60 }, { "_id": 2, "name": "Bob", "min_total": 50 }, { "_id": 3, "name": "Charlie", "min_total": 20 } ]) orders = db.orders orders.insert_many([ { "_id": 1, "customer_id": 1, "total": 50 }, { "_id": 2, "customer_id": 1, "total": 100 }, { "_id": 3, "customer_id": 2, "total": 75 } ]) # Correlated subquery: filter each customer's orders with a per-customer threshold pipeline = [ { "$lookup": { "from": "orders", "localField": "_id", "foreignField": "customer_id", "let": { "min_total": "$min_total" }, "pipeline": [ { "$match": { "$expr": { "$gte": [ "$total", "$$min_total" ] } } }, { "$project": { "_id": 0, "total": 1 } } ], "as": "qualifying_orders" } } ] result = customers.aggregate(pipeline) for doc in result: print(doc) client.close() example()