联表查询

在 MongoDB 中,联表查询不像关系型数据库(如 MySQL)那样通过 JOIN 语句实现,而是通过 聚合操作(Aggregation)来模拟类似的功能。MongoDB 是一个非关系型数据库,主要通过 嵌套文档引用文档 来处理复杂的数据结构。

1. 嵌套文档与引用文档的概念

  • 嵌套文档:MongoDB 支持在一个文档内嵌套其他文档,这样可以将相关的信息存储在一个文档中,避免了跨表查询。例如,在一个订单文档中,可以直接嵌套客户信息。
  • 引用文档:在 MongoDB 中,有时需要将文档通过引用(ObjectId)的方式关联,而不是直接嵌套。这样一个文档存储另一个文档的引用,类似于关系型数据库的外键。

2. 使用聚合实现联表查询:

尽管 MongoDB 没有关系型数据库那种直接的 JOIN 操作,但可以通过聚合操作中的 $lookup 来模拟 左外连接,将不同集合的数据合并到一起。


示例 1:嵌套文档查询

假设有一个 orders 集合,每个订单文档中包含了客户的信息,如下:

 1{
 2  "_id": 1,
 3  "orderId": "A123",
 4  "product": "Laptop",
 5  "customer": {
 6    "customerId": "C001",
 7    "name": "John Doe",
 8    "email": "john@example.com"
 9  }
10}

在这个例子中,客户信息直接嵌套在订单文档中。可以在查询订单时,直接访问嵌套的客户信息。

MongoDB 查询代码:

 1async function getOrderWithCustomer() {
 2  const client = await MongoClient.connect("mongodb://localhost:27017", { useNewUrlParser: true, useUnifiedTopology: true });
 3  const db = client.db("storeDB");
 4  const orders = db.collection("orders");
 5
 6  const result = await orders.find({}).toArray();
 7  console.log(result);
 8  await client.close();
 9}
10
11getOrderWithCustomer();

说明:

在这个查询中,直接从订单集合中获取数据,其中包括嵌套的客户信息。由于数据已经嵌套,因此无需进行额外的联表查询。


示例 2:引用文档查询

假设有两个集合:orderscustomersorders 集合存储订单信息,customers 集合存储客户信息。订单文档中只有客户的 customerId 引用。

  • orders 集合
1{
2  "_id": 1,
3  "orderId": "A123",
4  "product": "Laptop",
5  "customerId": "C001"
6}
  • customers 集合
1{
2  "_id": "C001",
3  "name": "John Doe",
4  "email": "john@example.com"
5}

在这个例子中,orders 集合中的 customerId 引用了 customers 集合中的文档。为了获得订单的完整信息,需要通过 $lookup 聚合操作将两个集合连接起来,类似关系型数据库中的 JOIN

MongoDB 聚合代码:

 1async function getOrderWithCustomerDetails() {
 2  const client = await MongoClient.connect("mongodb://localhost:27017", { useNewUrlParser: true, useUnifiedTopology: true });
 3  const db = client.db("storeDB");
 4  const orders = db.collection("orders");
 5
 6  const result = await orders.aggregate([
 7    {
 8      $lookup: {
 9        from: "customers",           // 引用的集合名称
10        localField: "customerId",     // 当前集合的字段(引用字段)
11        foreignField: "_id",          // 目标集合的字段(被引用的字段)
12        as: "customerDetails"         // 将连接结果存放到一个新的字段中
13      }
14    }
15  ]).toArray();
16
17  console.log(result);
18  await client.close();
19}
20
21getOrderWithCustomerDetails();

说明:

  • $lookup:这个操作符用于将两个集合的数据连接起来。它的功能类似 SQL 中的 JOIN,用于从目标集合(customers)中获取与源集合(orders)相关联的文档。
    • from:目标集合的名称,这里是 customers
    • localField:源集合中要匹配的字段,这里是 customerId
    • foreignField:目标集合中用于匹配的字段,这里是 customers 集合的 _id 字段。
    • as:指定新字段的名称,结果将存储在 customerDetails 字段中。

执行上述代码后,你会看到订单信息与客户详细信息合并后的结果。例如:

 1[
 2  {
 3    "_id": 1,
 4    "orderId": "A123",
 5    "product": "Laptop",
 6    "customerId": "C001",
 7    "customerDetails": [
 8      {
 9        "_id": "C001",
10        "name": "John Doe",
11        "email": "john@example.com"
12      }
13    ]
14  }
15]

示例 3:多级联表查询

如果有更多的集合需要连接(例如,orders -> customers -> products),也可以通过多个 $lookup 来实现多级联表查询。

假设在 orders 集合中也存储了产品的 productId,并且有一个 products 集合记录了产品的详细信息。

MongoDB 多级联表代码示例:

 1async function getOrderWithCustomerAndProduct() {
 2  const client = await MongoClient.connect("mongodb://localhost:27017", { useNewUrlParser: true, useUnifiedTopology: true });
 3  const db = client.db("storeDB");
 4  const orders = db.collection("orders");
 5
 6  const result = await orders.aggregate([
 7    {
 8      $lookup: {
 9        from: "customers",           // 第一个连接:从 customers 集合获取客户信息
10        localField: "customerId",
11        foreignField: "_id",
12        as: "customerDetails"
13      }
14    },
15    {
16      $lookup: {
17        from: "products",            // 第二个连接:从 products 集合获取产品信息
18        localField: "productId",
19        foreignField: "_id",
20        as: "productDetails"
21      }
22    }
23  ]).toArray();
24
25  console.log(result);
26  await client.close();
27}
28
29getOrderWithCustomerAndProduct();

说明:

  • 在上述代码中,使用了两个 $lookup 操作符,首先将 orders 集合与 customers 集合连接,接着将 orders 集合与 products 集合连接,形成多级联表查询。

总结:

  1. 嵌套文档:适合存储关联较紧密的数据,查询时不需要联表,可以直接嵌套在一个文档内。
  2. 引用文档:适合存储关联较松散的数据,通过 ObjectId 引用其他集合中的数据。使用 $lookup 聚合操作可以模拟类似 SQL 中的 JOIN 操作来联接不同集合的数据。
  3. 聚合管道中的 $lookup:允许在不同集合之间进行联表查询,支持左外连接的功能。

通过理解 嵌套文档引用文档,以及如何使用 聚合管道 实现联表查询,可以更好地应对 MongoDB 中复杂数据结构的处理。