MongoDB 基础系列十七:聚合查询之 Aggregation 综述

前言

此篇博文是 Mongdb 基础系列之一;

本文为作者的原创作品,转载需注明出处;

简介

MongoDB 提供了三种方式来对 documents 进行聚合查询,分别是 aggregation pipelinemap-reduce function 以及 single purpose aggregation methods

Aggregation Pipeline

Pipeline 是什么呢?英文直译为“管道”,可以形象的比喻为,将原始数据经过多条“管道”进行聚合处理,然后经过最后一道管道进行聚合处理以后,将聚合结果返回;其中每一个“管道”的处理流程,在官方文档中有一个术语,叫做Pipeline Stage,既是每一个 pipeline 阶段;MongoDB 为每一个 pipeline stage 提供了一系列的 operators 操作来完成聚合处理操作;

使用 MongoDB Pipeline 的优势是,MongoDB 提供了许多内置的( native )的方法( 就是指这些 operators )来帮助高效的进行数据的聚合处理;

Aggregation Pipeline 可以在分片集群上进行处理;

来简单看一下下面的这个例子,

可以看到,通过 Aggregation Pipeline 的聚合算法主要是通过了两次 Pipeline Stage 进行处理,第一个 Stage 通过内置方法 $match 筛选出 status = “A” 的 documents,第二个 Stage 通过内置方法 $group 对不同的 customer_id 字段进行分组,然后对分组后的 amount 字段进行求和,得到每个 customer 的 amount 总和;

Map-Reduce

MongoDB 的 Map-Reduce 模型严格按照 Google 的 Map Reduce 模型来进行设计的;总体而言,Map Reduce 操作包含两个阶段:map stage,处理输入文档并 emit 一个或多个经过转换的 documents;reduce stage,合并由 map stage 的输出数据;

MongoDB 对 Map Reduce 模型提供了 Javascript 的接口实现,需要由用户自己定义 Javascript 的实现来分别处理 mapstage 的两个阶段;

MongoDB 的 MapReduce 的优势是,提供了 Javascript 接口给用户实现 Map、Reduce 的功能,也就是提供了极大的灵活性( flexibility )供用户自定义自己想要的任何的功能;但是弊端是,性能不如 Aggregation Pipeline 并且更为复杂;补充,笔者附上一份针对 Aggregation Pipeline 和 Map-Reduce 操作的性能对比报告

最后来简单的看下面这样的一个例子,

可以看到,整个 Map-Reduce 流程包含两个 stage 和一个查询输出 query output

先来看一下该 query output,

1
2
3
4
{
query: { status: "A" },
out: "order_totals"
}

可见,其包含两个部分,

  • query: { status: “A” }
    什么鬼?查询先决条件?不是说好的 map 逻辑由用户来定制吗?是的,你又猜对了,这就是查询的先决条件,筛选出 orders collection 中 status = “A” 的 documents;是的,通过 query 操作符 MongoDB 已经帮你实现了 map 最核心的逻辑,提前帮你筛选好了数据;补充,后面所介绍的 map stage 中的 this 就是指通过筛选过后的 document;当然,这里可以不预先定义 query 参数,不过,随之而来的可能就是性能问题了,将会返回所有的 orders 给你的 map 方法来进行处理,如果你有 1 亿条 orders 呢?
  • out: “order_totals”
    将输出结果以 order_totals collection 进行返回;

再来看这两个 stage,

  • map stage,用户自定义 javascript 函数来处理 map 逻辑,然后通过 emit() 方法返回经过 map 处理过后的 customer id 和 amout 值;正如前面的 query output 所描述的那样,该 map 方法的输入是经过 MongoDB 预筛选之后的 documents 数据;
  • reduce stage,用户自定义 javascript 函数来处理 reduce 逻辑,很简单,这里直接将 values 进行求和并返回;备注,这里为什么可以直接求和?是因为 MongoDB 在内部已经根据 customer_id 进行过分组操作,这里的 values 参数对应的就是某一个 customer 的所有 amounts 数据,一个数组,参数 key 就是 customer_id;所以,直接求和就是对某一个 customer 的所有 amounts 进行求和;

Single Purpose Aggregation Operations

这个其实可以理解为 Aggregation Pipeline 的一种特殊的情况;MongoDB 提供了 db.collection.count()db.collection.distinct() 等针对某一个 collection 的聚合查询操作;这样可以快速的对一个 collection 进行聚合操作;看下面这个例子,

References

https://docs.mongodb.com/manual/core/aggregation-pipeline/

https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/#db.collection.aggregate

pymongo: http://api.mongodb.com/python/current/examples/aggregation.html