2015-03-13 11 views
10

ho un documento mongodb comeMongoDB elemmatch più elementi a matrice

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

Tipicamente contiene molti array nidificati come questo. Voglio proiettare una metrica da sola, con solo gli array che hanno i miei criteri di ricerca. Ho la query

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

Questo mi dà solo il primo oggetto nella matrice. Il secondo oggetto con classe: s2 non viene restituito.

Se provo

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

Mi ci danno tutti i 4 oggetti nella matrice.

Come ottengo tutti gli oggetti che corrispondono a un criterio in questo caso?

risposta

16

Non è possibile restituire più elementi di un array che corrispondono ai propri criteri in alcuna forma di una query di base .find(). Per abbinare più di un elemento, devi invece utilizzare il metodo .aggregate().

La differenza principale qui è che la "query" fa esattamente ciò che è destinato a fare e corrisponde ai "documenti" che soddisfano le vostre condizioni. Puoi provare a utilizzare l'operatore positional $ all'interno di un argomento di proiezione, ma le regole sono che corrisponderanno solo al "primo" elemento dell'array che corrisponde alle condizioni della query.

Al fine di "filtro" per più elementi di un array, procedere come segue:

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

Nelle versioni moderne di MongoDB che sono la versione 2.6 o superiore si può fare questo con $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" 
     ] 
    }} 
]) 

Questa è probabilmente l'opzione più efficiente, ma è ricorsiva, quindi considera prima la struttura del tuo documento poiché lo stesso campo con nome non può esistere con altre condizioni a nessun livello.

Forse più sicuro, ma solo utile in cui i risultati nella matrice sono "davvero unico" è questa tecnica con $map e $setDifference:

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

notare anche che in entrambi gli stadi della pipeline operative $group e $project si necessità per specificare tutti i campi che si intende restituire nei documenti dei risultati da quella fase.

La nota finale è che $elemMatch non è richiesto quando si esegue una query sul valore di una singola chiave all'interno di una matrice. "Dot notation" è preferito e consigliato quando si accede solo a una singola chiave dell'array. $elemMatch dovrebbe essere necessario solo quando le chiavi "multiple" nel documento all'interno dell'elemento "array" devono corrispondere a una condizione di query.

+0

Thx molto. Quello era dettagliato e esplicativo. – Manoj

+0

'filtermetric' dovrebbe essere' FilterMetric' in queste risposte per abbinare l'involucro di quel campo nel documento di esempio. – JohnnyHK