Aggregation Pipeline Match
Aggregation Pipeline
Aggregation Pipeline é um recurso incrível do MongoDB, com ela podemos selecionar e transformar dados de forma semelhante à instruções SQL.
Para que você consiga executar os exemplos eu preparei um repositório com um arquivo docker compose para rodar o MongoDB e também com o backup dos bancos de exemplo. Dê uma olhada no arquivo README. MongoDB Samples
Banco utilizado para executar os exemplos
Para esse artigo eu vou utilizar o banco sample_mflix do repositório mongo-samples.
Estágios
Uma Aggregation Pipeline é construída por vários estágios, a saída do estágio anterior será a entrada do próximo estágio
Match
Com o estágio match nós podemos filtar os documentos utilizando desde condições simples até condições complexas.
Esse é um exemplo de documento da collection movies.
{
"_id": {"$oid": "573a1390f29313caabcd42e8"},
"awards": {
"wins": 1,
"nominations": 0,
"text": "1 win."
},
"cast": ["A.C. Abadie", "Gilbert M. 'Broncho Billy' Anderson", "George Barnes", "Justus D. Barnes"],
"countries": ["USA"],
"directors": ["Edwin S. Porter"],
"fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.",
"genres": ["Short", "Western"],
"imdb": {
"rating": 7.4,
"votes": 9847,
"id": 439
},
"languages": ["English"],
"lastupdated": "2015-08-13 00:27:59.177000000",
"num_mflix_comments": 0,
"plot": "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.",
"poster": "https://m.media-amazon.com/images/M/MV5BMTU3NjE5NzYtYTYyNS00MDVmLWIwYjgtMmYwYWIxZDYyNzU2XkEyXkFqcGdeQXVyNzQzNzQxNzI@._V1_SY1000_SX677_AL_.jpg",
"rated": "TV-G",
"released": {"$date": "1903-12-01T00:00:00.000Z"},
"runtime": 11,
"title": "The Great Train Robbery",
"tomatoes": {
"viewer": {
"rating": 3.7,
"numReviews": 2559,
"meter": 75
},
"fresh": 6,
"critic": {
"rating": 7.6,
"numReviews": 6,
"meter": 100
},
"rotten": 0,
"lastUpdated": {"$date": "2015-08-08T19:16:10.000Z"}
},
"type": "movie",
"year": 1903
}
Por exemplo, no exemplo abaixo vamos filtrar somente os documentos da collection movies do ano de 1992
db.getCollection('movies').aggregate(
[
{
$match: {
year: 1992,
}
}
]
)
Essa é uma das formas mais simples do match.
Na collection movies podemos ter filmes ou séries, então poderíamos criar uma condição onde nós quiséssemos somente séries no ano de 1992, ficaria dessa forma:
db.getCollection('movies').aggregate(
[
{
$match: {
$and: [
{ year: 1992 },
{ type: 'series' }
]
}
}
]
)
Campos aninhados
Se quisermos fazer uma busca por campos aninhados, por exemplo, na collection movies poderíamos trazer somente os documentos onde o campo imdb.rating seja maior que 7.
db.getCollection('movies').aggregate(
[
{
$match: {
"imdb.rating": { $gt: 7 }
}
}
]
)
Or
Podemos adicionar uma condição de or buscando filmes que o imdb.rating seja maior que 7 ou o tomatoes.viewer.rating seja maior que 7.
[
{
$match: {
$or: [
{ "imdb.rating": { $gt: 7 } },
{ "tomatoes.viewer.rating": { $gt: 7 } }
]
}
}
]
)
Combinando And / Or
Podem haver situações que precisamos combinar um and e um or, por exemplo se quiséssemos filmes que são do ano de 1992 mas que a segunda condição poderia ser o campo imdb.rating maior que 7 ou o campo tomatoes.viewer.rating maior que 7 poderíamos fazer dessa forma:
db.getCollection('movies').aggregate(
[
{
$match: {
$and: [
{ year: 1992 },
{
$or: [
{ "imdb.rating": { $gt: 7 } },
{ "tomatoes.viewer.rating": { $gt: 7 } }
]
}
],
}
}
]
)
Trabalhando com arrays no Match
É muito comum precisarmos trabalhar com arrays dentro de documentos, na collection movies o campo countries é um array, se quiséssemos somente documentos onde tivessem dois valores dentro do array countries poderíamos fazer da seguinte forma:
db.getCollection('movies').aggregate(
[
{
$match: { countries: { $size: 2 } }
}
]
)
Se quiséssemos somente documentos com mais de dois valores dentro do campo countries poderíamos fazer assim:
db.getCollection('movies').aggregate(
[
{
$match: {
$expr: {
$gt: [
{ $size: "$countries" },
2
]
}
}
}
]
)
Aqui podemos notar algumas coisas diferentes, como por exemplo o $countries com o $, isso é para dizer ao MongoDB que estamos nos referindo à um campo dentro do documento.
Outro novo elemento é o $expr, o uso dele é necessário quando temos que avaliar expressões, no caso acima, precisamos primeiro calcular o tamanho do campo countries para depois compará-lo com o número 2
Filtrando um país
Se quisermos filtrar o campo countries que o MongoDB retorne somente documentos que possuam o valor desejado em pelo menos uma das posições do array podemos fazer dessa forma:
db.getCollection('movies').aggregate(
[
{
$match: {
countries: 'USA'
}
}
]
)
Obrigatoriedade de dois países
Se precisássemos fazer uma busca parecida, mas com a obrigatoriedade de termos pelo menos dois países específicos, poderíamos fazer dessa forma:
db.getCollection('movies').aggregate(
[
{
$match: {
countries: {
$all: [
'USA', 'Italy'
]
}
}
}
]
)
Obrigatoriedade de pelo menos um país em uma lista
Se quiséssemos somente documentos em que pelo menos um dos países esteja presente dentro de uma lista de países, poderíamos fazer dessa forma:
db.getCollection('movies').aggregate(
[
{
$match: {
countries: {
$in: [
'India', 'Italy'
]
}
}
}
]
)