MongoDB 多表联合查询(Inner Join)入门指南

数据库中的“联合查询”通常用于同时从多个表中检索信息。在传统的关系型数据库中,使用 SQL 的 INNER JOIN 可以轻松实现。但在 MongoDB 中,由于其是无模式的文档数据库,所以并不直接支持 SQL 风格的联接。然而,我们可以通过多次查询和聚合框架(Aggregation Framework)来实现类似的功能。

本文将引导你了解如何在 MongoDB 中实现多表联合查询,主要通过 MongoDB 的聚合管道来实现。

1. 流程概述

我们将以下面的步骤来完成联合查询的过程。下面是一个简单的流程表格来描述我们要进行的步骤。

步骤 描述
1 准备 MongoDB 数据库及集合(Collection)
2 使用 lookup 实现联合查询的聚合步骤
3 执行查询,查看结果
4 在实际项目中应用联合查询的示例

2. 数据集准备

首先,我们需要做一些准备工作。我们假设有两个集合:usersorders

用户集合 (users)

[
    { "_id": 1, "name": "Alice" },
    { "_id": 2, "name": "Bob" },
    { "_id": 3, "name": "Charlie" }
]

订单集合 (orders)

[
    { "_id": 101, "userId": 1, "total": 250 },
    { "_id": 102, "userId": 1, "total": 300 },
    { "_id": 103, "userId": 2, "total": 150 }
]

使用 Mermaid 表示 ER 图

下面是两个集合的ER图,显示了它们之间的关系:

erDiagram
    USERS {
        string name
        int _id
    }
    ORDERS {
        int _id
        int userId
        float total
    }

    USERS ||--o{ ORDERS : ""

3. 使用 lookup 实现联合查询

MongoDB 的聚合框架使我们可以使用 $lookup 来手动实现类似于 SQL 的 INNER JOIN

3.1 汇总查询代码

以下是执行联合查询的代码:

db.orders.aggregate([
    {
        $lookup: {
            from: "users",          // 目标集合的名称
            localField: "userId",   // 当前集合中对应的字段 
            foreignField: "_id",     // 目标集合中对应的字段
            as: "userInfo"           // 命名结果字段
        }
    },
    {
        $unwind: "$userInfo"        // 将 `userInfo` 数组拆分成文档
    }
])

3.2 代码解析

  • db.orders.aggregate([...]): 我们在 orders 集合上调用 aggregate 方法。

  • $lookup: 这个操作符用于执行联合查询。 提供了以下选项:

    • from: 目标集合的名称,这里是 users
    • localField: 指向当前集合(orders)的字段,作为联接的关键字段,这里是 userId
    • foreignField: 指向目标集合(users)的字段,这里是 _id
    • as: 联接结果将存储在这个字段名中,这里命名为 userInfo
  • $unwind: 将 userInfo 字段的数组拆分为多个文档,变成一个用户 - 订单的映射。

4. 执行查询以及查看结果

接下来,我们可以执行以上代码,查看查询结果:

const result = db.orders.aggregate([...]).toArray();
printjson(result);

结果示例

假设你执行了查询,将会得到类似如下的结果:

[
    {
        "_id": 101,
        "userId": 1,
        "total": 250,
        "userInfo": {
            "_id": 1,
            "name": "Alice"
        }
    },
    {
        "_id": 102,
        "userId": 1,
        "total": 300,
        "userInfo": {
            "_id": 1,
            "name": "Alice"
        }
    },
    {
        "_id": 103,
        "userId": 2,
        "total": 150,
        "userInfo": {
            "_id": 2,
            "name": "Bob"
        }
    }
]

5. 实际应用示例

在实际项目中,联合查询通常用于在前端展示更复杂的数据结构,例如,显示用户的所有订单信息。在构建报告或分析时,会使用此类联合查询来分析数据关系。

5.1 结合使用框架的案例

如果你使用 Node.js 作为后端,可以通过以下方式实现:

const express = require('express');
const mongoose = require('mongoose');

const app = express();
const PORT = process.env.PORT || 3000;

// 连接到 MongoDB
mongoose.connect('mongodb://localhost:27017/mydb', { useNewUrlParser: true, useUnifiedTopology: true });

app.get('/users-orders', async (req, res) => {
    try {
        const result = await db.orders.aggregate([
            {
                $lookup: {
                    from: "users",
                    localField: "userId",
                    foreignField: "_id",
                    as: "userInfo"
                }
            },
            {
                $unwind: "$userInfo"
            }
        ]).toArray();

        res.json(result);
    } catch (error) {
        res.status(500).send(error.message);
    }
});

app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

结论

通过本文,你已经学会如何在MongoDB中实现多表联合查询(类似于 INNER JOIN 的操作)。虽然 MongoDB 不支持诸如 SQL 的直接联接方式,但通过聚合框架和 $lookup 等操作符,我们能实现复杂的数据查询需求。这为我们在实际开发中构建多维数据查询场景打下了良好的基础。

希望你能在实际项目中应用这些技能,提升你的开发能力。如果你有任何问题,请随时提问!