Overview
En esta guía, puedes aprender cómo usar el driver de sincronizar de Kotlin para crear expresiones para usar en pipeline de agregación. Puedes realizar operaciones de expresión con métodos Kotlin detectables y seguros en cuanto a tipos en lugar de utilizar documentos BSON. Dado que estos métodos siguen el patrón de interfaz fluida, puedes encadenar operaciones de agregación para crear un código compacto y naturalmente legible.
Las operaciones de esta guía utilizan métodos del com.mongodb.client.model.mql paquete. Estos métodos proporcionan una forma idiomática de utilizar la API de query, el mecanismo mediante el cual el driver interactúa con una implementación de MongoDB. Para obtener más información sobre la API de query, consulta la documentación del manual del servidor.
Cómo utilizar operaciones
Los ejemplos de esta guía suponen que incluye las siguientes importaciones en su 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 acceder a los campos de documento en una expresión, debe hacer referencia al documento actual que está siendo procesado por la canalización de agregación mediante el uso de
current() método. Para acceder al valor de un campo, debes usar el método apropiado según el tipo, como getString() o getDate(). Cuando especificas el tipo para un campo, aseguras que el controlador proporcione solo aquellos métodos que sean compatibles con ese tipo. El siguiente código muestra cómo hacer referencia a un campo string llamado name:
current().getString("name")
Para especificar un valor en una operación, pásalo al método constructor of() para convertirlo en un tipo válido. El código siguiente muestra cómo referenciar un valor de 1.0:
of(1.0)
Para crear una operación, encadene un método a su campo o referencia de valor. Puede compilar operaciones más complejas encadenando varios métodos.
El siguiente ejemplo crea una operación para encontrar pacientes en Nuevo México que hayan ido al consultorio del doctor al menos una vez. La operación realiza las siguientes acciones:
Comprueba si el tamaño del valor del arreglo
visitDateses mayor que0utilizando el métodogt()Comprueba si el valor del campo
statees “Nuevo México” utilizando el métodoeq()
El método and() vincula estas operaciones para que la etapa de la pipeline coincida solo con los documentos que cumplan ambos criterios.
current() .getArray("visitDates") .size() .gt(of(0)) .and(current() .getString("state") .eq(of("New Mexico")))
Mientras que algunos etapas de agregación, como group(), aceptan operaciones directamente, otras etapas esperan que primero incluyas tu operación en un método como computed() o expr(). Estos métodos, que aceptan valores de tipo TExpression, permiten utilizar expresiones en ciertas agregaciones.
Para completar la etapa del pipeline de agregación, incluye tu expresión en un método del generador de agregados. La siguiente lista proporciona ejemplos de cómo incluir tu expresión en métodos comunes del builder de agregados:
match(expr(<expression>))project(fields(computed("<field name>", <expression>)))group(<expression>)
Para obtener más información sobre estos métodos, consulte la Guía deoperaciones de agregación.
Métodos constructores
Puede utilizar estos métodos constructores para definir valores para usar en expresiones de agregación de Kotlin.
Método | Descripción |
|---|---|
Hace referencia al documento actual que está siendo procesado por la pipeline de agregación. | |
Hace referencia al documento actual que está siendo procesado por el canal de agregación como un valor de mapa. | |
Devuelve un tipo | |
Devuelve una matriz de tipos | |
Devuelve un valor de entrada. | |
Devuelve un mapa vacío. | |
Devuelve el valor nulo tal como existe en la API de consulta. |
Importante
Al asignar un valor a uno de estos métodos, el controlador lo interpreta literalmente. Por ejemplo, of("$x") representa el valor de la cadena "$x", en lugar de un campo llamado x.
Consulte cualquiera de las secciones de Operaciones para ver ejemplos que utilizan estos métodos.
Operaciones
Las siguientes secciones proporcionan información y ejemplos acerca de las operaciones de expresiones de agregación disponibles en el controlador. Las operaciones se categorizan por propósito y funcionalidad.
Cada sección cuenta con una tabla que describe los métodos de agregación disponibles en el controlador y los operadores de expresión correspondientes en la API de Query. Los nombres de los métodos enlazan con la documentación de la API y los nombres de los operadores de la pipeline de agregación enlazan con descripciones y ejemplos en la documentación del Manual del Servidor. Aunque cada método es efectivamente equivalente al operador de agregación correspondiente, pueden diferir en los parámetros esperados y en la implementación.
El ejemplo en cada sección utiliza el método listOf() para crear una pipeline a partir de la etapa de agregación. Luego, cada ejemplo pasa por el pipeline al método aggregate() de MongoCollection.
Nota
El controlador genera una expresión de la API de consulta que puede ser diferente de la proporcionada en cada ejemplo. Sin embargo, ambas expresiones producirán el mismo resultado de agregación.
Importante
El driver no proporciona métodos para todos los operadores de pipeline de agregación en la API de query. Para utilizar una operación no soportada en una agregación, debes definir toda la expresión usando el tipo BSON Document.
Operaciones aritméticas
Puedes realizar una operación aritmética en un valor de tipo MqlInteger o MqlNumber utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
Supongamos que tiene datos meteorológicos de un año específico que incluyen la precipitación diaria (en pulgadas). Quiere calcular la precipitación media, en milímetros, de cada mes.
El operador multiply() multiplica el campo precipitation por 25.4 para convertir el valor del campo a milímetros. El método del acumulador avg() devuelve el promedio como el campo avgPrecipMM. El método group() agrupa los valores por mes que se dan en el campo date de cada documento.
El siguiente código muestra el pipeline para esta agregación:
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)) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $group: { _id: { $month: "$date" }, avgPrecipMM: { $avg: { $multiply: ["$precipitation", 25.4] } } } } ]
Operaciones de arreglos
Puedes realizar una operación de arreglo en un valor de tipo MqlArray utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
Supón que cuentas con una colección de películas, cada una de las cuales contiene un arreglo de documentos anidados con los próximos horarios de funciones. Cada documento anidado contiene un arreglo que representa el número total de asientos en el teatro, donde la primera entrada del arreglo es el número de asientos premium y la segunda entrada es el número de asientos regulares. Cada documento anidado también contiene el número de tickets que ya se han comprado para la función. Un documento en esta colección podría parecerse al siguiente:
{ "_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 }] }
El método filter() solo muestra los resultados que coinciden con el predicado proporcionado. En este caso, el predicado utiliza sum() para calcular el número total de butacas y compara ese valor con el número de ticketsBought usando el método lt(). El método project() almacena estos resultados filtrados como un nuevo campo de arreglo availableShowtimes.
Tip
Debes especificar el tipo de valores que contiene un arreglo cuando utilices el método getArray() para trabajar con los valores como cualquier tipo específico. Por ejemplo, deberás especificar que un arreglo contiene enteros para realizar cálculos con esos enteros en otra parte de tu aplicación.
El ejemplo en esta sección especifica que el arreglo seats contiene valores de tipo MqlDocument para que pueda extraer campos anidados de cada entrada del arreglo.
El siguiente código muestra el pipeline para esta agregación:
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 }) ) ) ) )
Nota
Para mejorar la legibilidad, el ejemplo anterior asigna valores intermedios a las variables totalSeats y isAvailable. Si no se asignan estos valores intermedios a las variables, el código seguirá generando resultados equivalentes.
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $project: { availableShowtimes: { $filter: { input: "$showtimes", as: "showtime", cond: { $lt: [ "$$showtime.ticketsBought", { $sum: "$$showtime.seats" } ] } } } } } ]
Operaciones booleanas
Puede realizar una operación booleana en un valor de tipo MqlBoolean utilizando los métodos descritos en esta sección.
Supón que quieres clasificar las mediciones de temperatura atmosférica muy bajas o altas (en grados Fahrenheit) como extremas.
El operador or() comprueba si las temperaturas son extremas comparando el campo temperature con valores predefinidos utilizando los métodos lt() y gt(). El método project() registra este resultado en el campo extremeTemp.
El siguiente código muestra el pipeline para esta agregación:
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))) ) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $project: { extremeTemp: { $or: [ { $lt: ["$temperature", 10] }, { $gt: ["$temperature", 95] } ] } } } ]
Operaciones de comparación
Puede realizar una operación de comparación en un valor de tipo MqlValue utilizando los métodos descritos en esta sección.
Tip
El método cond() es similar al operador ternario de Kotlin y se puede usar para ramificaciones simples basadas en valores booleanos. Use los métodos switchOn() para comparaciones más complejas, como la búsqueda de patrones en el tipo de valor u otras comprobaciones arbitrarias del valor.
Método | Operador de canalización de agregación |
|---|---|
El siguiente ejemplo muestra un pipeline que coincide con todos los documentos donde el campo location tiene el valor "California":
val location = current().getString("location") val results = collection.aggregate<Document>( listOf( Aggregates.match( Filters.expr(location.eq(of("California"))) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $match: { location: { $eq: "California" } } } ]
Operaciones condicionales
Puede realizar una operación condicional utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
Supongamos que tiene una colección de clientes con su información de membresía. Originalmente, los clientes eran miembros o no. Con el tiempo, se introdujeron los niveles de membresía y se utilizó el mismo campo. La información almacenada en este campo puede ser de varios tipos diferentes, y desea crear un valor estandarizado que indique su nivel de membresía.
El método switchOn() verifica cada cláusula por orden. Si el valor coincide con el tipo indicado por la cláusula, entonces la cláusula determina el valor de string correspondiente al nivel de membresía. Si el valor original es una string, representa el nivel de membresía y se utiliza ese valor. Si el tipo de dato es booleano, retorna Gold o Guest para el nivel de membresía. Si el tipo de dato es un arreglo, retorna el string más reciente del arreglo que coincide con el nivel de membresía más reciente. Si el campo member es de un tipo desconocido, el método switchOn() proporciona un valor por defecto de Guest.
El siguiente código muestra el pipeline para esta agregación:
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") } }) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $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" } } } } ]
Operaciones Convenientes
Puedes aplicar funciones personalizadas a valores de tipo MqlValue utilizando los métodos descritos en esta sección.
Para mejorar la legibilidad y permitir la reutilización del código, puede mover el código redundante a métodos estáticos. Sin embargo, no puedes encadenar directamente métodos estáticos en Kotlin. El método passTo() te permite encadenar valores en métodos estáticos personalizados.
Método | Operador de canalización de agregación |
|---|---|
No hay operador correspondiente |
Supongamos que quieres determinar cómo se está desempeñando una clase en comparación con algunos puntos de referencia. Quiere encontrar la calificación final promedio para cada clase y compararla con los valores de referencia.
El siguiente método personalizado gradeAverage() toma una matriz de documentos y el nombre de un campo entero compartido entre ellos. Calcula el promedio de ese campo en todos los documentos de la matriz proporcionada y determina el promedio de ese campo en todos los elementos de la matriz proporcionada. El método evaluate() compara un valor proporcionado con dos límites de rango proporcionados y genera una cadena de respuesta basada en la comparación de los 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 }
Tip
Utilizar el método passTo() permite reutilizar los métodos personalizados para otras agregaciones. Por ejemplo, se puede utilizar el método gradeAverage() para encontrar el promedio de calificaciones de grupos de estudiantes filtrados por año de ingreso o distrito, no solo por clases. De manera similar, puedes utilizar el método evaluate() para evaluar el rendimiento de un estudiante individual o del rendimiento de toda una escuela.
El método passArrayTo() toma un arreglo de todos los estudiantes y calcula la calificación promedio utilizando el método gradeAverage(). Luego, el método passNumberTo() utiliza el método evaluate() para determinar el rendimiento de las clases. Este ejemplo almacena el resultado como el campo evaluation utilizando el método project().
El siguiente código muestra el pipeline para esta agregación:
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)) }) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $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" } } } } ]
Operaciones de conversión
Puede realizar una operación de conversión para convertir entre ciertos tipos MqlValue utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
No hay operador correspondiente | |
No hay operador correspondiente | |
Supongamos que deseas tener una colección de datos estudiantiles que incluya sus años de graduación, los cuales se almacenan como cadenas de texto. Se desea calcular el año de su reunión de cinco años y almacenar este valor en un campo nuevo.
El método parseInteger() convierte el graduationYear en un número entero para que add() pueda calcular el año de la reunión. El método addFields() almacena este resultado como un nuevo campo reunionYear.
El siguiente código muestra el pipeline para esta agregación:
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)) }) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $addFields: { reunionYear: { $add: [ { $toInt: "$graduationYear" }, 5 ] } } } ]
Operaciones de fechas
Puedes realizar una operación de fecha en un valor de tipo MqlDate utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
Supón que tienes datos sobre entregas de paquetes y quieres hacer coincidir las entregas que ocurrieron cualquier lunes en la zona horaria "America/New_York".
Si el campo deliveryDate contiene valores de cadena que representan fechas válidas, como "2018-01-15T16:00:00Z" o "Jan 15, 2018, 12:00
PM EST", puede utilizar el método parseDate() para convertir las cadenas en tipos de fecha.
El método dayOfWeek() determina qué día de la semana es una fecha, luego la convierte en un número. La asignación numérica utiliza 0 para significar domingo al usar la zona horaria "America/New_York". El método eq() compara este valor con 2, o Lunes.
El siguiente código muestra el pipeline para esta agregación:
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)) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $match: { $expr: { $eq: [ { $dayOfWeek: { date: { $dateFromString: { dateString: "$deliveryDate" } }, timezone: "America/New_York" }}, 2 ] } } } ]
Operaciones de documentos
Puede realizar una operación de documento en un valor de tipo MqlDocument utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
No hay operador correspondiente | |
Supongamos que tienes una colección de datos de clientes heredados que incluye direcciones como documentos secundarios bajo el campo mailing.address. Quieres encontrar a todos los clientes que viven en el estado de Washington. Un documento en esta colección podría tener el siguiente aspecto:
{ "_id": ..., "customer.name": "Mary Kenneth Keller", "mailing.address": { "street": "601 Mongo Drive", "city": "Vasqueztown", "state": "CO", "zip": 27017 } }
El método getDocument() recupera el campo mailing.address como documento, por lo que el campo state anidado se puede recuperar con el método getString(). El método eq() comprueba si el valor del campo state es "WA".
El siguiente código muestra el pipeline para esta agregación:
val address = current().getDocument("mailing.address") val results = collection.aggregate<Document>( listOf( Aggregates.match( Filters.expr( address .getString("state") .eq(of("WA")) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $match: { $expr: { $eq: [{ $getField: { input: { $getField: { input: "$$CURRENT", field: "mailing.address"}}, field: "state" }}, "WA" ] }}}]
Operaciones de mapas
Puede realizar una operación de mapa en un valor de tipo MqlMap o MqlEntry utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente |
Supongamos que tiene una colección de datos de inventario donde cada documento representa un artículo individual que usted es responsable de suministrar. Cada documento contiene un campo que representa un mapa de todos sus almacenes y cuántas copias tienen en inventario del artículo. Quiere determinar el número total de copias de artículos que tiene en todos los almacenes. Un documento de esta colección podría ser similar al siguiente:
{ "_id": ..., "item": "notebook" "warehouses": [ { "Atlanta", 50 }, { "Chicago", 0 }, { "Portland", 120 }, { "Dallas", 6 } ] }
El método entries() devuelve las entradas del mapa en el campo warehouses como un arreglo. El método sum() calcula el valor total de los ítems en función de los valores del arreglo recuperados con el método getValue(). Este ejemplo almacena el resultado como el nuevo campo totalInventory usando el método project().
El siguiente código muestra el pipeline para esta agregación:
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() }) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $project: { totalInventory: { $sum: { $getField: { $objectToArray: "$warehouses" }, } } } } ]
Operaciones de cadenas
Puedes realizar una operación de string en un valor de tipo MqlString utilizando los métodos descritos en esta sección.
Método | Operador de canalización de agregación |
|---|---|
Supongamos que desea generar nombres de usuario en minúsculas para los empleados de una empresa a partir de los apellidos y las identificaciones de los empleados.
El método append() combina los campos lastName y employeeID en un solo nombre de usuario, mientras que el método toLower() convierte todo el nombre de usuario en minúsculas. Este ejemplo almacena el resultado como un nuevo campo username usando el método project().
El siguiente código muestra el pipeline para esta agregación:
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() ) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $project: { username: { $toLower: { $concat: ["$lastName", "$employeeID"] } } } } ]
Operaciones de comprobación de tipos
Puedes realizar una operación de comprobación de tipo en un valor de tipo MqlValue utilizando los métodos descritos en esta sección.
Estos métodos no retornan valores booleanos. En su lugar, proporciona un valor por defecto que coincida con el tipo especificado por el método. Si el valor comprobado coincide con el tipo de método, se devuelve el valor comprobado. De lo contrario, se devuelve el valor por defecto proporcionado. Para programar una lógica de bifurcación según el tipo de datos, consulte switchOn().
Método | Operador de canalización de agregación |
|---|---|
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente | |
No hay operador correspondiente |
Suponga que tiene una colección de datos de calificaciones. Una versión temprana del esquema de revisiones permitía a los usuarios enviar revisiones negativas sin una calificación en estrellas. Quiere convertir cualquiera de estas revisiones negativas sin calificación con estrellas para que tengan el valor mínimo de una estrella.
El método isNumberOr() devuelve el valor de rating, o un valor de 1 si rating no es un número o es nulo. El método project() devuelve este valor como un nuevo campo numericalRating.
El siguiente código muestra el pipeline para esta agregación:
val rating = current().getField("rating") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed( "numericalRating", rating .isNumberOr(of(1)) ) ) ) ) )
El siguiente código proporciona un pipeline de agregación equivalente en la Query API:
[ { $project: { numericalRating: { $cond: { if: { $isNumber: "$rating" }, then: "$rating", else: 1 } } } } ]