mongodb汇总按分组字段排序

时间:2018-10-31 16:52:50

标签: mongodb aggregation-framework

MongoDB 4.0。

这是数据集(sales-aggregate-test.js):

use Test123;

const HOW_MANY_PRODUCTS = 1000
const HOW_MANY_SALES_PER_PRODUCT = 50

for(let i = 0; i < HOW_MANY_PRODUCTS; i++) {
  const productNumber = (i + 10001)
  const productId = '5bd9d139d96b8fce000' + productNumber
  db.getCollection('products').insert({
    _id: ObjectId(productId),
    title: 'Product ' + productNumber,
  })

  for(let j = 0; j < HOW_MANY_SALES_PER_PRODUCT; j++) {
    const saleNumber = (j + 10001)
    const saleId = '5bd9d139d96b8f' + productNumber + saleNumber
    db.getCollection('sales').insert({
      _id: ObjectId(saleId),
      product: ObjectId(productId),
      quantity: i + j + 1,
    })
  }
}

插入:mongo < ./sales-aggregate-test.js

现在这是查询(sales-aggregate-test-actual-query.js):

use Test123;

db.getCollection('sales').aggregate(
  [
    {
      $sort: { product: 1, remoteVariantId: 1, quantity: -1, }
    },

    {
      $lookup: {
        from:               'products',
        localField:         'product',
        foreignField:        '_id',
        as:                 'productModel',
      }
    },

    {
      $unwind: '$productModel'
    },

    {
      $match: {
        'productModel.archived': { $ne: true }
      }
    },

    {
      $project: {
        product: 1,
        quantity: 1,
      }
    },

    //{ $limit: 10 },

    {
      $group: {
        _id: '$product',

        saleModelsCount: { $sum: 1 },

        quantity : { $sum: '$quantity' },
      }
    },

    {
      $sort: { quantity: -1, }
    },
    { $limit: 3  },
  ]
  // ,{ allowDiskUse: true }
)

要达到什么目的?更快地获取它:

{ "_id" : ObjectId("5bd9d139d96b8fce00011000"), "saleModelsCount" : 50, "quantity" : 51225 }
{ "_id" : ObjectId("5bd9d139d96b8fce00010999"), "saleModelsCount" : 50, "quantity" : 51175 }
{ "_id" : ObjectId("5bd9d139d96b8fce00010998"), "saleModelsCount" : 50, "quantity" : 51125 }

这基本上是:给我最畅销的产品。由于销售包括数量,因此我需要先按数量对它们进行分组,然后再进行排序。

现在在此测试数据集上,它是“快速”的-仅需2.5秒。问题在于真实的数据集,其中的产品模型更大,涉及的因素更多(例如销售模型中的“价格”字段)。

该问题似乎是由最后的$group$sort阶段引起的。快速注释掉两个返回值。仅注释掉一个会使查询变慢。

如何使其更快?开放征求意见-也可以采用其他方法。

1 个答案:

答案 0 :(得分:1)

对您可能有用的一些想法:

首先,您可以摆脱第一个$r = new \ReflectionObject($pdf); $p = $r->getProperty('fgcolor'); $p->setAccessible(true); $textColor = $p->getValue($pdf); ,因为在最后一个管道阶段还有另一个$sort,并且可以保证顺序正确。

几乎没有办法替换$lookup + $unwind + $match + $project + $group

您可以将$addFields$filter结合使用,以过滤掉$unwind之前的某些元素:

{
    $lookup: {
        from: 'products',
        localField: 'product',
        foreignField: '_id',
        as: 'productModel',
    }
},

{
    $addFields: {
        productModel: {
            $filter: {
                input: '$productModel',
                as: 'model',
                cond: { $ne: [ '$$model.archived', true ] }
            }
        }
    }
},

{
    $unwind: '$productModel'
}

在这种情况下,您可以删除$match,因为此操作是在嵌套数组中执行的。

第二种方法可能是使用$lookup with custom pipeline,以便您可以在$lookup内执行此附加过滤:

{
    $lookup: {
        from:   'products',
        let: { productId: "$product" },
        pipeline: [
            {
                $match: { $expr: { $and: [ { $eq: [ "$$productId", "$_id" ] }, { $ne: [ "$archived", true ] } ] } }
            }
        ],
        as: 'productModel',
    }
}

在这两种情况下,作为另一种优化,您都不需要$unwind,因为您过滤了productModel数组,然后只需修改$group

{
    $group: {
        _id: '$product',
        saleModelsCount: { $sum: { $size: "$productModel" } },
        quantity : { $sum: '$quantity' },
    }
}