es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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.

Tags:  ,

Answer

  1. Avatar for 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:

    steps = [
      {'$match': query},
      {'$unwind': '$xs'},
      {'$sort': {'xs.value': -1}},
      {'$group': {
          '_id': '$xs.name',
          'docs': {'$push': {
              'id': '$id',
              'xs': {'name': '$xs.name', 'value': '$xs.value'}
          }},
      }},
      {'$project': {'docs': {'$slice': ['$docs', 10]}}},
      {'$unwind': '$docs'},
      {'$replaceRoot': {'newRoot': '$docs'}},
    ]
    

    Esta cadena de agregación primero realiza un $match de los documentos que coinciden con la consulta, luego $unwind el arreglo xs para crear un documento para cada uno de sus elementos. Luego $sort por xs.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 por xs.name, y para cada grupo, almacena los valores id y xs en un arreglo llamado docs. Luego, proyecta solo los 10 valores principales para cada grupo utilizando $slice y $unwind para aplanar el arreglo. Finalmente, utiliza $replaceRoot para promover el objeto docs 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 por xs.name en lugar de crear un nuevo documento para cada valor. ¡Avísame si te ayuda!

Comments are closed.