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

aggregate() 函数用于查找仅当 status 等于“A”时每个 custID 的 amount 字段的总和。因此,管道构建如下。

管道中的第一阶段使用 $match 过滤 status='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}
广告