Menu Docs

Construtor de agregações

Neste guia, você pode aprender como executar agregações e construir pipelines usando o construtor de agregação Laravel. O construtor de agregação permite que você use uma sintaxe segura de tipo para construir um pipeline de agregação MongoDB .

Uma aggregation pipeline é uma pipeline de processamento de dados que executa sequencialmente transformações e cálculos em dados do banco de MongoDB database e, em seguida, gera os resultados como um novo documento ou conjunto de documentos.

Um pipeline de agregação é composto de estágios de agregação. Os estágios de agregação usam operadores para processar dados de entrada e produzir dados que o próximo estágio usa como entrada.

O construtor de agregação Laravel MongoDB permite criar estágios de agregação e pipelines de agregação. As seções a seguir mostram exemplos de como usar o construtor de agregação para criar os estágios de um pipeline de agregação:

Dica

O recurso de construtor de agregação está disponível somente nas versões 4.3 e posteriores do Laravel MongoDB . Para saber mais sobre como executar agregações sem usar o construtor de agregação , consulteAgregações no guia Construtor de Query.

Para iniciar um pipeline de agregação , chame o método Model::aggregate(). Em seguida, encadeie os métodos do estágio de agregação e especifique os parâmetros necessários para o estágio. Por exemplo, você pode chamar o método do operador sort() para construir um estágio $sort.

O construtor de agregação inclui os seguintes namespaces que você pode importar para construir estágios de agregação:

  • MongoDB\Builder\Accumulator

  • MongoDB\Builder\Expression

  • MongoDB\Builder\Query

  • MongoDB\Builder\Type

Esta seção apresenta os seguintes exemplos que mostram como usar estágios de agregação comuns:

Para saber mais sobre os operadores de agregação MongoDB , consulte Estágios de agregação no manual do servidor MongoDB.

Os exemplos seguintes executam pipelines de agregação em uma coleção representada pelo modelo User . Você pode adicionar os dados de amostra executando o seguinte método 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'))],
]);

Você pode encadear o método match() ao seu pipeline de agregação para especificar um filtro de query. Se você omitir este estágio, o método aggregate() produzirá todos os documentos na collection do modelo para o estágio seguinte.

Esse estágio de agregação geralmente é colocado em primeiro lugar para recuperar os dados usando índices disponíveis e reduzir a quantidade de dados do processo dos estágios subsequentes.

Dica

Se você omitir o método match() , o pipeline de agregação corresponderá a todos os documentos da coleção que correspondem ao modelo antes de outros estágios de agregação.

Este exemplo constrói um filtro de query para um estágio de agregação de correspondência usando o construtor MongoDB\Builder\Query . O estágio de correspondência inclui os seguintes critérios:

  • Retorna resultados que correspondem a qualquer um dos filtros de query usando a função Query::or()

  • Corresponde a documentos que contêm um campo occupation com um valor de "designer" usando as funções Query::query() e Query::eq()

  • Corresponde a documentos que contêm um campo name com um valor de "Eliud Nkosana" usando as funções Query::query() e Query::eq()

Clique no botão VIEW OUTPUT para ver os documentos retornados executando o código:

$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"
}
}
}
]

Dica

A função Query::or() corresponde ao operador de query $or do MongoDB. Para saber mais sobre esse operador, consulte $or no manual do servidor MongoDB.

Você pode encadear o método group() ao seu aggregation pipeline para modificar a estrutura dos dados realizando cálculos e agrupando-os por valores de campo comuns.

Esse estágio de agregação geralmente é colocado imediatamente após um estágio de correspondência para reduzir o processo dos estágios subsequentes dos dados.

Este exemplo utiliza o construtor MongoDB\Builder\Expression para definir as chaves de grupo em um estágio de agregação de grupo . O estágio de grupo especifica o seguinte comportamento de agrupamento:

  • Define o valor da chave de grupo, representada pelo campo _id , para o valor de campo definido pelo construtor Expression

  • Referencia os valores do documento no campo occupation chamando a função Expression::fieldPath()

Clique no botão VIEW OUTPUT para ver os documentos retornados executando o código:

$pipeline = User::aggregate()
->group(_id: Expression::fieldPath('occupation'));
$result = $pipeline->get();
[
{ "_id": "engineer" },
{ "_id": "designer" }
]

Dica

Este estágio de exemplo executa uma tarefa semelhante à do método construtor de query distinct() . Para saber mais sobre o método distinct() , consulte o exemplo de uso Recuperar valores de campo distintos .

Você pode encadear o método sort() ao aggregation pipeline para especificar a ordem de saída dos documentos.

Você pode adicionar esse estágio de agregação em qualquer lugar do pipeline. Muitas vezes, é colocado após o estágio de grupos, pois pode depender dos dados agrupados. Recomendamos colocar o estágio de classificação o mais tarde possível no pipeline para limitar os dados que ele processa.

Para especificar uma classificação, defina o valor do campo como Sort::Asc enum para uma classificação ascendente ou Sort::Desc enum para uma classificação descendente.

Este exemplo mostra um estágio de pipeline de agregação sort() que classifica os documentos pelo campo name a Sort::Desc, correspondendo à ordem alfabética inversa. Clique no botão VIEW OUTPUT para ver os documentos retornados executando o código:

$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"
}
}
}
]

Você pode encadear o método project() ao seu aggregation pipeline para especificar quais campos dos documentos devem ser exibidos por esse estágio.

Para especificar campos a serem incluídos, passe o nome de um campo e um valor verdadeiro, como 1 ou true. Todos os outros campos são omitidos da saída.

Como alternativa, para especificar campos a serem excluídos, passe cada nome de campo e um valor falso, como 0 ou false. Todos os outros campos estão incluídos no resultado.

Dica

Quando você especifica campos a serem incluídos, o campo _id é incluído por padrão. Para excluir o campo _id , exclua-o explicitamente no estágio de projeção.

Este exemplo mostra como usar o estágio de agregação do método project() para incluir somente o campo name e excluir todos os outros campos da saída. Clique no botão VIEW OUTPUT para ver os dados retornados executando o código:

$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" }
]

Para criar um pipeline de agregação , chame o método Model::aggregate() e, em seguida, encadeie os estágios de agregação na sequência que deseja que eles executem. Os exemplos nesta seção são adaptados do manual do servidor. Cada exemplo fornece um link para os dados de amostra que você pode inserir em seu banco de dados para testar a operação de agregação .

Esta seção apresenta os seguintes exemplos, que mostram como usar estágios de agregação comuns:

Este exemplo usa os dados de amostra fornecidos na seção Calcular Contagem, Soma e Média da referência de estágio $group no manual do Servidor.

O exemplo de código a seguir calcula o valor total de vendas, a quantidade média de vendas e a contagem de vendas para cada dia no ano 2014. Para fazer isso, ele usa um pipeline de agregação que contém os seguintes estágios:

  1. estágio $match para filtrar documentos que contêm um campo date em que o ano é 2014

  2. estágio $group para agrupar os documentos por data e calcular o valor total das vendas, a quantidade média de vendas e a contagem de vendas para cada grupo

  3. estágio $sort para classificar os resultados pelo valor total da venda para cada grupo em ordem decrescente

Clique no botão VIEW OUTPUT para ver os dados retornados executando o código:

$pipeline = Sale::aggregate()
->match(
date: [
Query::gte(new UTCDateTime(new DateTimeImmutable('2014-01-01'))),
Query::lt(new UTCDateTime(new DateTimeImmutable('2015-01-01'))),
],
)
->group(
_id: Expression::dateToString(Expression::dateFieldPath('date'), '%Y-%m-%d'),
totalSaleAmount: Accumulator::sum(
Expression::multiply(
Expression::numberFieldPath('price'),
Expression::numberFieldPath('quantity'),
),
),
averageQuantity: Accumulator::avg(
Expression::numberFieldPath('quantity'),
),
count: Accumulator::sum(1),
)
->sort(
totalSaleAmount: Sort::Desc,
);
[
{ "_id": "2014-04-04", "totalSaleAmount": { "$numberDecimal": "200" }, "averageQuantity": 15, "count": 2 },
{ "_id": "2014-03-15", "totalSaleAmount": { "$numberDecimal": "50" }, "averageQuantity": 10, "count": 1 },
{ "_id": "2014-03-01", "totalSaleAmount": { "$numberDecimal": "40" }, "averageQuantity": 1.5, "count": 2 }
]

Este exemplo usa os dados de amostra fornecidos na seção Unwind Embedded Arrays da referência de estágio $unwind no manual do servidor.

O exemplo de código a seguir agrupa itens vendidos por suas marcações e calcula o valor total de vendas para cada marcação. Para fazer isso, ele usa um pipeline de agregação que contém os seguintes estágios:

  1. Estágio $unwind para gerar um documento separado para cada elemento da array items

  2. Estágio $unwind para gerar um documento separado para cada elemento das arrays items.tags

  3. $group estágio para agrupar os documentos pelo valor da tag e calcular o valor total de vendas de itens que têm cada tag

Clique no botão VIEW OUTPUT para ver os dados retornados executando o código:

$pipeline = Sale::aggregate()
->unwind(Expression::arrayFieldPath('items'))
->unwind(Expression::arrayFieldPath('items.tags'))
->group(
_id: Expression::fieldPath('items.tags'),
totalSalesAmount: Accumulator::sum(
Expression::multiply(
Expression::numberFieldPath('items.price'),
Expression::numberFieldPath('items.quantity'),
),
),
);
[
{ "_id": "school", "totalSalesAmount": { "$numberDecimal": "104.85" } },
{ "_id": "electronics", "totalSalesAmount": { "$numberDecimal": "800.00" } },
{ "_id": "writing", "totalSalesAmount": { "$numberDecimal": "60.00" } },
{ "_id": "office", "totalSalesAmount": { "$numberDecimal": "1019.60" } },
{ "_id": "stationary", "totalSalesAmount": { "$numberDecimal": "264.45" } }
]

Este exemplo utiliza os dados de amostra fornecidos na seção Executar uma única união de igualdade com $lookup da referência de estágio $lookup no manual do Servidor.

O exemplo de código a seguir une os documentos da collection orders aos documentos da collection inventory usando o campo item da collection orders e o campo sku da collection inventory.

Para fazer isso, o exemplo usa um pipeline de agregação que contém um estágio $lookup que especifica a coleção para recuperar os dados e os nomes dos campo locais e externos.

Clique no botão VIEW OUTPUT para ver os dados retornados executando o código:

$pipeline = Order::aggregate()
->lookup(
from: 'inventory',
localField: 'item',
foreignField: 'sku',
as: 'inventory_docs',
);
[
{ "_id": 1, "item": "almonds", "price": 12, "quantity": 2, "inventory_docs": [
{ "_id": 1, "sku": "almonds", "description": "product 1", "instock": 120 }
] },
{ "_id": 2, "item": "pecans", "price": 20, "quantity": 1, "inventory_docs": [
{ "_id": 4, "sku": "pecans", "description": "product 4", "instock": 70 }
] },
{ "_id": 3, "inventory_docs": [
{ "_id": 5, "sku": null, "description": "Incomplete" },
{ "_id": 6 }
] }
]

Ao usar o construtor de agregação para criar um pipeline de agregação, você pode definir operações ou estágios em uma fábrica de operadores personalizada. Uma fábrica de operadores personalizados é uma função que retorna expressões ou estágios de uma aggregation pipeline. Você pode criar essas funções para melhorar a legibilidade e a reutilização do código.

Este exemplo mostra como criar e usar uma fábrica de operadores personalizados que retorna expressões que extraem o ano de um campo de data especificado.

A função a seguir aceita o nome de um campo que contém uma data e retorna uma expressão que extrai o ano da data:

public function yearFromField(string $dateFieldName): YearOperator
{
return Expression::year(
Expression::dateFieldPath($dateFieldName),
);
}

O exemplo de aggregation pipeline inclui os seguintes estágios:

  • addFields(), que chama a função de fábrica do operador personalizado para extrair o ano do campo birthday e atribuí-lo ao campo birth_year

  • project(), que inclui somente os campos name e birth_year em sua saída

Clique no botão VIEW OUTPUT para ver os dados retornados executando o código:

$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
}
]