2013-02-27 15 views
49

ho matrice in documento secondario come questoCome filtrare matrice in documento secondario con MongoDB

{ 
    "_id" : ObjectId("512e28984815cbfcb21646a7"), 
    "list" : [ 
     { 
      "a" : 1 
     }, 
     { 
      "a" : 2 
     }, 
     { 
      "a" : 3 
     }, 
     { 
      "a" : 4 
     }, 
     { 
      "a" : 5 
     } 
    ] 
} 

Posso filtrare documento secondario per un> 3

mio aspettarsi risultato qui sotto

{ 
    "_id" : ObjectId("512e28984815cbfcb21646a7"), 
    "list" : [ 
     { 
      "a" : 4 
     }, 
     { 
      "a" : 5 
     } 
    ] 
} 

provo utilizzare $elemMatch ma restituisce il primo elemento corrispondente nell'array

La mia domanda:

db.test.find({ _id" : ObjectId("512e28984815cbfcb21646a7") }, { 
    list: { 
     $elemMatch: 
      { a: { $gt:3 } 
      } 
    } 
}) 

Il risultato restituito un elemento in ordine di

{ "_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ { "a" : 4 } ] } 

e io cerco di utilizzare aggregati con $match ma non funziona

db.test.aggregate({$match:{_id:ObjectId("512e28984815cbfcb21646a7"), 'list.a':{$gte:5} }}) 

E 'restituire tutti gli elementi in ordine di

{ 
    "_id" : ObjectId("512e28984815cbfcb21646a7"), 
    "list" : [ 
     { 
      "a" : 1 
     }, 
     { 
      "a" : 2 
     }, 
     { 
      "a" : 3 
     }, 
     { 
      "a" : 4 
     }, 
     { 
      "a" : 5 
     } 
    ] 
} 

Posso filtrare l'elemento nella matrice per ottenere risultati come previsto?

risposta

77

Uso aggregate è l'approccio giusto, ma è necessario $unwind matrice list prima di applicare la $match modo da poter filtrare singoli elementi e quindi utilizzare $group di rimetterlo insieme:

db.test.aggregate(
    { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}}, 
    { $unwind: '$list'}, 
    { $match: {'list.a': {$gt: 3}}}, 
    { $group: {_id: '$_id', list: {$push: '$list.a'}}}) 

uscite :

{ 
    "result": [ 
    { 
     "_id": ObjectId("512e28984815cbfcb21646a7"), 
     "list": [ 
     4, 
     5 
     ] 
    } 
    ], 
    "ok": 1 
} 

MongoDB 3.2 Aggiornamento

A partire dalla release 3.2, è possibile utilizzare l'operatore new $filter aggregazione per fare questo in modo più efficiente solo inclusi gli list elementi che si desidera nel corso di una $project:

db.test.aggregate([ 
    { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}}, 
    { $project: { 
     list: {$filter: { 
      input: '$list', 
      as: 'item', 
      cond: {$gt: ['$$item.a', 3]} 
     }} 
    }} 
]) 
+0

Vuol l'operazione di aggregazione modificare il documento o lo fa solo eseguire una selezione? – Cherif

+2

@Cherif 'aggrega' non ha effetto sul documento stesso. – JohnnyHK

+0

Come procedere, se si desidera pubblicare il risultato di tale query sul client? – samson

8

Sopra soluzione funziona meglio se multipla sub corrispondenza i documenti sono obbligatori $elemMatch viene anche molto utilizzare se singolo documento corrispondente sub è richiesto come output

db.test.find({list: {$elemMatch: {a: 1}}}, {'list.$': 1}) 

risultati:

{ 
    "_id": ObjectId("..."), 
    "list": [{a: 1}] 
} 
+0

Questa è stata la soluzione migliore per me. Ma il secondo argomento funziona solo con tipi primitivi. Poiché avevo un oggetto nell'array, la mia soluzione era: 'ShoppingCartModel.findOne ({" _ id ": ctx.params.idCart," active ": true, prodotti: {$ elemMatch: {" products.active ": true} }}) ' (Stavo filtrando solo l'attivo: veri carrelli e prodotti) –

2

Uso $filter aggregation

selezionare un sottoinsieme della matrice di rendimento basato sulla la condizione specificata . Restituisce un array con solo quegli elementi che corrispondono alla condizione . Gli elementi restituiti sono nell'ordine originale.

db.test.aggregate([ 
    {$match: {"list.a": {$gt:3}}}, // <-- match only the document which have a matching element 
    {$project: { 
     list: {$filter: { 
      input: "$list", 
      as: "list", 
      cond: {$gt: ["$$list.a", 3]} //<-- filter sub-array based on condition 
     }} 
    }} 
]); 
Problemi correlati