2015-10-09 10 views
5

Ich habe gerade begonnen, MongoDB zu lernen und kann keine Lösung für mein Problem finden.MongoDB multidimensionale Array-Projektion

erhielt das Dokument:

> db.test.insert({"name" : "Anika", "arr" : [ [11, 22],[33,44] ] }) 

Bitte beachten Sie die "arr" Feld, das ein mehrdimensionales Array ist.

Jetzt suche ich nach einer Abfrage, die nur den Wert von arr [0] [1] zurückgibt, was 22 ist. Ich habe versucht, das mit $ slice zu erreichen, aber ich weiß nicht, wie ich das zweite ansprechen soll Dimension damit.

> db.test.find({},{_id:0,"arr":{$slice: [0,1]}}) 
{ "name" : "ha", "arr" : [ [ 11, 22 ] ] } 

Ich habe auch versucht

> db.test.find({},{_id:0,"arr":{$slice: [0,1][1,1]}}) 
{ "name" : "ha", "arr" : [ [ 11, 22 ] ] } 

Die gewünschte Ausgabe wäre entweder

22 

oder

{"arr":[[22]]} 

Danke


EDIT:

Nach dem Lesen der Kommentare Ich denke, dass ich die Beispieldaten viel zu vereinfacht habe, und ich habe mehr Informationen liefern:

  1. Es in der viele weitere Dokumente Sammlung wie diese, die Ich habe zur Verfügung gestellt. Aber sie alle haben die gleiche Struktur.
  2. Es gibt mehr Array-Elemente sind als nur zwei
  3. In der realen Welt das Array wirklich lange Texte enthält (500kb-1MB), so ist es sehr expansive die gesamten Daten an den Client zu übertragen.
  4. Vor der Aggregation werde ich eine Abfrage durch das 'Name' Feld machen. Nur übersprungen, dass in dem Beispiel der Einfachheit halber.
  5. Die Zielindizes sind variabel, so dass manchmal muss ich den Wert von arr wissen [0] [1], es arr [1] [4]

Beispiel Daten das nächste Mal ist :

> db.test.insert({"name" : "Olivia", "arr" : [ [11, 22, 33, 44],[55,66,77,88],[99] ] }) 
> db.test.insert({"name" : "Walter", "arr" : [ [11], [22, 33, 44],[55,66,77,88],[99] ] }) 
> db.test.insert({"name" : "Astrid", "arr" : [ [11, 22, 33, 44],[55,66],[77,88],[99] ] }) 
> db.test.insert({"name" : "Peter", "arr" : [ [11, 22, 33, 44],[55,66,77,88],[99] ] }) 

Beispielabfrage:

> db.test.find({name:"Olivia"},{"arr:"...}) 

Antwort

2

können Sie die Aggregation Framework verwenden:

db.test.aggregate([ 
    { $unwind: '$arr' }, 
    { $limit: 1 }, 
    { $project: { _id: 0, arr: 1 } }, 
    { $unwind: '$arr' }, 
    { $skip: 1 }, 
    { $limit: 1 } 
]) 

Returns:

{ "arr": 22 } 

Edit: Das ursprüngliche Plakat meine Lösung seine Bedürfnisse modifiziert gerecht und kam mit dem Follow-up:

db.test.aggregate([ 
    { $match: { name:"Olivia" } }, 
    { $project: { _id: 0,arr: 1 } }, 
    { $unwind: '$arr' }, 
    { $skip: 1 }, 
    { $limit:1 }, 
    { $unwind: "$arr" }, 
    { $skip: 2 }, 
    { $limit: 1 } 
]) 

Diese Abfrage im resultieren { arr: 77 } angesichts der vom OP zur Verfügung gestellten erweiterten Daten. Beachten Sie, dass $ skip und $ limit benötigt werden, um die richtigen Elemente in der Array-Hierarchie auszuwählen.

+0

Dies kann nur mit einem einzelnen Dokument funktionieren. Sure "$ skip" und "$ limit" simulieren "indexierte Positionen", aber sobald Sie mehrere Collections-Dokumente "abwickeln", wird dies irrelevant. –

+0

Es funktioniert, wenn die gewünschte Ausgabe ** ** für ein einzelnes Dokument ist. Es ist immer möglich, nur ein gewünschtes Dokument mit '$ match' zu erhalten. –

+0

Es wird nie realistisch sein. Hier geht es darum, den Menschen "echte" Beispiele zu geben, die sie wirklich nutzen können. Einzeldokumentfälle sind im Client besser codiert als in meiner Antwort angegeben. –

0

Die $slice Form Sie fragen nicht tun, Multi-dimentional Arrays. Jedes Array wird einzeln betrachtet und daher von der aktuellen $slice nicht unterstützt.

Als solches ist es eigentlich viel kürzer auf indizierte done „ersten“ und „letzten“ Werte als hat .aggregate() vorgeschlagen verwendet und gegenwärtig:

db.test.aggregate([ 
    { "$unwind": "$arr" }, 
    { "$group": { 
     "_id": "$_id", 
     "arr": { "$first": "$arr" } 
    }}, 
    { "$unwind": "$arr" }, 
    { "$group": { 
     "_id": "$_id", 
     "arr": { "$last": "$arr" } 
    }} 
]) 

aber in zukünftigen Versionen von MongoDB (derzeit arbeitet in Entwicklungszweig 3,18 als das Schreibens) Sie $arrayElemAt als Operator für die Aggregation Rahmen, der wie folgt funktioniert:

db.test.aggregate([ 
    { "$project": { 
     "arr": { 
      "$arrayElemAt": [ 
       { "$arrayElemAt": [ "$arr", 0 ] }, 
       1 
      ] 
     } 
    }} 
]) 

Sowohl im Grunde auf das gleiche { "arr": 22 } Ergebnis kommen, wenn die Future Available Form funktioniert ziemlich flexibel auf Array-Index-Werte, anstatt zuerst und zuletzt.

+0

Es ist gut, über $ arrayElemAt 'zu wissen! Danke für das Teilen. Ich habe jedoch eine Frage zu Ihrer Verwendung von Aggregationsoperatoren. Ist '$ group' in meinem Beispiel nicht teurer als' $ limit'? –

+0

Wenn Sie die Anzahl der Elemente in einem Array nicht kennen, können Sie wahrscheinlich auch nicht '$ first' und' $ last' verwenden. Die Verwendung von '$ limit' sieht also vielseitiger für mich aus. –

+1

@DmytroShevchenko Nein. Je mehr Aggregationspipeline-Stufen == desto teurer. PUNKT. Weniger ist besser. Natürlich bedeuten die neuen Operatoren (wenn sie verfügbar werden) eine einzige Stufe, was daher das beste Ergebnis ist. Ganz ehrlich, wenn das die ganze Komplexität dessen ist, was Sie fragen, dann ist das Aggregations-Framework nicht "zur Zeit" die Antwort. Tun Sie es stattdessen im Client-Code, der viel effizienter ist. –