MongoEngine - 聚合

术语"聚合"用于处理数据并返回计算结果的操作。 对集合中文档的一个或多个字段进行求和、计数和平均可以称为聚合函数。

MongoEngine 提供了aggregate() 函数封装了PyMongo 的聚合框架。 聚合操作使用集合作为输入并返回一个或多个文档作为结果。

MongoDB 使用数据处理管道的概念。 管道可以有多个阶段。 基本阶段提供过滤器并像查询一样操作。 其他工具提供按一个或多个字段进行分组和/或排序的工具、字符串连接任务、数组聚合工具等。

在 MongoDB 管道创建中定义了以下阶段 −

名称 说明
$project 通过添加新字段或删除现有字段来重新调整流中的每个文档。
$match 过滤文档流,只允许匹配的文档不加修改地进入下一阶段。 $match 使用标准的 MongoDB 查询。
$redact 根据存储在文档本身中的信息,通过限制每个文档的内容来重新塑造每个文档。
$limit 限制文档未经修改地传递到管道
$skip 跳过前 n 个文档并将未修改的剩余文档传递给管道。
$group 按给定的标识符表达式对输入文档进行分组,并将累加器表达式应用于每个组。 输出文档只包含标识符字段和累计字段。
$sort 按指定的排序键重新排序文档流。
$out 将聚合管道的结果文档写入集合。

聚合表达式使用字段路径访问输入文档中的字段。 要指定字段路径,请使用以美元符号 $$$ 作为字段名称前缀的字符串。 表达式可以使用一个或多个布尔运算符($and、$or、$not)和比较运算符($eq、$gt、$lt、$gte、$lte 和 $ne)。

以下算术表达式也用于聚合 −

$add 添加数字以返回总和。 接受任意数量的参数表达式
$subtract 返回第一个值减去第二个值的结果
$multiply 将数字相乘以返回乘积。 接受任意数量的参数表达式
$divide 返回第一个数字除以第二个数字的结果。 接受两个参数表达式
$mod 返回第一个数除以第二个数的余数。 接受两个参数表达式

下面的字符串表达式也可以用于聚合 −

$concat 连接任意数量的字符串
$substr 返回字符串的子串,从指定的索引位置开始到指定的长度
$toLower 将字符串转换为小写。 接受单个参数表达式
$toUpper 将字符串转换为大写。 接受单个参数表达式
$strcasecmp 执行字符串比较,如果两个字符串相等则返回 0,如果第一个大于第二个则返回 1,如果第一个字符串小于第二个则返回 -1

为了演示 aggregate() 函数在 MongoEngine 中的工作原理,让我们首先定义一个名为 orders 的文档类。

from mongoengine import *
con=connect('mydata')

class orders(Document):
   custID = StringField()
   amount= IntField()
   status = StringField()

然后我们在 orders 集合中添加以下文档 −

_id custID amount status
ObjectId("5eba52d975fa1e26d4ec01d0") A123 500 A
ObjectId("5eba536775fa1e26d4ec01d1") A123 250 A
ObjectId("5eba53b575fa1e26d4ec01d2") B212 200 D
ObjectId("5eba540e75fa1e26d4ec01d3") B212 400 A

仅当状态等于"A"时,aggregate() 函数才用于查找每个 custID 的 amount 字段的总和。 因此,管道构造如下。

管道中的第一阶段使用 $match 来过滤状态为"A"的文档。 第二阶段使用 $group 标识符对 CustID 上的文档进行分组并执行 amount 求和。

 pipeline = [
{"$match" : {"status" : "A"}},
{"$group": {"_id": "$custID", "total": {"$sum": "$amount"}}}
]

此管道现在用作 aggregate() 函数的参数。

docs = orders.objects().aggregate(pipeline)

我们可以使用 for 循环遍历文档游标。 完整代码如下 −

from mongoengine import *
con=connect('mydata')

class orders(Document):
   custID = StringField()
   amount= IntField()
   status = StringField()

pipeline = [
   {"$match" : {"status" : "A"}},
   {"$group": {"_id": "$custID", "total": {"$sum": "$amount"}}}
   ]
docs = orders.objects().aggregate(pipeline)
for doc in docs:
   print (x)

对于给定的数据,生成以下输出 −

{'_id': 'B212', 'total': 400}
{'_id': 'A123', 'total': 750}