【问题标题】:$group after $lookup is taking way too long$lookup 后的 $group 花费的时间太长
【发布时间】:2018-05-15 15:13:20
【问题描述】:

我有以下 mongo 集合:

{
    "_id" : "22pTvYLd7azAAPL5T",
    "plate" : "ABC-123",
    "company": "AMZ",
    "_portfolioType" : "account"
},
{
    "_id" : "22pTvYLd7azAAPL5T",
    "plate" : "ABC-123",
    "_portfolioType" : "sale",
    "price": 87.3
},
{
    "_id" : "22pTvYLd7azAAPL5T",
    "plate" : "ABC-123",
    "_portfolioType" : "sale",
    "price": 88.9
}

我正在尝试聚合在plate 字段中具有相同值的所有文档。以下是我目前写的查询:

db.getCollection('temp').aggregate([
{
    $lookup: { 
        from: 'temp',
        let: { 'p': '$plate', 't': '$_portfolioType' },
        pipeline: [{
            '$match': {
                '_portfolioType': 'sale',
                '$expr': { '$and': [ 
                    { '$eq': [ '$plate', '$$p'  ] },
                    { '$eq': [ '$$t', 'account'  ] }
                ]}
            }
        }],
        as: 'revenues' 
    },
},
{
    $project: {
        plate: 1,
        company: 1,
        totalTrades: { $arrayElemAt: ['$revenues', 0] },
    },
},

{
    $addFields: {
        revenue: { $add: [{ $multiply: ['$totalTrades.price', 100] }, 99] },
    },
},

{
    $group: {
        _id: '$company',
        revenue: { $sum: '$revenue' },
    }
}
])

如果我删除$group 阶段,查询工作正常,但是,只要我添加$group 阶段,mongo 就会开始无限处理。我尝试添加$match 作为第一阶段,以限制要处理的文档数量,但没有任何运气。例如:

{
    $match: { $or: [{ _portfolioType: 'account' }, { _portfolioType: 'sale' }] }
},

我也尝试使用{ explain: true },但它没有返回任何有用的信息。

【问题讨论】:

  • 它“工作正常”,因为它会在第一批文档可用时立即返回一个光标。 $group 是一个阻塞阶段。它需要所有先前的阶段才能完成。 $lookup 看起来不正确。你想在那里做什么?
  • 您能否提供所需的最终结果。写“试图聚合所有在板块字段中具有相同值的文档”的语句不是很直观。
  • 好的,我的最终目标是获取每个“公司”(公司字段)的文档,其计数为 plate(即具有唯一 plate 的公司的文档数量和计算的收入字段基本上是:获取与_portfolioType: sale 相关联的所有文档plate
  • 我正要讨论索引,直到我意识到你正在做一个“自我加入”回到同一个集合。你可能不需要这样做。看看Aggregation filter after $lookup,其中“尽管有问题标题”,但该特定问题的实际解决方案(OP 也在执行“自加入”)实际上根本不涉及$lookup。您可能会问一些非常相似的问题,如果您不确定,那么您应该按照要求显示一些文件和预期结果。

标签: mongodb aggregation-framework


【解决方案1】:

正如 Neil Lunn 所注意到的,您很可能不需要查找来达到您的“最终目标”,这仍然很模糊。

请阅读cmets并根据需要进行调整:

db.temp.aggregate([
    {$group:{
        // Get unique plates
        _id: "$plate",
        // Not clear what you expect if there are documents with
        // different company, and the same plate.
        // Assuming "it never happens"
        // You may need to $cond it here with {$eq: ["$_portfolioType", "account"]}
        // but you never voiced it.         
        company: {$first:"$company"},
        // Not exactly all documents with _portfolioType: sale,
        // but rather price from all documents for this plate.
        // Assuming price field is available only in documents 
        // with "_portfolioType" : "sale". Otherwise add a $cond here.
        // If you really need "all documents", push $$ROOT instead.
        prices: {$push: "$price"}        
    }},
    {$project: {
       company: 1,
       // Apply your math here, or on the previous stage
       // to calculate revenue per plate
       revenue: "$prices" 
    }}
    {$group: {
        // Get document for each "company" 
        _id: "$company",
        // Revenue associated with plate
        revenuePerPlate: {$push: {"k":"$_id", "v":"$revenue"}}        
    }},
    {$project:{         
        _id: 0,
        company: "$_id",
        // Count of unique plate
        platesCnt: {$size: "$revenuePerPlate"},
        // arrayToObject if you wish plate names as properties
        revenuePerPlate: {$arrayToObject: "$revenuePerPlate"}
    }}
])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-31
    • 2011-12-06
    • 1970-01-01
    • 2015-05-02
    • 2016-12-03
    • 2011-11-18
    相关资源
    最近更新 更多