Agregación lenta: ordenar documentos según objetos filtrados y anidados.
Mis documentos se ven así:
docs = [
{
'id': 1,
'xs': [
{'name': 'foo', 'value': 0.5},
{'name': 'bar', 'value': 0.3},
],
},
{
'id': 2,
'xs': [
{'name': 'foo', 'value': 0.9},
{'name': 'bar', 'value': 0.1},
],
},
]
Me gustaría obtener los primeros N documentos (ordenados descendientemente + límite) según su xs.value
, para cada valor individual de xs.name
.
Intenté hacer esto con $unwind
y $sort
, pero se siente algo lento. Tengo alrededor de 6000 documentos y cada uno de ellos tiene 20 elementos en xs
, y tarda aproximadamente 3 minutos para la siguiente agregación:
steps = [
{'$match': query},
{'$unwind': '$xs'},
{'$match': {'xs.name': 'foo'}},
{'$sort': {'xs.value': -1}},
{'$limit': 10}
]
¿Algún consejo sobre cómo acelerar esto? Estoy pensando que podría escribir la agregación de muchas maneras, pero no estoy seguro de cuál tiene el mejor potencial para acelerar.
¡Gracias!
Editar:
Los siguientes pasos:
{'$match': {**query, 'xs.name': 'foo'}},
{'$unwind': '$xs'},
{'$match': {'xs.name': 'foo'}},
{'$limit': 1},
tardan aproximadamente un minuto en completarse, sin siquiera ordenar.
Los índices se ven así:
'xs.name': {'v': 2, 'key': [('xs.name', 1)]},
'xs.value-1': {'v': 2, 'key': [('xs.value', -1)]}}
Editar 2:
Otro intento:
{'$match': query},
{'$project': {
'items': {
'$filter': {'input': '$xs', 'as': 'this', 'cond': {'$eq': ['$$this.name', "foo"]}}
},
}},
{'$limit': 1},
Es muy rápido, pero agregar esto:
{'$sort': {'xs.value': -1}},
antes del limit
lo hace bastante lento.
davy.ai
Una solución potencial para acelerar la agregación podría ser utilizar el operador
$group
en lugar de$unwind
. Aquí tienes un ejemplo de una cadena de agregación que hace esto:Esta cadena de agregación primero realiza un
$match
de los documentos que coinciden con la consulta, luego$unwind
el arregloxs
para crear un documento para cada uno de sus elementos. Luego$sort
porxs.value
para asegurarse de que los valores más altos estén en la parte superior. En lugar de$limit
, utiliza$group
para agrupar los documentos porxs.name
, y para cada grupo, almacena los valoresid
yxs
en un arreglo llamadodocs
. Luego, proyecta solo los 10 valores principales para cada grupo utilizando$slice
y$unwind
para aplanar el arreglo. Finalmente, utiliza$replaceRoot
para promover el objetodocs
al nivel de la raíz y descartar el campo_id
.Esta cadena de agregación debería ser más rápida porque evita la operación adicional
$match
en tu cadena original y utiliza$group
en lugar de$unwind
para agrupar los documentos porxs.name
en lugar de crear un nuevo documento para cada valor. ¡Avísame si te ayuda!