MongoDB(35)聚合操作的常见阶段有哪些?

17 阅读2分钟

在MongoDB中,聚合操作通过管道的方式将多个阶段(stages)串联在一起,每个阶段对文档进行特定的处理。以下是聚合操作中常见的阶段及其详细解释,每个阶段还将附带代码示例。

常见的聚合阶段

  1. $match:过滤文档,条件类似于 find 查询。
  2. $group:将文档分组,并可进行计算(如求和、平均值、计数等)。
  3. $sort:对文档进行排序。
  4. $project:重新定义文档结构,可用于选择、计算和重命名字段。
  5. $limit:限制返回的文档数量。
  6. $skip:跳过指定数量的文档。
  7. $unwind:将数组字段拆分成独立的文档。
  8. $lookup:进行集合间的连接操作。
  9. $addFields:添加新的字段到文档中。
  10. $replaceRoot:替换输入文档为指定的字段。

代码示例

以下是使用Node.js进行各个阶段的代码示例。首先确保安装了MongoDB的Node.js驱动:

npm install mongodb

插入示例数据

const { MongoClient } = require('mongodb');

async function insertData() {
    const uri = "mongodb://localhost:27017";
    const client = new MongoClient(uri, { useUnifiedTopology: true });

    try {
        await client.connect();
        const db = client.db('myDatabase');
        const collection = db.collection('orders');

        await collection.deleteMany({}); // 清空集合

        await collection.insertMany([
            { customerId: 1, amount: 100, status: "shipped", items: [{ product: "A", qty: 2 }, { product: "B", qty: 1 }] },
            { customerId: 1, amount: 200, status: "pending", items: [{ product: "B", qty: 4 }] },
            { customerId: 2, amount: 150, status: "shipped", items: [{ product: "A", qty: 1 }] },
            { customerId: 2, amount: 50, status: "pending", items: [{ product: "C", qty: 2 }] },
            { customerId: 3, amount: 250, status: "shipped", items: [{ product: "D", qty: 3 }] }
        ]);

        console.log("Data inserted");
    } finally {
        await client.close();
    }
}

insertData().catch(console.error);

使用聚合管道

async function aggregateData() {
    const uri = "mongodb://localhost:27017";
    const client = new MongoClient(uri, { useUnifiedTopology: true });

    try {
        await client.connect();
        const db = client.db('myDatabase');
        const collection = db.collection('orders');

        // $match
        console.log("\n$match stage:");
        let result = await collection.aggregate([
            { $match: { status: "shipped" } }
        ]).toArray();
        console.log(result);

        // $group
        console.log("\n$group stage:");
        result = await collection.aggregate([
            { $group: { _id: "$customerId", totalAmount: { $sum: "$amount" } } }
        ]).toArray();
        console.log(result);

        // $sort
        console.log("\n$sort stage:");
        result = await collection.aggregate([
            { $sort: { amount: -1 } }
        ]).toArray();
        console.log(result);

        // $project
        console.log("\n$project stage:");
        result = await collection.aggregate([
            { $project: { customerId: 1, amount: 1, status: 1, _id: 0 } }
        ]).toArray();
        console.log(result);

        // $limit
        console.log("\n$limit stage:");
        result = await collection.aggregate([
            { $limit: 2 }
        ]).toArray();
        console.log(result);

        // $skip
        console.log("\n$skip stage:");
        result = await collection.aggregate([
            { $skip: 2 }
        ]).toArray();
        console.log(result);

        // $unwind
        console.log("\n$unwind stage:");
        result = await collection.aggregate([
            { $unwind: "$items" }
        ]).toArray();
        console.log(result);

        // $lookup
        await db.collection('customers').deleteMany({}); // 清空集合
        await db.collection('customers').insertMany([
            { _id: 1, name: "Customer 1" },
            { _id: 2, name: "Customer 2" },
            { _id: 3, name: "Customer 3" }
        ]);

        console.log("\n$lookup stage:");
        result = await collection.aggregate([
            {
                $lookup: {
                    from: "customers",
                    localField: "customerId",
                    foreignField: "_id",
                    as: "customerDetails"
                }
            }
        ]).toArray();
        console.log(result);

        // $addFields
        console.log("\n$addFields stage:");
        result = await collection.aggregate([
            { $addFields: { discount: { $cond: { if: { $gte: ["$amount", 200] }, then: 20, else: 0 } } } }
        ]).toArray();
        console.log(result);

        // $replaceRoot
        console.log("\n$replaceRoot stage:");
        result = await collection.aggregate([
            { $replaceRoot: { newRoot: { customerId: "$customerId", totalAmount: "$amount" } } }
        ]).toArray();
        console.log(result);

    } finally {
        await client.close();
    }
}

aggregateData().catch(console.error);

在这段代码中,我们演示了如何使用聚合管道的各个阶段来处理数据:

  1. $match:过滤出 statusshipped 的订单。
  2. $group:按 customerId 分组并计算总金额。
  3. $sort:按 amount 字段降序排序。
  4. $project:选择 customerIdamountstatus 字段,排除 _id 字段。
  5. $limit:限制返回的文档数量为2。
  6. $skip:跳过前两个文档。
  7. $unwind:将 items 数组拆分成独立的文档。
  8. $lookup:连接 customers 集合,添加 customerDetails 字段。
  9. $addFields:添加新的字段 discount,根据 amount 的值设置折扣。
  10. $replaceRoot:用新的根字段替换文档的根。

总结

聚合管道是MongoDB中非常强大的工具,能够通过多个阶段对数据进行复杂的处理和转换。通过上述代码示例,你可以了解如何在Node.js中使用聚合管道的常见阶段,来实现数据的过滤、分组、排序、投影、限制、跳过、拆分、连接、添加字段和替换根等操作。这些阶段提供了丰富的功能,能够满足各种复杂的数据处理需求。