sql >> Databasteknik >  >> NoSQL >> MongoDB

MongoDB sortering vs aggregerad $sort på arrayindex

Aggregeringsramverket "hanterar" helt enkelt inte arrayer på samma sätt som tillämpas på .find() frågor i allmänhet. Detta gäller inte bara operationer som .sort() , men också med andra operatörer, och nämligen $slice , även om det exemplet håller på att fixas (mer senare).

Så det är i stort sett omöjligt att hantera någonting med hjälp av "dot notation"-formen med ett index för en arrayposition som du har. Men det finns en väg runt detta.

Vad du "kan" göra är i princip att räkna ut vad det "n:te" arrayelementet faktiskt är som ett värde, och sedan returnera det som ett fält som kan sorteras:

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

Det är ett hemskt och "iterbart" tillvägagångssätt där du i princip "går igenom" varje arrayelement genom att skaffa $first matcha per dokument från arrayen efter bearbetning med $ varva ner . Sedan efter $unwind igen, du testar för att se om de arrayelementen är desamma som de som redan "setts" från de identifierade arraypositionerna.

Det är fruktansvärt, och värre för de fler positioner du vill flytta längs, men det ger resultatet:

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

Lyckligtvis får kommande utgåvor av MongoDB (som för närvarande finns i utvecklingsutgåvor) en "fix" för detta. Det kanske inte är den "perfekta" fixen du önskar, men det löser det grundläggande problemet.

Det finns en ny $slice operatör tillgänglig för aggregeringsramverket där, och den kommer att returnera de nödvändiga elementen i arrayen från de indexerade positionerna:

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

Som producerar:

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

Så du kan notera att som en "slice" är resultatet fortfarande en "array", men $sort i aggregeringsramverket har alltid använt den "första positionen" av arrayen för att sortera innehållet. Det betyder att med ett singularvärde extraherat från den indexerade positionen (precis som den långa proceduren ovan) så kommer resultatet att sorteras som du förväntar dig.

Slutfallen här är att det är precis så det fungerar. Antingen lev med den typ av operationer du behöver ovanifrån för att arbeta med en indexerad position av arrayen, eller "vänta" tills en helt ny glänsande version kommer till din räddning med bättre operatörer.




  1. Från matta-välj spara data till databasen MongoDB

  2. Frågar intern arraystorlek i MongoDB

  3. Transaktionsskapande med validering i ServiceStack Redis Client

  4. Ansluter Django +1.10 med MongoDB