Poo*_*rna 4 mongodb aggregation-framework
在MongoDB聚合管道中,从一个阶段到另一个阶段的记录流发生一次/批次(或)将等待当前阶段完成整个集合,然后再将其传递到下一阶段?
例如,我有一个带有以下样本记录的集合classtest
{name: "Person1", marks: 20}
{name: "Person2", marks: 20}
{name: "Person1", marks: 20}
Run Code Online (Sandbox Code Playgroud)
我有大约100名学生的1000条记录,我有以下聚合查询
db.classtest.aggregate(
[
{$sort: {name: 1}},
{$group: {_id: '$name',
total: {$sum: '$marks'}}},
{$limit: 5}
])
Run Code Online (Sandbox Code Playgroud)
我有以下问题.
我的实际想法是对聚合结果进行分页.在上面的场景中,如果$ group维护排序顺序并仅处理所需的记录数,我想$match condition {$ge: 'lastPersonName'}在后续页面查询中应用.
我已经解决了这个问题,不需要维护另一个集合,甚至不需要 $group 遍历整个集合,因此发布了我自己的答案。
正如其他人指出的那样:
$group 不保留顺序,因此早期排序没有多大帮助。$group不做任何优化,即使有后续$limit,即$group在整个集合上运行。我的用例具有以下独特的功能,这帮助我解决了它:
我对页面大小不是很特别。前端能够处理不同的页面大小。下面是我用过的聚合命令。
db.classtest.aggregate(
[
{$sort: {name: 1}},
{$limit: 5 * 10},
{$group: {_id: '$name',
total: {$sum: '$marks'}}},
{$sort: {_id: 1}}
])
Run Code Online (Sandbox Code Playgroud)以上解释。
$sort紧接在 之前$limit,框架会优化要发送到下一阶段的数据量。参考这里$group舞台。这样,最终结果的大小可能介于 0 到 50 之间。然后将最后一条记录中的名称(在保留结果中)用作后续页面请求中的 $match 标准,如下所示。
db.classtest.aggregate(
[
{$match: {name: {$gt: lastRecordName}}}
{$sort: {name: 1}},
{$limit: 5 * 10},
{$group: {_id: '$name',
total: {$sum: '$marks'}}},
{$sort: {_id: 1}}
])
Run Code Online (Sandbox Code Playgroud)在上面,框架仍然会$match, $sort and $limit作为单个操作一起优化,我已经通过解释计划确认了这一点。
这里要考虑的第一件事是聚合框架与要应用的阶段的"管道"一起工作以获得结果.如果您熟悉在操作系统的"命令行"或"shell"上处理事物,那么您可能对"管道"或|操作员有一些经验.
这是一个常见的unix习语:
ps -ef | grep mongod | tee "out.txt"
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这里的第一个命令的输出ps -ef被"管道"到下一个命令grep mongod,而下一个命令又将其输出"管道" tee out.txt输出到终端以及指定的文件名.这是一个"管道",每个阶段"馈送"下一个阶段,并按照它们所写入的序列的"顺序".
聚合管道也是如此.这里的"管道"实际上是一个"数组",它是在处理数据到结果时传递的有序指令集.
db.classtest.aggregate([
{ "$group": {
"_id": "$name",
"total": { "$sum": "$marks"}
}},
{ "$sort": { "name": 1 } },
{ "$limit": 5 }
])
Run Code Online (Sandbox Code Playgroud)
所以这里发生的是集合中的所有项目首先被处理$group以获得它们的总数.没有指定的"顺序"进行分组,因此预先排序数据没有多大意义.这样做也没有任何意义,因为你还没有进入你的后期阶段.
对于您的下一个"数据页面",您最理想$match的是找到找到的最后一个唯一名称,如下所示:
db.classtest.aggregate([
{ "$match": { "name": { "$gt": lastNameFound } }},
{ "$group": {
"_id": "$name",
"total": { "$sum": "$marks"}
}},
{ "$sort": { "name": 1 } },
{ "$limit": 5 }
])
Run Code Online (Sandbox Code Playgroud)
这不是最好的解决方案,但实际上并没有这种分组的替代方案.然而,每次迭代结束时,它会显着"更快".或者,如果您的数据允许,在每个聚合语句中存储所有unqiue名称(或从另一个集合中读取该名称)和"分页"以及"范围查询"可能是一个可行的选项.
就像是:
db.classtest.aggregate([
{ "$match": { "name": { "$gte": "Allan", "$lte": "David" } }},
{ "$group": {
"_id": "$name",
"total": { "$sum": "$marks"}
}},
{ "$sort": { "name": 1 } },
])
Run Code Online (Sandbox Code Playgroud)
不幸的是,没有"限制分组直到x结果"选项,所以除非你可以使用另一个列表,否则你基本上将你发送的每个聚合查询分组一切(并且可能每次都会逐渐变小).
| 归档时间: |
|
| 查看次数: |
18178 次 |
| 最近记录: |