聚合构建器
Overview
在本指南中,您可以学习;了解如何使用 Laravel Integration聚合构建器来执行聚合和构建管道。 聚合构建器允许您使用类型安全的语法来构建MongoDB聚合管道。
聚合管道是一种数据处理管道, MongoDB database中的数据按顺序执行转换和计算,然后将结果作为新文档或文档集输出。
聚合管道由聚合阶段组成。 聚合阶段使用操作符处理输入数据并生成下一阶段用作输入的数据。
Laravel MongoDB 聚合构建器允许您构建聚合阶段和聚合管道。 以下部分举例说明如何使用聚合构建器创建聚合管道的各个阶段:
提示
聚合构建器功能仅在 Laravel MongoDB 4.3及更高版本中可用。 要学习;了解有关在不使用聚合构建器的情况下运行聚合的更多信息,请参阅 查询构建器指南中的聚合。
添加聚合构建器依赖项
聚合构建器是MongoDB /builder 包的一部分。 您必须将此包作为依赖项添加到项目中才能使用。 运行以下命令,将聚合构建器依赖项添加到应用程序中:
composer require mongodb/builder:^0.2
安装完成后,验证 composer.json
文件的require
对象中是否包含以下行:
"mongodb/builder": "^0.2",
创建聚合管道
要启动聚合管道,请调用Model::aggregate()
方法。 然后,按照希望运行的顺序链接聚合阶段方法。
聚合构建器包括以下命名空间,您可以导入这些命名空间来构建聚合阶段:
MongoDB\Builder\Accumulator
MongoDB\Builder\Expression
MongoDB\Builder\Query
MongoDB\Builder\Type
提示
要学习;了解有关构建器类的更多信息,请参阅MongoDBMongoDB /MongoDB MongoDB -php-builderGithub Github存储库。
本节通过以下示例展示如何使用常见聚合阶段和组合阶段来构建聚合管道:
要了解有关MongoDB 聚合操作符的更多信息,请参阅 MongoDB Server手册中的 聚合阶段 。
示例文档
以下示例在User
模型表示的集合上运行聚合管道。 您可以通过运行以下insert()
方法来添加样本数据:
User::insert([ ['name' => 'Alda Gröndal', 'occupation' => 'engineer', 'birthday' => new UTCDateTime(new DateTimeImmutable('2002-01-01'))], ['name' => 'Francois Soma', 'occupation' => 'engineer', 'birthday' => new UTCDateTime(new DateTimeImmutable('1998-02-02'))], ['name' => 'Janet Doe', 'occupation' => 'designer', 'birthday' => new UTCDateTime(new DateTimeImmutable('1987-03-03'))], ['name' => 'Eliud Nkosana', 'occupation' => 'engineer', 'birthday' => new UTCDateTime(new DateTimeImmutable('1984-04-04'))], ['name' => 'Bran Steafan', 'occupation' => 'engineer', 'birthday' => new UTCDateTime(new DateTimeImmutable('1998-05-05'))], ['name' => 'Ellis Lee', 'occupation' => 'designer', 'birthday' => new UTCDateTime(new DateTimeImmutable('1996-06-06'))], ]);
匹配阶段示例
您可以将match()
方法链接到聚合管道以指定查询筛选器。 如果省略此阶段, aggregate()
方法将为下一阶段输出模型集合中的所有文档。
此聚合阶段通常放在第一位,以便使用可用索引检索数据,并减少后续阶段处理的数据量。
提示
如果省略match()
方法,则聚合管道会在其他聚合阶段之前匹配集合中与模型对应的所有文档。
此示例使用MongoDB\Builder\Query
构建器为匹配聚合阶段构造查询过滤器。 匹配阶段包括以下标准:
使用
Query::or()
函数返回与任一查询筛选器匹配的结果使用
Query::query()
和Query::eq()
函数匹配包含值为"designer"
的occupation
字段的文档使用
Query::query()
和Query::eq()
函数匹配包含值为"Eliud Nkosana"
的name
字段的文档
单击 VIEW OUTPUT按钮查看运行以下代码返回的文档:
$pipeline = User::aggregate() ->match(Query::or( Query::query(occupation: Query::eq('designer')), Query::query(name: Query::eq('Eliud Nkosana')), )); $result = $pipeline->get();
[ { "_id": ..., "name": "Janet Doe", "occupation": "designer", "birthday": { "$date": { "$numberLong": "541728000000" } } }, { "_id": ..., "name": "Eliud Nkosana", "occupation": "engineer", "birthday": { "$date": { "$numberLong": "449884800000" } } }, { "_id": ..., "name": "Ellis Lee", "occupation": "designer", "birthday": { "$date": { "$numberLong": "834019200000" } } } ]
小组赛阶段示例
您可以将group()
方法链接到聚合管道,通过执行计算并按公共字段值分组来修改数据结构。
此聚合阶段通常紧随匹配阶段之后放置,以减少后续阶段处理的数据。
此示例使用MongoDB\Builder\Expression
构建器定义群组聚合阶段的群组键。 小组赛阶段指定以下分组行为:
将
_id
字段表示的组键的值设置为Expression
构建器定义的字段值通过调用
Expression::fieldPath()
函数引用occupation
字段中的文档值
单击VIEW OUTPUT按钮,查看运行代码返回的文档:
$pipeline = User::aggregate() ->group(_id: Expression::fieldPath('occupation')); $result = $pipeline->get();
[ { "_id": "engineer" }, { "_id": "designer" } ]
提示
此示例阶段执行与distinct()
查询构建器方法类似的任务。 要了解有关distinct()
方法的详情,请参阅检索不同字段值用法示例。
排序阶段示例
您可以将sort()
方法链接到聚合管道,以指定文档的输出顺序。
您可以在管道中的任何位置添加此聚合阶段。 它通常位于小组赛阶段之后,因为它可能取决于分组数据。 我们建议在管道中尽可能晚地放置排序阶段,以限制其处理的数据。
要指定排序,请将字段值设置为Sort::Asc
枚举以进行升序排序,或将Sort::Desc
枚举设置为降序排序。
此示例显示了sort()
聚合管道阶段,该阶段按name
字段到Sort::Desc
对文档进行排序,对应于反向字母顺序。 单击VIEW OUTPUT按钮,查看运行代码返回的文档:
$pipeline = User::aggregate() ->sort(name: Sort::Desc); $result = $pipeline->get();
[ { "_id": ..., "name": "Janet Doe", "occupation": "designer", "birthday": { "$date": { "$numberLong": "541728000000" } } }, { "_id": ..., "name": "Francois Soma", "occupation": "engineer", "birthday": { "$date": { "$numberLong": "886377600000" } } }, { "_id": ..., "name": "Ellis Lee", "occupation": "designer", "birthday": { "$date": { "$numberLong": "834019200000" } } }, { "_id": ..., "name": "Eliud Nkosana", "occupation": "engineer", "birthday": { "$date": { "$numberLong": "449884800000" } } }, { "_id": ..., "name": "Bran Steafan", "occupation": "engineer", "birthday": { "$date": { "$numberLong": "894326400000" } } }, { "_id": ..., "name": "Alda Gröndal", "occupation": "engineer", "birthday": { "$date": { "$numberLong": "1009843200000" } } } ]
项目阶段示例
您可以将project()
方法链接到聚合管道,以指定此阶段要显示文档中的哪些字段。
要指定要包含的字段,请传递字段名称和真值,例如1
或true
。 输出中将省略所有其他字段。
或者,要指定要排除的字段,请传递每个字段名称和一个假值,例如0
或false
。 所有其他字段都包含在输出中。
提示
当您指定要包含的字段时,默认包含_id
字段。 要排除_id
字段,请在投影阶段明确将其排除。
此示例演示如何使用project()
方法聚合阶段仅包含name
字段并从输出中排除所有其他字段。 单击VIEW OUTPUT按钮可查看运行代码返回的数据:
$pipeline = User::aggregate() ->project(_id: 0, name: 1); $result = $pipeline->get();
[ { "name": "Alda Gröndal" }, { "name": "Francois Soma" }, { "name": "Janet Doe" }, { "name": "Eliud Nkosana" }, { "name": "Bran Steafan" }, { "name": "Ellis Lee" } ]
聚合管道示例
此聚合管道示例链接了多个阶段。 每个阶段都在从每个先前阶段检索的输出上运行。 在此示例中,各个阶段按顺序执行以下操作:
将
birth_year
字段添加到文档中,并将值设置为从birthday
字段中提取的年份。按
occupation
字段的值对文档进行分组,并使用Accumulator::avg()
函数计算每组的birth_year
平均值。 将计算结果赋给birth_year_avg
字段。按组键字段以升序对文档进行排序。
根据组键字段的值创建
profession
字段,包含birth_year_avg
字段并省略_id
字段。
单击VIEW OUTPUT按钮,查看运行代码返回的数据:
$pipeline = User::aggregate() ->addFields( birth_year: Expression::year( Expression::dateFieldPath('birthday'), ), ) ->group( _id: Expression::fieldPath('occupation'), birth_year_avg: Accumulator::avg(Expression::numberFieldPath('birth_year')), ) ->sort(_id: Sort::Asc) ->project(profession: Expression::fieldPath('_id'), birth_year_avg: 1, _id: 0);
[ { "birth_year_avg": 1991.5, "profession": "designer" }, { "birth_year_avg": 1995.5, "profession": "engineer" } ]
注意
由于此管道省略了match()
阶段,因此初始阶段的输入由集合中的所有文档组成。
创建自定义操作符工厂
使用聚合构建器创建聚合管道时,可以在自定义操作符工厂中定义操作或阶段。 自定义操作符工厂是一个返回聚合管道的表达式或阶段的函数。 您可以创建这些函数来提高代码可读性和重用性。
此示例演示如何创建和使用自定义操作符工厂,该工厂返回从指定日期字段中提取年份的表达式。
以下函数接受包含日期的字段名称,并返回从该日期提取年份的表达式:
public function yearFromField(string $dateFieldName): YearOperator { return Expression::year( Expression::dateFieldPath($dateFieldName), ); }
示例聚合管道包括以下阶段:
addFields()
,它会调用自定义操作符工厂函数以从birthday
字段中提取年份并将其分配给birth_year
字段project()
,其输出中仅包含name
和birth_year
字段
单击VIEW OUTPUT按钮,查看运行代码返回的数据:
$pipeline = User::aggregate() ->addFields(birth_year: $this->yearFromField('birthday')) ->project(_id: 0, name: 1, birth_year: 1);
[ { "name": "Alda Gröndal", "birth_year": 2002 }, { "name": "Francois Soma", "birth_year": 1998 }, { "name": "Janet Doe", "birth_year": 1987 }, { "name": "Eliud Nkosana", "birth_year": 1984 }, { "name": "Bran Steafan", "birth_year": 1998 }, { "name": "Ellis Lee", "birth_year": 1996 } ]