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
阶段引起的。快速注释掉两个返回值。仅注释掉一个会使查询变慢。
如何使其更快?开放征求意见-也可以采用其他方法。
答案 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' },
}
}