sql >> Base de Datos >  >> NoSQL >> MongoDB

Ordenación de MongoDB vs ordenación $ agregada en el índice de matriz

El marco de agregación simplemente no "trata" las matrices de la misma manera que se aplica a .find() consultas en general. Esto no solo es cierto para operaciones como .sort() , pero también con otros operadores, a saber, $slice , aunque ese ejemplo está a punto de corregirse (más adelante).

Por lo tanto, es casi imposible tratar con cualquier cosa utilizando la forma de "notación de puntos" con un índice de una posición de matriz como la que tiene. Pero hay una forma de evitar esto.

Lo que "puede" hacer es básicamente calcular cuál es realmente el elemento de matriz "nth" como un valor, y luego devolverlo como un campo que se puede ordenar:

  db.test.aggregate([
    { "$unwind": "$items" },
    { "$group": { 
      "_id": "$_id",
      "items": { "$push": "$items" },
      "itemsCopy":  { "$push": "$items" },
      "first": { "$first": "$items" }
    }},
    { "$unwind": "$itemsCopy" },
    { "$project": {
      "items": 1,
      "itemsCopy": 1,
      "first": 1,
      "seen": { "$eq": [ "$itemsCopy", "$first" ] }
    }},
    { "$match": { "seen": false } },
    { "$group": {
      "_id": "$_id",
      "items": { "$first": "$items" },
      "itemsCopy": { "$push": "$itemsCopy" },
      "first": { "$first": "$first" },
      "second": { "$first": "$itemsCopy" }
    }},
    { "$sort": { "second": -1 } }
  ])

Es un enfoque horrible e "iterable" en el que esencialmente "pasas por" cada elemento de la matriz obteniendo el $first coincidencia por documento de la matriz después del procesamiento con $unwind . Luego, después de $unwind de nuevo, prueba para ver si los elementos de la matriz son los mismos que los que ya se han "visto" en las posiciones de la matriz identificadas.

Es terrible, y peor para cuantas más posiciones quieras mover, pero obtiene el resultado:

{ "_id" : 2, "items" : [ 0, 3, 4 ], "itemsCopy" : [ 3, 4 ], "first" : 0, "second" : 3 }
{ "_id" : 1, "items" : [ 1, 2, 0 ], "itemsCopy" : [ 2, 0 ], "first" : 1, "second" : 2 }
{ "_id" : 3, "items" : [ 2, 1, 5 ], "itemsCopy" : [ 1, 5 ], "first" : 2, "second" : 1 }

Afortunadamente, los próximos lanzamientos de MongoDB (como están actualmente disponibles en los lanzamientos de desarrollo) obtienen una "solución" para esto. Puede que no sea la solución "perfecta" que deseas, pero resuelve el problema básico.

Hay un nuevo $slice operador disponible para el marco de agregación allí, y devolverá los elementos requeridos de la matriz desde las posiciones indexadas:

  db.test.aggregate([
    { "$project": {
      "items": 1,
      "slice": { "$slice": [ "$items",1,1 ] }
    }},
    { "$sort": { "slice": -1 } }
  ])

Que produce:

{ "_id" : 2, "items" : [ 0, 3, 4 ], "slice" : [ 3 ] }
{ "_id" : 1, "items" : [ 1, 2, 0 ], "slice" : [ 2 ] }
{ "_id" : 3, "items" : [ 2, 1, 5 ], "slice" : [ 1 ] }

Entonces puede notar que como un "segmento", el resultado sigue siendo una "matriz", sin embargo, el $sort en el marco de agregación siempre ha utilizado la "primera posición" de la matriz para ordenar los contenidos. Eso significa que con un valor singular extraído de la posición indexada (al igual que el procedimiento largo anterior), el resultado se ordenará como espera.

Los casos finales aquí son así como funciona. O viva con el tipo de operaciones que necesita desde arriba para trabajar con una posición indexada de la matriz, o "espere" hasta que una nueva versión brillante venga a su rescate con mejores operadores.