Si necesita calcular algo como esto en tiempo de ejecución, con contenido "filtrado" de la matriz que determina el orden de clasificación, entonces es mejor que haga algo con .aggregate()
para remodelar y determinar un valor de clasificación como este:
db.collection.aggregate([
// Pre-filter the array elements
{ "$project": {
"tags": 1,
"score": {
"$setDifference": [
{ "$map": {
"input": "$tags",
"as": "tag",
"in": {
"$cond": [
{ "$eq": [ "$$el.id", "t1" ] },
"$$el.score",
false
]
}
}},
[false]
]
}
}},
// Unwind to denormalize
{ "$unwind": "$score" },
// Group back the "max" score
{ "$group": {
"_id": "$_id",
"tags": { "$first": "$tags" },
"score": { "$max": "$score" }
}},
// Sort descending by score
{ "$sort": { "score": -1 } }
])
Donde la primera parte de la canalización se usa para "filtrar previamente" el contenido de la matriz (además de mantener el campo original) solo para aquellos valores de "puntuación" donde la identificación es igual a "t1". Esto se hace procesando $map
que aplica una condición a cada elemento a través de $cond
para determinar si devolver la "puntuación" para ese elemento o false
.
El $setDifference
la operación hace una comparación con una matriz de un solo elemento [false]
que elimina efectivamente cualquier false
valores devueltos desde el $map
. Como un "conjunto", esto también elimina las entradas duplicadas, pero para el propósito de clasificación aquí es algo bueno.
Con la matriz reducida y remodelada a valores, procesa $unwind
listo para la siguiente etapa para tratar los valores como elementos individuales. El $group
la etapa esencialmente aplica $max
en la "puntuación" para devolver el valor más alto contenido en los resultados filtrados.
Entonces solo es cuestión de aplicar el $sort
sobre el valor determinado para ordenar los documentos. Naturalmente, si quisiera esto al revés, use $min
y ordenar en orden ascendente en su lugar.
Por supuesto, agregue un $match
etapa al principio si todo lo que realmente quiere son documentos que realmente contengan valores "t1" para id
dentro de las etiquetas. Pero esa parte es de menor relevancia para la clasificación de los resultados filtrados que desea lograr.
La alternativa al cálculo es hacerlo todo mientras escribe entradas en la matriz en los documentos. Un poco desordenado, pero es algo como esto:
db.collection.update(
{ "_id": docId },
{
"$push": { "tags": { "id": "t1", "score": 60 } },
"$max": { "maxt1score": 60 },
"$min": { "mint1score": 60 }
}
)
Aquí el $max
El operador de actualización solo establece el valor para el campo especificado si el nuevo valor es mayor que el valor existente o, de lo contrario, aún no existe ninguna propiedad. El caso inverso es cierto para $min
, donde solo si es menor que se reemplazará con el nuevo valor.
Por supuesto, esto tendría el efecto de agregar varias propiedades adicionales a los documentos, pero el resultado final es que la clasificación se simplifica enormemente:
db.collection.find().sort({ "maxt1score": -1 })
Y se ejecutará mucho más rápido que calcular con una canalización de agregación.
Así que considere los principios de diseño. Los datos estructurados en matrices en los que desea resultados filtrados y emparejados para ordenar significa calcular en tiempo de ejecución para determinar qué valor ordenar. Agregar propiedades adicionales al documento en .update()
significa que simplemente puede hacer referencia a esas propiedades para ordenar directamente los resultados.