MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

MongoDB-Sortierung vs. Aggregat $sort auf Array-Index

Das Aggregations-Framework „handhabt“ Arrays einfach nicht auf die gleiche Weise, wie es auf .find() angewendet wird Abfragen im Allgemeinen. Dies gilt nicht nur für Operationen wie .sort() , sondern auch mit anderen Operatoren, und zwar $slice , obwohl dieses Beispiel bald behoben werden soll ( mehr später ).

Es ist also so gut wie unmöglich, irgendetwas mit der Form "Punktnotation" mit einem Index einer Array-Position zu behandeln, wie Sie es haben. Aber es gibt einen Weg, dies zu umgehen.

Was Sie tun "können", ist im Grunde herauszufinden, was das "n-te" Array-Element tatsächlich als Wert ist, und dies dann als sortierbares Feld zurückzugeben:

  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 ist ein schrecklicher und „iterierbarer“ Ansatz, bei dem Sie im Wesentlichen jedes Array-Element „durchgehen“, indem Sie den $first Übereinstimmung pro Dokument aus dem Array nach Verarbeitung mit $unwind . Dann nach $unwind erneut testen Sie, ob diese Array-Elemente mit denen identisch sind, die bereits von den identifizierten Array-Positionen "gesehen" wurden.

Es ist schrecklich und umso schlimmer, je mehr Positionen Sie verschieben möchten, aber es erhält das Ergebnis:

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

Glücklicherweise erhalten kommende Versionen von MongoDB (wie derzeit in Entwicklungsversionen verfügbar) einen „Fix“ dafür. Es ist vielleicht nicht die "perfekte" Lösung, die Sie sich wünschen, aber es löst das grundlegende Problem.

Es gibt ein neues $slice -Operator, der dort für das Aggregationsframework verfügbar ist, und er gibt die erforderlichen Elemente des Arrays aus den indizierten Positionen zurück:

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

Was produziert:

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

Sie können also feststellen, dass das Ergebnis als "Slice" immer noch ein "Array" ist, jedoch der $sort im Aggregation Framework hat immer die "erste Position" des Arrays verwendet, um den Inhalt zu sortieren. Das bedeutet, dass bei einem Singularwert, der aus der indizierten Position extrahiert wird (genau wie bei der langen Prozedur oben), das Ergebnis wie erwartet sortiert wird.

Die Endfälle hier sind, dass es genau so funktioniert. Entweder leben Sie mit der Art von Operationen, die Sie von oben benötigen, um mit einer indizierten Position des Arrays zu arbeiten, oder "warten", bis eine brandneue glänzende Version mit besseren Operatoren zu Ihrer Rettung kommt.