2015-05-29 11 views
13

Mam kolekcję że jestem wykonujących na agregację a ja w zasadzie dostał go doMongoDB Aggregation - mecz jeśli wartość w tablicy

{array:[1,2,3], value: 1}, 
{array:[1,2,3], value: 4} 

Jak bym wykonać dopasowanie agregacji w celu sprawdzenia, czy wartość jest w tablicy? Próbowałem użyć {$match: {"array: {$in: ["$value"]}}}, ale nic nie znalazłem.

chciałbym wyjście (w przypadku korzystania z wyżej jako przykład) za:

{array:[1,2,3], value:1} 
+0

nie próbowałem tego, ale myślę, że prawdopodobnie chcesz '$ elemMatch' (patrz [DOCS] (http://docs.mongodb.org/manual/reference/operator/query/elemMatch/# op._S_elemMatch)) zamiast '$ in' - chcesz przetestować, czy wartość jest tablicą, a nie czy tablica ma wartość! – DNA

+1

Naprawdę powinieneś wrócić i spojrzeć na to pytanie, ponieważ każdy, kto zasugerował '$ unwind', dostarczył ci najgorszą możliwą odpowiedź, którą możesz zastosować w swoim kodzie. Złożono znacznie lepsze alternatywy. –

Odpowiedz

6

Nieznaczne odchylenie w oparciu o @ chridam's odpowiedź:

db.test.aggregate([ 
    { "$unwind": "$array" }, 
    { "$group": { 
        _id: { "_id": "$_id", "value": "$value" }, 
        array: { $push: "$array" }, 
        mcount: { $sum: {$cond: [{$eq: ["$value","$array"]},1,0]}} 
       } 
    }, 
    { $match: {mcount: {$gt: 0}}}, 
    { "$project": { "value": "$_id.value", "array": 1, "_id": 0 }} 
]) 

Chodzi o to, aby $unwind i $group przywrócić tablicę, licząc w liczbie mcount liczbę elementów pasujących do wartości. Następnie prosty $match na mcount > 0 odfiltrowuje niechciane dokumenty.

2

Można użyć $where jeśli agregacja nie wymagane

db.collection.find({ $where: function(){ 
    return (this.array.indexOf(this.value) !== -1)} 
}) 
+0

Zgadzam się, że lepiej jest użyć '$ unwind', ale jest oczywiście o wiele bardziej efektywny sposób robienia tego. –

4

Bardziej efektywne podejście dotyczy pojedynczego potoku korzystającego z operatora w następujący sposób:

db.collection.aggregate([ 
    { 
     "$redact": { 
      "$cond": [ 
       { 
        "$setIsSubset": [ 
         ["$value"], 
         "$array" 
        ] 
       }, 
       "$$KEEP", 
       "$$PRUNE" 
      ] 
     } 
    } 
]) 

We wcześniejszych wersjach MongoDB, które nie obsługują $redact (wersje < 2,6), a następnie rozważyć ten agregacji rurociągu, która używa operatora $unwind:

db.collection.aggregate([ 
    { "$unwind": "$array" }, 
    { 
     "$project": { 
      "isInArray": { 
       "$cond": [ 
        { "$eq": [ "$array", "$value" ] }, 
        1, 
        0 
       ] 
      }, 
      "value": 1, 
      "array": 1 
     } 
    }, 
    { "$sort": { "isInArray": -1 } }, 
    { 
     "$group": { 
      "_id": { 
       "_id": "$_id", 
       "value": "$value" 
      }, 
      "array": { "$push": "$array" }, 
      "isInArray": { "$first": "$isInArray" } 
     } 
    }, 
    { "$match": { "isInArray": 1 } }, 
    { "$project": { "value": "$_id.value", "array": 1, "_id": 0 } } 
]) 
11

Jak podano, $where jest dobrym rozwiązaniem, gdy nie trzeba kontynuować logiki w potoku agregacji.

Ale jeśli to zrobisz, użyj $redact, aby $map przekształcić "wartość" w tablicę i użyć porównania $setIsSubSet. Jest to najszybszy sposób, aby to zrobić, ponieważ nie trzeba powielać dokumentów za pomocą $unwind:

db.collection.aggregate([ 
    { "$redact": { 
     "$cond": { 
      "if": { "$setIsSubset": [ 
       { "$map": { 
        "input": { "$literal": ["A"] }, 
        "as": "a", 
        "in": "$value" 
       }}, 
       "$array" 
      ]}, 
      "then": "$$KEEP", 
      "else": "$$PRUNE" 
     } 
    }} 
]) 

$redact operator rurociągu pozwala PRZETWARZANIE warunku logicznego ciągu $cond i używa specjalnych zabiegów $$KEEP do „keep "dokument, w którym warunek logiczny jest prawdziwy lub $$PRUNE, aby" usunąć "dokument, w którym warunek był fałszywy.

Pozwala to na pracę jak $project z kolejnym $match, ale w jednym etapie rurociągu, który jest bardziej wydajny.

Biorąc pod uwagę, że są to natywnie kodowane operatory, a nie JavaScript, jest to prawdopodobnie "najszybszy" sposób na dopasowanie. Tak więc pod warunkiem, że używasz wersji MongoDB 2.6 lub wyższej, to jest sposób, w jaki powinieneś robić to, aby porównać te elementy w dokumencie.

+0

kciuki za $ redact. Nie wiedziałem o tym i robi dokładnie to, czego potrzebuję: przejrzyj każdy dokument pod kątem określonej wartości w tablicy. Dzięki! – Mattijs

1

Spróbuj kombinacji $ EQ i $ setIntersection

{$group :{ 
    _id: "$id", 
    yourName : { $sum: 
    { $cond :[ 
     {$and : [ 
      {$eq:[{$setIntersection : ["$someArrayField", ["$value"]] },["$value"]]} 
     ] 
     },1,0] 
    } 

} }

3

Trochę późno, by odpowiedzieć, ale ten przedstawia inne rozwiązanie:

Korzystając addFields i mecz oddzielnie, to daje większą elastyczność niż redaktor. Możesz odsłonić kilka pól, a następnie użyć innej zgodnej logiki w oparciu o wyniki.

db.applications.aggregate([ 
    {$addFields: {"containsValueInArray": {$cond:[{$setIsSubset: [["valueToMatch"], "$arrayToMatchIn"]},true,false]}}}, 
    {$match: {"containsValueInArray":true}} 
]); 
0

Możesz użyć wyrażenia agregacji w regularnym zapytaniu w wersji 3.6.

db.collection_name.find({"$expr": {"$in": ["$value", "$array"]}}) 

Korzystanie Aggregation:

$match + $expr można użyć w bieżącym 3.6 wersji.

db.collection_name.aggregate({"$match": {"$expr": {"$in": ["$value", "$array"]}}}) 

Można spróbować $redact + $in wyraz w 3.4 wersji.

db.collection_name.aggregate({ 
    "$redact": { 
    "$cond": [ 
     { 
     "$in": [ 
      "$value", 
      "$array" 
     ] 
     }, 
     "$$KEEP", 
     "$$PRUNE" 
    ] 
    } 
}) 
Powiązane problemy