2013-02-27 10 views
49

Mam tablicę w dokument podrzędny jak tenJak filtrować tablicy w dokument podrzędny z MongoDB

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

mogę filtrować dokument podrzędny przez> 3

My oczekiwać skutkować poniżej

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

staram do użycia $elemMatch, ale zwraca pierwszy pasujący element w tablicy

Moje zapytanie:

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

Zwrot wynik jeden element w tablicy

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

i próbuję użyć kruszywa $match ale nie działa

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

To powrócić wszystkie elementy w tablicy

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

Czy mogę filtrować element w tablicy, aby uzyskać wynik w oczekiwany sposób?

Odpowiedz

77

Korzystanie aggregate jest to najlepsze rozwiązanie, ale trzeba $unwind tablicę list przed nałożeniem $match, dzięki czemu można filtrować poszczególne elementy, a następnie użyć $group umieścić go z powrotem razem:

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

wyjść :

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

MongoDB 3.2 Aktualizacja

Począwszy od wydania 3.2, można bardziej efektywnie korzystać z nowego operatora $filter agregacji to zrobić poprzez włączenie tylko list elementy, które chcesz podczas $project:

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

Czy operacja agregacja zmodyfikować dokument, czy też po prostu przeprowadzić wybór? – Cherif

+2

@Cherif 'aggregate' nie wpływa na sam dokument. – JohnnyHK

+0

Jak byś postąpił, gdybyś chciał opublikować wynik tego zapytania dla klienta? – samson

8

Przede rozwiązanie działa najlepiej jeśli wielokrotnego dopasowania sub dokumenty są wymagane. $elemMatch również wyposażony w bardzo używać, jeśli pojedynczy dokument dopasowanie sub jest wymagana jako wyjście

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

Wynik:

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

To było najlepsze rozwiązanie dla mnie. Ale drugi argument działa tylko z typami pierwotnymi. Ponieważ miałem obiekt w tablicy, moje rozwiązanie brzmiało: 'ShoppingCartModel.findOne ({" _ id ": ctx.params.idCart," active ": true, produkty: {$ elemMatch: {" products.active ": true} }}) ' (Filtrowałem tylko aktywne: prawdziwe wózki i produkty) –

2

Zastosowanie $filter aggregation

wybiera podzbiór tablicy, aby powrócić na podstawie podany warunek . Zwraca tablicę zawierającą tylko te elementy, które pasują do warunku . Zwrócone elementy są w oryginalnej kolejności.

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 
     }} 
    }} 
]); 
Powiązane problemy