Menu Docs
Página inicial do Docs
/ / /
Driver de Kotlin Sync

Operações de expressão de aggregation

Nesta página

  • Visão geral
  • Como usar operações
  • Métodos do construtor
  • operações
  • Operações aritméticas
  • Operações de array
  • Operações booleanas
  • Operações de comparação
  • Operações condicionais
  • Operações de conveniência
  • Operações de conversão
  • Operações de datas
  • Operações de documentos
  • Operações de mapas
  • Operações de strings
  • Operações de verificação de tipo

Neste guia, você pode aprender como usar o driver Kotlin Sync para construir expressões para uso em pipelines de agregação . Você pode executar operações de expressão com métodos Kotlin detectáveis e seguros em vez de usar documentos BSON. Como esses métodos seguem o padrão de interface fluente, você pode encadear operações de agregação para criar um código compacto e naturalmente legível.

As operações neste guia utilizam métodos do pacote com.mongodb.client.model.mql. Esses métodos fornecem uma maneira idiomática de usar a API de Consulta, o mecanismo pelo qual o driver interage com uma implantação do MongoDB. Para saber mais sobre a API de consulta, consulte a documentação manual do servidor.

Os exemplos deste guia pressupõem que você inclua as seguintes importações em seu código:

import com.mongodb.client.model.Aggregates
import com.mongodb.client.model.Accumulators
import com.mongodb.client.model.Projections
import com.mongodb.client.model.Filters
import com.mongodb.client.model.mql.MqlValues

Para acessar campos de documento em uma expressão, você deve fazer referência ao documento atual que está sendo processado pelo pipeline de agregação usando o método current() . Para acessar o valor de um campo, use o método digitado correto, como getString() ou getDate(). Ao especificar o tipo para um campo, você garante que o driver forneça apenas os métodos compatíveis com esse tipo. O seguinte código mostra como referenciar um campo de string denominado name:

current().getString("name")

Para especificar um valor em uma operação, passe para o método do construtor do of() para convertê-lo em um tipo válido. O seguinte código mostra como referenciar um valor de 1.0:

of(1.0)

Para criar uma operação, encadeie um método à sua referência de campo ou valor. Você pode criar operações mais complexas encadeando vários métodos.

O exemplo abaixo cria uma operação para encontrar pacientes no estado do Novo México, nos EUA, que foram a um consultório médico pelo menos uma vez. A operação executa as seguintes ações:

  • Verifica se o tamanho do valor da matriz visitDates é maior que 0 usando o método gt()

  • Verifica se o valor do campo state é "Novo México" usando o método eq()

O método and() vincula estas operações para que o estágio do pipeline corresponda apenas a documentos que atendam aos dois critérios.

current()
.getArray("visitDates")
.size()
.gt(of(0))
.and(current()
.getString("state")
.eq(of("New Mexico")))

Enquanto alguns estágios de agregação, como group(), aceitam operações diretamente, outros estágios esperam que você primeiro inclua sua operação em um método como computed() ou expr(). Esses métodos, que aceitam valores do tipo TExpression, permitem usar suas expressões em determinadas agregações.

Para concluir o estágio do pipeline de agregação, inclua sua expressão em um método de construtor de agregados. A seguinte lista fornece exemplos de como incluir sua expressão em métodos de construtor de agregados comuns:

  • match(expr(<expression>))

  • project(fields(computed("<field name>", <expression>)))

  • group(<expression>)

Para saber mais sobre esses métodos, consulte o guia Transforme seus dados com agregação .

Você pode utilizar estes métodos do construtor para definir valores para uso em expressões de agregação Kotlin.

Método
Descrição
Referencia o documento atual sendo processado pela aggregation pipeline.
Referencia o documento atual sendo processado pelo pipeline de agregação como um valor de mapa.
Retorna um tipo de MqlValue correspondente à primitiva fornecida.
Retorna uma matriz de MqlValue tipos correspondentes à matriz fornecida de primitivos.
Retorna um valor de entrada.
Retorna um valor de mapa vazio.
Retorna o valor nulo conforme está na API de consulta.

Importante

Quando você fornece um valor a um desses métodos, o driver o trata literalmente. Por exemplo, of("$x") representa o valor de string "$x", em vez de um campo denominado x.

Consulte qualquer uma das seções em Operações para obter exemplos de uso desses métodos.

As seções a seguir fornecem informações e exemplos de operações de expressão de agregação disponíveis no driver. As operações são categorizadas por finalidade e funcionalidade.

Cada seção tem uma tabela que descreve os métodos de agregação disponíveis no driver e os operadores de expressão correspondentes na API de query. Os nomes do método estão vinculados à documentação da API e os nomes do operador do agregação pipeline estão vinculados a descrições e exemplos na documentação manual do servidor. Embora cada método seja efetivamente equivalente ao operador de agregação correspondente, eles podem diferir nos parâmetros esperados e na implementação.

O exemplo em cada seção utiliza o método listOf() para criar um pipeline a partir do estágio de agregação . Em seguida, cada exemplo passa o pipeline para o método aggregate() de MongoCollection.

Observação

O driver gera uma expressão de API de query que pode ser diferente da expressão de API de query fornecida em cada exemplo. No entanto, ambas as expressões produzirão o mesmo resultado de agregação.

Importante

O driver não fornece métodos para todos os operadores de pipeline de agregação da API de consulta. Para usar uma operação não suportada em uma agregação, você deve definir a expressão inteira usando o tipo BSON Document .

Você pode executar uma operação aritmética em um valor do tipo MqlInteger ou MqlNumber utilizando os métodos descritos nesta seção.

Digamos que você tenha dados meteorológicos de um ano específico que inclua a medição de precipitação (em polegadas) para cada dia. Você deseja encontrar a precipitação média, em milímetros, para cada mês.

O operador multiply() multiplica o campo precipitation por 25.4 para converter o valor do campo em milímetros. O método acumulador avg() retorna a média como o campo avgPrecipMM . O método group() agrupa os valores por mês fornecidos no campo date de cada documento.

O seguinte código mostra o pipeline para essa agregação:

val month = current().getDate("date").month(of("UTC"))
val precip = current().getInteger("precipitation")
val results = collection.aggregate<Document>(
listOf(
Aggregates.group(
month,
Accumulators.avg("avgPrecipMM", precip.multiply(25.4))
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $group: {
_id: { $month: "$date" },
avgPrecipMM: {
$avg: { $multiply: ["$precipitation", 25.4] } }
} } ]

Você pode executar uma operação de array em um valor do tipo MqlArray utilizando os métodos descritos nesta seção.

Suponha que você tenha uma coleção de filmes, cada um dos quais contém uma array de documentos aninhados para os próximos horários de exibição. Cada documento aninhado contém uma array que representa o número total de assentos no teatro, em que a primeira entrada da array é o número de assentos premium e a segunda entrada é o número de assentos regulares. Cada documento aninhado também contém o número de ingressos que já foram comprados para o momento da exibição. Um documento nesta coleção pode se assemelhar ao seguinte:

{
"_id": ...,
"movie": "Hamlet",
"showtimes": [
{
"date": "May 14, 2023, 12:00 PM",
"seats": [ 20, 80 ],
"ticketsBought": 100
},
{
"date": "May 20, 2023, 08:00 PM",
"seats": [ 10, 40 ],
"ticketsBought": 34
}]
}

O método filter() exibe somente os resultados que correspondem ao predicado fornecido. Nesse caso, o predicado usa sum() para calcular o número total de assentos e compara esse valor com o número de ticketsBought usando o método lt() . O método project() armazena esses resultados filtrados como um novo campo de array availableShowtimes .

Dica

Você deve especificar o tipo de valores que uma array contém ao usar o método getArray() para trabalhar com os valores como qualquer tipo específico. Por exemplo, você deve especificar que uma array contém números inteiros para executar cálculos com esses números inteiros em outro lugar do seu aplicação.

O exemplo nesta seção especifica que a array seats contém valores do tipo MqlDocument para que ela possa extrair campos aninhados de cada entrada da array.

O seguinte código mostra o pipeline para essa agregação:

val showtimes = current().getArray<MqlDocument>("showtimes")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed("availableShowtimes", showtimes
.filter { showtime ->
val seats = showtime.getArray<MqlInteger>("seats")
val totalSeats = seats.sum { n -> n }
val ticketsBought = showtime.getInteger("ticketsBought")
val isAvailable = ticketsBought.lt(totalSeats)
isAvailable
})
)
)
)
)

Observação

Para melhorar a legibilidade, o exemplo anterior atribui valores intermediários às variáveis totalSeats e isAvailable . Se você não atribuir esses valores intermediários a variáveis, o código ainda produzirá resultados equivalentes.

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
availableShowtimes: {
$filter: {
input: "$showtimes",
as: "showtime",
cond: { $lt: [ "$$showtime.ticketsBought", { $sum: "$$showtime.seats" } ] }
} }
} } ]

Você pode executar uma operação booleana em um valor do tipo MqlBoolean utilizando os métodos descritos nesta seção.

Método
Operador de pipeline de agregação

Suponha que você queira classificar leituras de temperatura muito baixa ou alta (em graus Fahrenheit) como extremas.

O operador or() verifica se as temperaturas são extremas comparando o campo temperature com valores pré-definidos utilizando os métodos lt() e gt() . O método project() registra este resultado no campo extremeTemp .

O seguinte código mostra o pipeline para essa agregação:

val temperature = current().getInteger("temperature")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed(
"extremeTemp", temperature
.lt(of(10))
.or(temperature.gt(of(95)))
)
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
extremeTemp: { $or: [ { $lt: ["$temperature", 10] },
{ $gt: ["$temperature", 95] } ] }
} } ]

Você pode executar uma operação de comparação em um valor do tipo MqlValue utilizando os métodos descritos nesta seção.

Dica

O método cond() é semelhante ao operador ternário em Kotlin e você pode usá-lo para ramificações simples com base em valores booleanos. Use os métodos switchOn() para comparações mais complexas, como a realização de correspondência de padrões no tipo de valor ou outras verificações arbitrárias no valor.

O exemplo a seguir mostra um pipeline que corresponde a todos os documentos onde o campo location tem o valor "California":

val location = current().getString("location")
val results = collection.aggregate<Document>(
listOf(
Aggregates.match(
Filters.expr(location.eq(of("California")))
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $match: { location: { $eq: "California" } } } ]

Você pode executar uma operação condicional utilizando os métodos descritos nesta seção.

Suponha que você tenha uma collection de clientes com suas informações de adesão. Originalmente, os clientes eram membros ou não eram membros. Com o tempo, os níveis de adesão foram introduzidos e usados no mesmo campo. As informações armazenadas nesse campo podem ser de tipos diferentes; e recomendamos criar um valor padronizado que indique o nível de adesão.

O método switchOn() verifica cada cláusula em ordem. Se o valor corresponder ao tipo indicado pela cláusula, a cláusula determinará o valor da string correspondente ao nível de associação. Se o valor original for uma string, ele representará o nível de associação e este valor será utilizado. Se o tipo de dados for booleano, ele retornará Gold ou Guest para o nível de associação. Se o tipo de dados for uma array, ele retornará a string mais recente da array que corresponde ao nível de associação mais recente. Se o campo member for um tipo desconhecido, o método switchOn() fornecerá um valor padrão de Guest.

O seguinte código mostra o pipeline para essa agregação:

val member = current().getField("member")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed("membershipLevel",
member.switchOn { field ->
field
.isString { s -> s }
.isBoolean { b -> b.cond(of("Gold"), of("Guest")) }
.isArray { a -> a.last() }
.defaults { d -> of("Guest") }
})
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
membershipLevel: {
$switch: {
branches: [
{ case: { $eq: [ { $type: "$member" }, "string" ] }, then: "$member" },
{ case: { $eq: [ { $type: "$member" }, "bool" ] }, then: { $cond: {
if: "$member",
then: "Gold",
else: "Guest" } } },
{ case: { $eq: [ { $type: "$member" }, "array" ] }, then: { $last: "$member" } }
],
default: "Guest" } }
} } ]

Você pode aplicar funções personalizadas aos valores do tipo MqlValue utilizando os métodos descritos nesta seção.

Para melhorar a legibilidade e permitir a reutilização do código, mova o código redundante para métodos estáticos. No entanto, você não pode encadear métodos estáticos diretamente no Kotlin. O método passTo() permite encadear valores em métodos estáticos personalizados.

Método
Operador de pipeline de agregação
Nenhum operador correspondente

Suponha que você queira determinar o desempenho de uma turma em relação a alguns parâmetros. Você deseja encontrar a nota final média para cada turma e compará-la com os valores de parâmetro.

O método personalizado gradeAverage() a seguir recebe uma array de documentos e o nome de um campo inteiro compartilhado entre esses documentos. Ele calcula a média desse campo em todos os documentos da array fornecida e determina a média desse campo em todos os elementos da array fornecida. O método evaluate() compara um valor fornecido com dois limites de intervalo fornecidos e gera uma string de resposta com base na comparação dos valores:

fun gradeAverage(students: MqlArray<MqlDocument>, fieldName: String): MqlNumber {
val sum = students.sum { student -> student.getInteger(fieldName) }
val avg = sum.divide(students.size())
return avg
}
fun evaluate(grade: MqlNumber, cutoff1: MqlNumber, cutoff2: MqlNumber): MqlString {
val message = grade.switchOn { on ->
on
.lte(cutoff1) { g -> of("Needs improvement") }
.lte(cutoff2) { g -> of("Meets expectations") }
.defaults { g -> of("Exceeds expectations") }
}
return message
}

Dica

Usar o método passTo() permite reutilizar seus métodos personalizados para outras agregações. Por exemplo, você pode usar o método gradeAverage() para encontrar a média das notas de grupos de alunos filtrados por ano de ingresso ou distrito, não apenas pela classe. Da mesma forma, você pode usar o método evaluate() para avaliar o desempenho de um aluno específico ou o desempenho de toda a escola.

O método passArrayTo() pega uma array de todos os alunos e calcula a pontuação média usando o método gradeAverage() . Em seguida, o método passNumberTo() utiliza o método evaluate() para determinar o desempenho das salas de aula. Este exemplo armazena o resultado como o campo evaluation utilizando o método project() .

O seguinte código mostra o pipeline para essa agregação:

val students = current().getArray<MqlDocument>("students")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed("evaluation", students
.passArrayTo { s -> gradeAverage(s, "finalGrade") }
.passNumberTo { grade -> evaluate(grade, of(70), of(85)) })
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
evaluation: { $switch: {
branches: [
{ case: { $lte: [ { $avg: "$students.finalGrade" }, 70 ] },
then: "Needs improvement"
},
{ case: { $lte: [ { $avg: "$students.finalGrade" }, 85 ] },
then: "Meets expectations"
}
],
default: "Exceeds expectations" } }
} } ]

Você pode executar uma operação de conversão para converter entre determinados tipos de MqlValue utilizando os métodos descritos nesta seção.

Suponha que você queira ter uma coleção de dados dos alunos que inclua seus anos de graduação, que são armazenados como strings. Você deseja calcular o ano da reunião de cinco anos e armazenar este valor em um novo campo.

O método parseInteger() converte o graduationYear para um número inteiro de forma que add() possa calcular o ano de reunião. O método addFields() armazena este resultado como um novo campo reunionYear.

O seguinte código mostra o pipeline para essa agregação:

val students = current().getArray<MqlDocument>("students")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed("evaluation", students
.passArrayTo { s -> gradeAverage(s, "finalGrade") }
.passNumberTo { grade -> evaluate(grade, of(70), of(85)) })
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $addFields: {
reunionYear: {
$add: [ { $toInt: "$graduationYear" }, 5 ] }
} } ]

Você pode executar uma operação de data em um valor do tipo MqlDate usando os métodos descritos nesta seção.

Suponha que você tenha dados sobre entregas de pacote e queira fazer a correspondência entre as entregas que ocorreram em qualquer segunda-feira na zona "America/New_York" .

Se o campo deliveryDate contiver qualquer string que represente datas válidas, como "2018-01-15T16:00:00Z" ou "Jan 15, 2018, 12:00 PM EST", você poderá usar o método parseDate() para converter as strings em tipos de data.

O método dayOfWeek() determina qual dia da semana é uma data e, em seguida, converte-a em um número. A atribuição de número usa 0 para média domingo ao usar o fuso horário "America/New_York" . O método eq() compara esse valor com 2, ou segunda-feira.

O seguinte código mostra o pipeline para essa agregação:

val deliveryDate = current().getString("deliveryDate")
val results = collection.aggregate<Document>(
listOf(
Aggregates.match(
Filters.expr(
deliveryDate
.parseDate()
.dayOfWeek(of("America/New_York"))
.eq(of(2))
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $match: {
$expr: {
$eq: [ {
$dayOfWeek: {
date: { $dateFromString: { dateString: "$deliveryDate" } },
timezone: "America/New_York" }},
2
] }
} } ]

Você pode executar uma operação de documento em um valor do tipo MqlDocument usando os métodos descritos nesta seção.

Digamos que você tenha um conjunto de dados de cliente legado que contém endereços como documentos secundários no campo mailing.address . Você deseja encontrar todos os clientes que vivem no estado de Washington. Um documento desse conjunto poderia ser como o exemplo abaixo:

{
"_id": ...,
"customer.name": "Mary Kenneth Keller",
"mailing.address":
{
"street": "601 Mongo Drive",
"city": "Vasqueztown",
"state": "CO",
"zip": 27017
}
}

O método getDocument() recupera o campo mailing.address como um documento para que o campo state aninhado possa ser recuperado com o método getString() . O método eq() verifica se o valor do campo state é "WA".

O seguinte código mostra o pipeline para essa agregação:

val address = current().getDocument("mailing.address")
val results = collection.aggregate<Document>(
listOf(
Aggregates.match(
Filters.expr(
address
.getString("state")
.eq(of("WA"))
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[
{ $match: {
$expr: {
$eq: [{
$getField: {
input: { $getField: { input: "$$CURRENT", field: "mailing.address"}},
field: "state" }},
"WA" ]
}}}]

Você pode executar uma operação de mapa em um valor de tipo MqlMap ou MqlEntry utilizando os métodos descritos nesta seção.

Método
Operador de pipeline de agregação
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente

Suponha que você tenha uma coleção de dados de estoque em que cada documento representa um item individual que você é responsável por fornecer. Cada documento contém um campo que é um mapa de todos os seus armazéns e quantas cópias eles têm em seu inventário do item. Você deseja determinar o número total de cópias de itens que você tem em todos os armazéns. Um documento desse conjunto poderia ser como o exemplo abaixo:

{
"_id": ...,
"item": "notebook"
"warehouses": [
{ "Atlanta", 50 },
{ "Chicago", 0 },
{ "Portland", 120 },
{ "Dallas", 6 }
]
}

O método entries() retorna as entradas do mapa no campo warehouses como uma array. O método sum() calcula o valor total dos itens baseado nos valores na array recuperada com o método getValue() . Este exemplo armazena o resultado como o novo campo totalInventory utilizando o método project().

O seguinte código mostra o pipeline para essa agregação:

val warehouses = current().getMap<MqlNumber>("warehouses")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed("totalInventory", warehouses
.entries()
.sum { v -> v.getValue() })
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
totalInventory: {
$sum: {
$getField: { $objectToArray: "$warehouses" },
} }
} } ]

Você pode executar uma operação de string em um valor do tipo MqlString utilizando os métodos descritos nesta seção.

Suponha que você queira gerar nomes de usuário em letras minúsculas para os funcionários de uma empresa a partir dos sobrenomes e ID dos funcionários.

O método append() combina os campos lastName e employeeID em um único nome de usuário, enquanto o método toLower() torna todo o nome de usuário minúsculo. Este exemplo armazena o resultado como um novo campo username utilizando o método project().

O seguinte código mostra o pipeline para essa agregação:

val lastName = current().getString("lastName")
val employeeID = current().getString("employeeID")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed(
"username", lastName
.append(employeeID)
.toLower()
)
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
username: {
$toLower: { $concat: ["$lastName", "$employeeID"] } }
} } ]

Você pode executar uma operação de verificação de tipo em um valor do tipo MqlValue utilizando os métodos descritos nesta seção.

Estes métodos não retornam valores booleanos. Em vez disso, você fornece um valor padrão que corresponde ao tipo especificado pelo método. Se o valor marcado corresponder ao tipo de método, o valor marcado será retornado. Caso contrário, o valor predefinido fornecido é devolvido. Para programar lógica de ramificação com base no tipo de dados, consulte switchOn().

Método
Operador de pipeline de agregação
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente
Nenhum operador correspondente

Suponha que você tenha uma collection de dados de classificação. Uma versão anterior do esquema de avaliação permitia que os usuários enviassem avaliações negativas sem uma classificação por estrelas. Recomendamos converter essas avaliações negativas sem uma classificação por estrelas para ter o valor mínimo de 1 estrela.

O método isNumberOr() retorna o valor de rating ou um valor de 1 se rating não for um número ou for nulo. O método project() retorna este valor como um novo campo numericalRating.

O seguinte código mostra o pipeline para essa agregação:

val rating = current().getField("rating")
val results = collection.aggregate<Document>(
listOf(
Aggregates.project(
Projections.fields(
Projections.computed(
"numericalRating", rating
.isNumberOr(of(1))
)
)
)
)
)

O código a seguir fornece um pipeline de agregação equivalente na API de consulta:

[ { $project: {
numericalRating: {
$cond: { if: { $isNumber: "$rating" },
then: "$rating",
else: 1
} }
} } ]

Voltar

Transforme seus dados com agregação