sql >> Database >  >> NoSQL >> MongoDB

MongoDB sorteren versus aggregeren $sort op array-index

Het aggregatieraamwerk "handelt" arrays gewoon niet op dezelfde manier als wordt toegepast op .find() vragen in het algemeen. Dit geldt niet alleen voor bewerkingen zoals .sort() , maar ook met andere operators, en namelijk $slice , hoewel dat voorbeeld op het punt staat een oplossing te krijgen (later meer).

Het is dus vrijwel onmogelijk om met iets om te gaan met behulp van de "dot-notatie"-vorm met een index van een array-positie zoals je hebt gedaan. Maar er is een manier om dit te omzeilen.

Wat u "kunt" doen is in feite uitzoeken wat het "n-de" array-element eigenlijk is als een waarde, en dat vervolgens teruggeven als een veld dat kan worden gesorteerd:

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

Het is een vreselijke en "itereerbare" benadering waarbij je in wezen door elk array-element "stapt" door de $first match per document uit de array na verwerking met $unwind . Dan na $unwind nogmaals, je test om te zien of die array-elementen hetzelfde zijn als degene(n) die al "gezien" zijn vanaf de geïdentificeerde array-posities.

Het is verschrikkelijk, en erger voor de meer posities die je wilt verplaatsen, maar het geeft wel het resultaat:

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

Gelukkig krijgen aankomende releases van MongoDB (zoals momenteel beschikbaar in ontwikkelingsreleases) hiervoor een "fix". Het is misschien niet de "perfecte" oplossing die u wenst, maar het lost wel het basisprobleem op.

Er is een nieuwe $slice operator beschikbaar voor het aggregatieraamwerk daar, en het zal de vereiste elementen van de array retourneren vanuit de geïndexeerde posities:

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

Wat produceert:

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

U kunt dus opmerken dat als een "slice", het resultaat nog steeds een "array" is, maar de $sort in het aggregatieraamwerk heeft altijd de "eerste positie" van de array gebruikt om de inhoud te sorteren. Dat betekent dat met een enkelvoudige waarde die wordt geëxtraheerd uit de geïndexeerde positie (net als de lange procedure hierboven), het resultaat wordt gesorteerd zoals u verwacht.

De eindgevallen hier zijn dat is precies hoe het werkt. Ofwel leef met het soort operaties dat je van bovenaf nodig hebt om met een geïndexeerde positie van de array te werken, of "wacht" tot een gloednieuwe glanzende versie je te hulp komt met betere operators.




  1. Zoeken naar de waarde van een veld in MongoDB zonder het expliciet een naam te geven

  2. Hoe kan een string tot nu toe worden geconverteerd met mongo-aggregatie?

  3. Tests schrijven voor Python Eve RESTful API's tegen een echte MongoDB

  4. Wat moet ik kiezen:MongoDB/Cassandra/Redis/CouchDB?