MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Tri MongoDB vs agrégat $ sort sur l'index du tableau

Le framework d'agrégation ne "traite" pas les tableaux de la même manière qu'il est appliqué à .find() requêtes en général. Ce n'est pas seulement vrai des opérations comme .sort() , mais aussi avec d'autres opérateurs, et notamment $slice , bien que cet exemple soit sur le point d'être corrigé (plus tard).

Il est donc pratiquement impossible de traiter quoi que ce soit en utilisant la forme "notation par points" avec un index d'une position de tableau comme vous l'avez fait. Mais il existe un moyen de contourner cela.

Ce que vous "pouvez" faire, c'est essentiellement déterminer ce que le "nième" élément de tableau est réellement en tant que valeur, puis le renvoyer sous la forme d'un champ pouvant être trié :

  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 } }
  ])

C'est une approche horrible et "itérable" où vous "parcourez" essentiellement chaque élément du tableau en obtenant le $first correspondance par document du tableau après traitement avec $unwind . Puis après $unwind encore une fois, vous testez pour voir si ces éléments de tableau sont les mêmes que ceux déjà "vus" à partir des positions de tableau identifiées.

C'est terrible, et pire pour les positions que vous souhaitez déplacer, mais cela donne le résultat :

{ "_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 }

Heureusement, les prochaines versions de MongoDB (actuellement disponibles dans les versions de développement) obtiennent un "correctif" pour cela. Ce n'est peut-être pas la solution "parfaite" que vous désirez, mais cela résout le problème de base.

Il y a un nouveau $slice disponible pour le cadre d'agrégation ici, et il renverra le ou les éléments requis du tableau à partir des positions indexées :

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

Qui produit :

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

Vous pouvez donc noter qu'en tant que "slice", le résultat est toujours un "array", cependant le $sort dans le cadre d'agrégation a toujours utilisé la "première position" du tableau afin de trier le contenu. Cela signifie qu'avec une valeur singulière extraite de la position indexée (tout comme la procédure longue ci-dessus), le résultat sera trié comme prévu.

Les cas finaux ici sont juste comme ça que ça marche. Vivez avec le type d'opérations dont vous avez besoin d'en haut pour travailler avec une position indexée du tableau, ou "attendez" jusqu'à ce qu'une toute nouvelle version brillante vienne à votre secours avec de meilleurs opérateurs.