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}