如何在MongoDB中使用$lookup
MongoDB是一种流行的NoSQL数据库,它将数据存储在集合中。MongoDB集合由一个或多个包含实际数据的文档组成,这些文档以JSON格式存储数据。文档类似于传统关系型SQL数据库中的行,而集合类似于表。
数据库中的一个关键功能是能够查询存储在数据库中的数据。查询数据允许检索特定信息,data analysis,进行数据报告,以及数据集成。
要能够有效地查询数据库,能够将多个表中的数据(在SQL数据库的情况下)或多个集合(NOSQL databases)中的数据组合成一个结果集是至关重要的。
在MongoDB中,$lookup用于在查询时组合来自两个集合的信息。它执行的是在SQL数据库中的左外连接的等效操作。
$lookup的用途和目标
数据库的一个重要功能是对原始数据进行处理,以获取有意义的信息。
例如,如果您经营一家餐厅生意,您可能想要分析您餐厅的数据,以找出每天的收入情况,周末哪些食物需求量大,或者甚至了解每天每小时卖出多少杯咖啡。
对于此类需求,简单的数据库查询是不够的。您需要对存储的数据进行高级查询。为了满足此类需求,MongoDB具有一个称为聚合管道的功能。
聚合管道是一个由可组合操作(称为阶段)组成的系统,用于处理数据以生成最终的聚合结果。聚合管道中的阶段示例包括$sort、$match、$group、$merge、$count和$lookup等。
这些阶段可以以任何顺序应用于聚合管道中。在聚合管道的每个阶段,对通过聚合管道传递的数据执行不同的操作。
$lookup因此是MongoDB聚合管道中的一个阶段。$lookup用于在MongoDB数据库中的两个集合之间执行左外连接。左外连接将左侧的所有文档或条目与右侧具有匹配的文档或条目组合在一起。
例如,考虑下面的两个集合,为了更容易理解,将其表示为表格格式:
orders_collection:
order_id | customer_id | order_date | total_amount |
---|---|---|---|
1 | 100 | 2022-05-01 | 50.00 |
2 | 101 | 2022-05-02 | 75.00 |
3 | 102 | 2022-05-03 | 100.00 |
customers_collection:
customer_num | customer_name | customer_email | customer_phone |
---|---|---|---|
100 | John Doe | [email protected] | 555-1234 |
102 | Jane Smith | [email protected] | 555-5678 |
如果我们使用出现在order_collection中的customer_id字段,在上述集合上执行左外连接,其中order_collection为左侧集合,customers_collection为右侧集合,结果将包含Orders Collection中的所有文档以及Customers Collection中具有与Orders Collection中任何记录的customer_id匹配的customer_num的文档。
在以表格格式表示时,订单和顾客集合之间的左外连接操作的最终结果如下所示:
请注意,对于Orders Collection中的customer_id为101的客户,其在Customers Collection中没有匹配的customer_num值,因此从客户表中填充了空值。
$lookup在字段之间执行严格的相等比较,并检索匹配的整个文档,而不仅仅是匹配的字段。
$lookup语法
$lookup的语法如下:
{
$lookup:
{
from: ,
localField: ,
foreignField: ,
as:
}
}
$lookup有四个参数:
- from – 表示我们要查找文档的集合。在我们之前的示例中使用的是orders_collection和customers_collection,我们将把customers_collection作为from集合。
- localField – 这是我们在工作或主要集合中用来与from集合(在我们的例子中是customers_collection)中的字段进行比较的字段。在上面的示例中,localField将是在orders_collection中找到的customer_id。
- foreignField – 这是我们想要在我们在中指定的集合中进行比较的字段。在我们的示例中,这将是在中找到的customer_num。
- as – 这是我们指定的一个新字段名,用来表示将显示在我们的文档中的字段,该字段包含从localField和foreignField之间的匹配结果生成的文档。所有这些匹配都放在这个字段的数组中。如果没有匹配,该字段将包含一个空数组。
从我们之前的两个集合中,我们将使用以下代码在两个集合上执行$lookup操作,以orders_collection作为我们的工作或主要集合。
{
$lookup: {
from: "customers_collection",
localField: "customer_id",
foreignField: "customer_num",
as: "customer_info"
}
请注意,as字段可以是任何字符串值。但是,如果给它一个已经存在于工作文档中的名称,该字段将被覆盖。
从多个集合中联接数据
MongoDB的$lookup是MongoDB聚合管道中的一个有用阶段。虽然MongoDB的聚合管道不一定必须有一个$lookup阶段,但是在执行需要跨多个集合联接数据的复杂查询时,该阶段非常关键。
$lookup阶段在两个集合上执行左外连接,从而创建一个新字段,或者使用来自另一个集合的文档创建一个现有字段的值。
这些文档是根据它们是否具有与要比较的字段的值匹配的值而选择的。最终结果是一个包含一组文档的字段,如果找到匹配,则为空数组。
考虑下面显示的员工和项目集合。
我们可以使用以下代码来联接这两个集合:
db.projects.aggregate([
{
$lookup: {
from: "employees",
localField: "employees",
foreignField: "_id",
as: "assigned_employees"
}
}
])
这个操作的结果是两个集合的组合。结果是项目和分配给每个项目的所有员工。员工以数组的形式表示。
与$lookup一起使用的管道阶段
如前所述,$lookup是MongoDB聚合管道中的一个阶段,可以与其他聚合管道阶段一起使用。为了演示这些阶段如何与$lookup一起使用,我们将使用以下两个集合进行说明。
在MongoDB中,它们以JSON格式存储。这是MongoDB中上述集合的样子。
可以与$lookup一起使用的聚合管道阶段的一些示例包括:
$match
$match是用于筛选文档流的聚合管道阶段,只允许满足给定条件的文档继续进行聚合管道中的下一个阶段。这个阶段最好早期使用,以删除不需要的文档,从而优化聚合管道。
使用之前的两个集合,可以这样组合$match和$lookup:
db.users.aggregate([
{
$match: {
country: "USA"
}
},
{
$lookup: {
from: "orders",
localField: "_id",
foreignField: "user_id",
as: "orders"
}
}
])
$match用于筛选来自美国的用户。然后将$match 的结果与$lookup结合起来,以获取来自美国用户的订单详细信息。上述操作的结果如下所示:
$project
$project是一个用于重新定义文档结构的阶段,可以指定要包含、排除或添加到文档中的字段。例如,如果您处理的每个文档都有十个字段,但只有四个字段包含您需要用于数据处理的数据,您可以使用$project 来过滤掉您不需要的字段。
这样可以避免将不必要的数据发送到聚合管道的下一个阶段。
我们可以这样组合$lookup和$project:
db.users.aggregate([
{
$lookup: {
from: "orders",
localField: "_id",
foreignField: "user_id",
as: "orders"
}
},
{
$project: {
name: 1,
_id: 0,
total_spent: { $sum: "$orders.price" }
}
}
])
上述操作结合了users和orders集合,使用$lookup,然后使用$project仅显示每个用户的名称和每个用户的支出金额。$project还用于从结果中移除_id字段。上述操作的结果如下所示:
$unwind
$unwind是一个聚合阶段,用于展开数组字段,为数组中的每个元素创建新的文档。这在您想在数组字段值上运行一些聚合时非常有用。
例如,下面的示例中,如果您想在hobbies字段上运行聚合,您无法这样做,因为它是一个数组。但是,您可以使用$unwind将其展开,然后在结果文档上执行聚合。
使用users和orders集合,我们可以使用$lookup和$unwind一起使用,像这样:
db.users.aggregate([
{
$lookup: {
from: "orders",
localField: "_id",
foreignField: "user_id",
as: "orders"
}
},
{
$unwind: "$orders"
}
])
在上面的代码中,$lookup返回一个名为orders的数组字段。然后使用$unwind展开数组字段。此操作的结果如下所示:请注意,Alice出现两次,因为她有两个订单。
$lookup用例示例
在进行数据处理时,$lookup是一个有用的工具。例如,您可能有两个集合,您希望根据包含相似数据的集合上的字段进行连接。可以使用简单的$lookup阶段来实现这一点,并在主要集合中添加一个来自另一个集合的文档的新字段。
考虑下面显示的users和orders集合:
可以使用$lookup将两个集合合并,得到下面显示的结果:
$lookup也可以用于执行更复杂的连接。$lookup不仅仅限于在两个集合之间进行连接。您可以使用多个$lookup阶段在两个以上的集合上执行连接。考虑下面显示的三个集合:
我们可以使用下面的代码在三个集合之间执行更复杂的连接,以获取所有已下订单的订单以及所订购产品的详细信息。
下面的代码允许我们完成这个操作:
db.orders.aggregate([
{
$lookup: {
from: "order_items",
localField: "_id",
foreignField: "order_id",
as: "order_items"
}
},
{
$unwind: "$order_items"
},
{
$lookup: {
from: "products",
localField: "order_items.product_id",
foreignField: "_id",
as: "product_details"
}
},
{
$group: {
_id: "$_id",
customer: { $first: "$customer" },
total: { $sum: "$order_items.price" },
products: { $push: "$product_details" }
}
}
])
上述操作的结果如下所示:
结论
在涉及多个集合的数据处理中,$lookup非常有用,因为它可以让您连接数据并根据存储在多个集合中的数据得出结论。数据处理很少仅依赖于一个集合。
为了从数据中得出有意义的结论,跨多个集合连接数据是关键步骤。因此,考虑在MongoDB聚合管道中使用$lookup阶段,以便更好地处理数据并从存储在多个集合中的原始数据中得出有意义的见解。
您还可以探索一些MongoDB commands and queries。