是否可以使用管道在Mongo中有效地进行排序,分组和限制?

时间:2017-08-09 18:09:36

标签: mongodb aggregation-framework

给定年龄指数的用户:

{ name: 'Bob',
  age:   21  }

{ name: 'Cathy,
  age:   21  }

{ name: 'Joe',
  age:   33  }

获得输出:

[ 
  { _id: 21,
    names: ['Bob, 'Cathy'] },
  { _id: 33,
    names: ['Joe'] }
]

是否可以按年龄进行排序,分组和限制?

db.users.aggregate(
   [  
      {
        $sort: { 
           age: 1 
        }
      },
      {
        $group : {
           _id : $age,
           names:{ $push: '$name' }
      },
      {
        $limit: 10
      }
  ]

我做了一些研究,但是不清楚是否可以先排序然后分组。在我的测试中,小组失去了那种,但我不明白为什么。

如果组保留排序,则排序和限制可以大大减少所需的处理。它只需要做足够的工作来填充"限制10组。

所以,

  1. 组是否保留排序顺序?或者你必须分组然后排序?
  2. 是否可以排序,分组和限制仅执行足够的处理以返回限制?或者是否需要处理整个集合然后限制?

1 个答案:

答案 0 :(得分:4)

要回答您的第一个问题:$group 保留订单。有一个公开的变更请求,这些变更也突出了一些背景,但看起来产品不会被改变以保留输入文件。顺序:

一般情况下可以说两件事:您通常希望先分组,然后再进行排序。原因是排序较少的元素(分组通常产生的元素)将比排序所有输入文档更快。

其次,MongoDB将确保尽可能高效地进行排序。 documentation州:

  

当$ sort紧接在管道中的$ limit之前时,$ sort   操作仅在进展时保持前n个结果,其中n   是指定的限制,MongoDB只需要存储n个项目   记忆。当allowDiskUse为true时,此优化仍然适用   n项超过聚合内存限制。

因此,此代码可以完成您的工作:

collection.aggregate({
    $group: {
        _id: '$age',
        names: { $push: '$name' }
    }
}, {
    $sort: { 
        '_id': 1 
    }
}, {
    $limit: 10
})

编辑,点击您的评论:

我同意你说的话。进一步考虑你的逻辑,我会说:如果$group足够智能使用索引,那么它甚至不应该在开始时需要$sort阶段。不幸的是,它不是(还不是)。就目前的情况而言,$group永远不会使用索引,并且它不会根据以下阶段(在这种情况下为$limit)采取快捷方式。另请参阅此link,其中有人进行了一些基本测试。

聚合框架还很年轻所以我想,有很多工作要做,以使聚合管道更智能,更快。

StackOverflow上有答案(例如here),人们建议使用前期$sort阶段以强制"强迫" MongoDB以某种方式使用索引。然而,这显着减慢了我的测试(使用不同随机分布的100万条样本形状记录)。

当谈到聚合管道的性能时,开始时$match个阶段才是最有帮助的。如果您可以从一开始就限制需要通过管道的记录总数,那么这是您最好的选择 - 显然......;)