首页 > 解决方案 > MongoDB 聚合管道的操作超时

问题描述

我在 MongoDB Atlas 上有一个 MongodDB 数据库。

它有“orders”、“products”、“itemTypes”和“brands”。

如果我汇总订单 + 产品 + itemTypes 就可以了:

[{
    $unwind: {
        path: '$orders'
    }
}, {
    $lookup: {
        from: 'products',
        localField: 'orders.productId',
        foreignField: 'productId',
        as: 'products'
    }
}, {
    $lookup: {
        from: 'itemTypes',
        localField: 'products.typeId',
        foreignField: 'typeId',
        as: 'itemTypes'
    }
}, {
    $set: {
        'orders.price': {
            $arrayElemAt: ['$products.price', 0]
        },
        'orders.brandId': {
            $arrayElemAt: ['$products.brandId', 0]
        },
        'orders.typeId': {
            $arrayElemAt: ['$products.typeId', 0]
        },
        'orders.typeName': {
            $arrayElemAt: ['$itemTypes.name', 0]
        }
    }
}, {
    $group: {
        _id: '$_id',
        createdAt: {
            $first: '$createdAt'
        },
        status: {
            $first: '$status'
        },
        retailerId: {
            $first: '$retailerId'
        },
        retailerName: {
            $first: '$retailerName'
        },
        orderId: {
            $first: '$orderId'
        },
        orders: {
            $push: '$orders'
        }
    }
}]

如果我聚合订单 + 产品 + itemTypes + 品牌,无论是 Mongo Compass 还是 Mongo Atlas 聚合构建器的 Web UI 都会出现操作超时错误。

[{
    $unwind: {
        path: '$orders'
    }
}, {
    $lookup: {
        from: 'products',
        localField: 'orders.productId',
        foreignField: 'productId',
        as: 'products'
    }
}, {
    $lookup: {
        from: 'itemTypes',
        localField: 'products.typeId',
        foreignField: 'typeId',
        as: 'itemTypes'
    }
}, {
    $lookup: {
        from: 'brands',
        localField: 'products.brandId',
        foreignField: 'brandId',
        as: 'brands'
    }
}, {
    $set: {
        'orders.price': {
            $arrayElemAt: ['$products.price', 0]
        },
        'orders.brandId': {
            $arrayElemAt: ['$products.brandId', 0]
        },
        'orders.typeId': {
            $arrayElemAt: ['$products.typeId', 0]
        },
        'orders.typeName': {
            $arrayElemAt: ['$itemTypes.name', 0]
        },
        'orders.brandName': {
            $arrayElemAt: ['$brands.name', 0]
        }
    }
}, {
    $group: {
        _id: '$_id',
        createdAt: {
            $first: '$createdAt'
        },
        status: {
            $first: '$status'
        },
        retailerId: {
            $first: '$retailerId'
        },
        retailerName: {
            $first: '$retailerName'
        },
        orderId: {
            $first: '$orderId'
        },
        orders: {
            $push: '$orders'
        }
    }
}]

这是超时聚合的演示:

https://mongoplayground.net/p/Jj6EhSl58MS

我们有大约 50,000 个订单,14,000 种产品,200 个品牌,89 种商品类型。

无论如何要优化此聚合以使其不会超时?

P/s:我的最终目标是在 Mongodb Charts 功能中使用漂亮的图表可视化流行的品牌和项目类型。

标签: mongodbaggregationmongodb-atlas

解决方案


如果您在 Mongo Atlas 上,您可以使用 Triggers 在后台运行聚合查询 - 无论是在数据库更新时还是作为计划的触发器 ( https://docs.mongodb.com/realm/triggers/ )。

当触发器运行时,您可以使用“$merge”操作将聚合管道的结果保存在新集合中。

exports = function() {
    const mongodb = context.services.get(CLUSTER_NAME);
    const orders = mongodb.db(DATABASE_NAME).collection("orders");
    const ordersSummary = mongodb.db(DATABASE_NAME).collection("orders.summary");
    
    const pipeline = [
        {
            YOUR_PIPELINE
        },
        { $merge: { into: "orders.summary", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
    ];

    orders.aggregate(pipeline);
};

这样,您的图表将非常快,因为它们只需从新集合中进行简单查询。


推荐阅读