2015-03-13 7 views
10

Ich habe ein Dokument wie mongodbMongoDB elemmatch mehrere Elemente in dem Array

{ 
     "_id" : ObjectId("54e66b2da7b5f3a92e09dc6c"), 
     "SomeMetric" : [ 
      { 
       //some object 
      } 
      { 
       //some object 
      } 
     ], 
     "FilterMetric" : [ 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s1" 
      }, 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s2" 
      }, 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s1" 
      }, 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s2" 
      } 
     ] 
    } 

typischerweise viele verschachtelten Arrays wie folgt es enthält. Ich möchte nur eine Metrik mit nur den Arrays projizieren, die meine Suchkriterien haben. Ich habe die Abfrage

db.sample.find(
{"filtermetric.class" : "s2"},{"filtermetric" : { $elemMatch : {class: "s2"}}} 
) 

Das ist mir in dem Array nur das erste Objekt gibt. Das zweite Objekt mit der Klasse: s2 wird nicht zurückgegeben.

Wenn ich versuche,

db.sample.find(
    {"filtermetric" : { $elemMatch : {class: "s2"}}} 
    ) 

Es gib mir alle 4 Objekte in der Anordnung.

Wie bekomme ich in einem solchen Fall alle Objekte, die einem Kriterium entsprechen?

Antwort

16

können Sie nicht mehrere Elemente eines Arrays zurückgeben Ihre Kriterien in irgendeiner Form einer grundlegenden .find() Suche entsprechen. Um mehr als ein Element zu finden, müssen Sie stattdessen die Methode .aggregate() verwenden.

Der Hauptunterschied hier ist, dass die "Abfrage" genau das tut, was es beabsichtigt ist und "Dokumente", die Ihre Bedingungen erfüllen. Sie können versuchen, den positional $ -Operator innerhalb eines Projektionsarguments zu verwenden, aber die Regeln dort sind, dass es nur dem "ersten" Feldelement entspricht, das den Abfragebedingungen entspricht.

Um für mehrere Array-Elemente zu "filtern", gehen Sie wie folgt vor:

db.sample.aggregate([ 
    // Filter possible documents 
    { "$match": { "filtermetric.class": "s2" } }, 

    // Unwind the array to denormalize 
    { "$unwind": "$filtermetric" }, 

    // Match specific array elements 
    { "$match": { "filtermetric.class": "s2" } }, 

    // Group back to array form 
    { "$group": { 
     "_id": "$_id", 
     "filtermetric": { "$push": "$filtermetric" } 
    }} 
]) 

In modernen Versionen von MongoDB, die Version 2.6 oder höher Sie können dies tun, mit $redact:

db.sample.aggregate([ 
    // Filter possible documents 
    { "$match": { "filtermetric.class": "s2" } }, 

    // Redact the entries that do not match 
    { "$redact": { 
     "$cond": [ 
      { "$eq": [ { "$ifNull": [ "$class", "s2" ] }, "s2" ] }, 
      "$$DESCEND", 
      "$$PRUNE" 
     ] 
    }} 
]) 

Das ist wahrscheinlich Ihre effizienteste Option, aber es ist rekursiv. Betrachten Sie daher zuerst Ihre Dokumentstruktur, da das gleiche benannte Feld auf keiner Ebene mit anderen Bedingungen existieren kann.

möglicherweise sicherer, aber nur dann sinnvoll, wenn die Ergebnisse in der Reihe „wirklich einzigartig“ sind, ist diese Technik mit $map und $setDifference:

db.sample.aggregate([ 
    { "$project": { 
     "filtermetric": { "$setDifference": [ 
      { "$map": [ 
       "input": "$filtermetric", 
       "as": "el", 
       "in": {"$cond": [ 
        { "$eq": [ "$$el.class", "s2" ] }, 
        "$$el", 
        false 
       ]} 
      ]}, 
      [false] 
     ]} 
    }} 
]) 

auch darauf hingewiesen, dass sowohl in den $group und $project operativen Pipeline-Stufen Sie Notwendigkeit, um alle Felder anzugeben, die Sie in den Ergebnisdokumenten von dieser Phase zurückgeben möchten.

Die letzte Anmerkung ist, dass $elemMatch nicht erforderlich ist, wenn Sie nur den Wert eines einzelnen Schlüssels innerhalb eines Arrays abfragen. "Dot notation" wird bevorzugt und empfohlen, wenn nur auf einen einzelnen Schlüssel des Arrays zugegriffen wird. $elemMatch sollte nur benötigt werden, wenn "mehrere" Schlüssel im Dokument innerhalb des Array "element" mit einer Abfragebedingung übereinstimmen müssen.

+0

Thx viel. Das war detailliert und erklärend. – Manoj

+0

'filtermetric' sollte in diesen Antworten' FilterMetric' sein, damit es mit der Umrandung dieses Felds im Beispieldokument übereinstimmt. – JohnnyHK

Verwandte Themen