Javascript 如何加快MongoDB视图的计数

Javascript 如何加快MongoDB视图的计数,javascript,mongodb,aggregation-framework,Javascript,Mongodb,Aggregation Framework,我一直在解决为什么我创建的MongoDB视图如此缓慢的问题。该视图以交易集合为目标,并返回openBalance大于0的记录。我还运行了一些额外的聚合阶段,以按照我想要的方式对数据进行塑形 为了加快视图的执行速度,它通过在视图聚合管道的第一阶段中的索引字段上进行匹配来使用目标集合上的索引,如下所示: // View Stage 1 { "transactions.details.openBalance" : { "$exists" : true, "$gt" : 0.0 } } 经过大量调查

我一直在解决为什么我创建的MongoDB视图如此缓慢的问题。该视图以
交易
集合为目标,并返回
openBalance
大于
0
的记录。我还运行了一些额外的聚合阶段,以按照我想要的方式对数据进行塑形

为了加快视图的执行速度,它通过在视图聚合管道的第一阶段中的索引字段上进行匹配来使用目标集合上的索引,如下所示:

// View Stage 1

{ "transactions.details.openBalance" : { "$exists" : true, "$gt" : 0.0 } }
经过大量调查,我确定视图中的聚合返回数据的速度非常快。慢的是作为端点一部分运行的计数:

let count = await db.collection('view_transactions_report').find().count();
所以我现在想弄清楚的是,为什么视图上的计数比底层集合上的计数慢得多,以及我能做些什么来加速它。或者,也许有另一种生成计数的方法

基础集合大约有800000条记录,但计数很快就会返回。但是视图上的计数返回的速度要慢得多,它只返回一组经过过滤的10000条初始800000条记录。具体来说,我指的是集合计数返回的时间为3/4秒,而mongo视图计数返回的时间为6秒

那么,首先,为什么视图上的计数(数据集要小得多)比基础集合上的计数慢得多,其次,我可以做些什么来解决视图计数的速度问题

我正在运行另外两个聚合查询,以确定运行缓慢的
totalCustomers
totalOpenBalance
(请参见下面的代码)

我的端点函数代码的相关部分如下所示:

// previous code

  let count = await db.collection('view_transaction_report').find(search).count();

  let totalCustomers = await db.collection('view_transaction_report').find(search).count({
     $sum: "customer._id"
   });

  let result = {};

  if (totalCustomers > 0) {
    result = await db.collection('view_transaction_report').aggregate([{
        $match: search,
      },
      {
        $group: {
          _id: null,
          totalOpenBalance: {
            $sum: '$lastTransaction.details.openBalance'
          }
        }
      }
    ]).next();
  }

  db.collection('view_transaction_report').find(search).skip(skip).limit(pagesize).forEach(function (doc) {
    docs.push(doc);
  }, function (err) {
    if (err) {
      if (!ioOnly) {
        return next(err);
      } else {
        return res(err);
      }
    }
    if (ioOnly) {
      res({
        sessionId: sessID,
        count: count,
        data: docs,
        totalCustomers: totalCustomers,
        totalOpenBalance: result.totalOpenBalance
      });
    } else {
      res.send({
        count: count,
        data: docs,
        totalCustomers: totalCustomers,
        totalOpenBalance: result.totalOpenBalance
      });
    }
  });
关于
executionStats
,这是生成视图的
queryPlanner
部分显示的内容:

            "queryPlanner" : {
                "plannerVersion" : 1.0, 
                "namespace" : "vio.transactions", 
                "indexFilterSet" : false, 
                "parsedQuery" : {
                    "$and" : [
                        {
                            "transactions.details.openBalance" : {
                                "$gt" : 0.0
                            }
                        }, 
                        {
                            "transactions.destails.openBalance" : {
                                "$exists" : true
                            }
                        }
                    ]
                }, 
                "winningPlan" : {
                    "stage" : "CACHED_PLAN", 
                    "inputStage" : {
                        "stage" : "FETCH", 
                        "filter" : {
                            "transactions.details.openBalance" : {
                                "$exists" : true
                            }
                        }, 
                        "inputStage" : {
                            "stage" : "IXSCAN", 
                            "keyPattern" : {
                                "transactions.details.openBalance" : 1.0
                            }, 
                            "indexName" : "openBalance", 
                            "isMultiKey" : true, 
                            "multiKeyPaths" : {
                                "transactions.details.openBalance" : [
                                    "transactions", 
                                    "transactions.details"
                                ]
                            }, 
                            "isUnique" : false, 
                            "isSparse" : true, 
                            "isPartial" : false, 
                            "indexVersion" : 2.0, 
                            "direction" : "forward", 
                            "indexBounds" : {
                                "transactions.details.openBalance" : [
                                    "(0.0, inf.0]"
                                ]
                            }
                        }
                    }
                }, 
                "rejectedPlans" : [

                ]
            }

在评论中,@Wan Bachtiar提到“openBalance”看起来是一个很好的例子。为了澄清,是的,在目标集合中,“openBalance”字段是数组中的嵌入字段。即使在视图中,数据的形状使得“openBalance”是一个不在数组中的嵌入式字段,情况也是如此

目标集合上的多键索引是问题所在,因为Mongo需要查看与此“openBalance”字段相关的每个数组元素,而不是1对1文档的情况,从逻辑上讲,这大大增加了扫描时间-因为有时有很多,与此特定字段相关的许多数组元素


经过进一步检查,我意识到我可以通过改变如何通过ETL将“openBalance”填充到mongo集合来解决这个问题。通过进行此更改,我将能够使“openBalance”成为一个标准索引,而不是一个多键索引,这反过来将允许mongo搜索更小的数据集以返回我的计数。

再次澄清,缓慢的是“计数”,以及“totalCustomers”和“totalOpenBalance”的聚合查询。这看起来类似吗?你能展示一下
search
的样子吗?不熟悉node.js api。我很好奇这是如何变成聚合查询的。发布原始计数表达式,以及文档结构的示例。似乎您在一个嵌入式字段
openBalance
上进行了排序。很高兴您最后对其进行了排序。干杯