2014-07-01 14 views
10

Mam następujące dwa Objects w kolekcji Mongo nazwie cars:

{vendor: "BMW", 
model: "325i", 
class: "coupe", 
equipment: [ 
    {name: "airbag", 
    cost: 120 
    }, 
    {name: "led", 
    cost: 170 
    }, 
    {name: "abs", 
    cost: 150 
    } 
] 
} 

{vendor: "Mercedes", 
model: "C 250", 
class: "coupe", 
equipment: [ 
    {name: "airbag", 
    cost: 180 
    }, 
    {name: "esp", 
    cost: 170 
    }, 
    {name: "led", 
    cost: 120 
    } 
] 
} 

gram dookoła z nowej funkcji agregacji redact wprowadzonego w Mongo 2.6 Chcę wykonać zapytanie, aby uzyskać listę samochodów z filtrowanymi częściami sprzętu. Słowem: Dajcie mi wszystkie samochody typu coupe i tylko informacje o sprzęcie LED i AIRBAG.

Próbowałem to agregacji:

db.cars.aggregate(
    [ 
     {$match: {"class": "coupe"}}, 
     {$redact: 
     { 
      $cond: 
      { 
       if: { $in : ["$name", ["airbag", "led"]]}, 
       then: "$$DESCEND", 
       else: "$$PRUNE" 
      } 
     } 
     } 
    ] 
) 

Ale to prowadzi do następującego błędu:

assert: command failed: { "errmsg" : "exception: invalid operator '$in'", "code" : 15999, "ok" : 0 } : aggregate failed

Co robię źle? Czy istnieje inny sposób na osiągnięcie celu.

Spodziewam się wrócić to wynikać z Mongo:

{vendor: "BMW", 
model: "325i", 
class: "coupe", 
equipment: [ 
    {name: "airbag", 
    cost: 120 
    }, 
    {name: "led", 
    cost: 170 
    } 
] 
} 

{vendor: "Mercedes", 
model: "C 250", 
class: "coupe", 
equipment: [ 
    {name: "airbag", 
    cost: 180 
    }, 
    {name: "led", 
    cost: 120 
    } 
] 
} 

Wszelkie sugestie, w jaki sposób to osiągnąć?

Cheers, Ralf

+0

Chcesz użyć '$ setIsSubset' zamiast' $ in' podczas wykonywania agregacji (w tym redakcji) w MongoDB. Zobacz http://docs.mongodb.org/manual/reference/operator/aggregation/setIsSubset – Paul

+0

Możesz użyć '$ filter' z' $ in' z wersji 3.4. Coś w stylu 'db.cars.aggregate ([ { " $ match ": { " klasa ":" coupe ", " equipment.name ": { " $ in ": [" poduszka powietrzna "," led " ] } } } { "$ addFields": { "urządzenie" { "$ filtr": { "wejście" "$ trwałe" "w": "trwałe" "warunkach": { "$ w": [ "$$ equipment.name" [ "poduszki powietrznej", "led"] ] } } } } } } ]) ' – Veeram

Odpowiedz

4

$redact operator rurociągu nie jest tak naprawdę jeden chcesz na tym przypadku. To, co chce zrobić, to rekursywnie "schodzić" przez strukturę dokumentu i oceniać warunki na każdym "poziomie", aby zobaczyć, jakie działania podejmie.

W twoim przypadku nie ma pola "nazwa" na najwyższym poziomie dokumentu, aby spełnić warunek, w wyniku czego cały dokument jest "przycinany".

Jesteś po przefiltrowaniu tablic, a do tego przypadku, gdy nie chcesz używać $unwind i $match, wówczas można skorzystać z nowych operatorów, takich jak $map:

db.cars.aggregate([ 
    { "$match": { "class": "coupe" }}, 
    { "$project": { 
     "equipment": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$equipment", 
        "as": "el", 
        "in": { 
         "$cond": [ 
          { "$or": [ 
           { "$eq": [ "$$el.name", "airbag" ] }, 
           { "$eq": [ "$$el.name", "led" ] } 
          ]}, 
          { "$cond": [ 1, "$$el", 0 ] }, 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 

Operator $map współpracuje z tablicy i ocenia logiczny warunek względem wszystkich elementów, w tym przypadku w zakresie $cond. To samo, co $in, które nie jest operatorem logicznym w tym znaczeniu, to użycie operatora logicznego dla struktury agregacji.

Ponieważ jakikolwiek produkt, który nie spełnia warunku, zwróci false, należy teraz "usunąć" wszystkie wartości false. Jest to wspomagane przez $setDifference, które zrobi to przez porównanie.

Rezultatem jest to, co chcesz:

{ 
    "_id" : ObjectId("53b2725120edfc7d0df2f0b1"), 
    "equipment" : [ 
      { 
        "name" : "airbag", 
        "cost" : 120 
      }, 
      { 
        "name" : "led", 
        "cost" : 170 
      } 
    ] 
} 
{ 
    "_id" : ObjectId("53b2725120edfc7d0df2f0b2"), 
    "equipment" : [ 
      { 
        "name" : "airbag", 
        "cost" : 180 
      }, 
      { 
        "name" : "led", 
        "cost" : 120 
      } 
    ] 
} 

Jeśli były naprawdę intencyjny w sprawie korzystania $redact wtedy zawsze jest to wymyślony przykład:

db.cars.aggregate([ 
    { "$match": { "class": "coupe" }}, 
    { "$redact": { 
     "$cond": { 
      "if": { 
       "$or": [ 
        { "$eq": [ 
         { "$ifNull": ["$name", "airbag"] }, 
         "airbag" 
        ]}, 
        { "$eq": [ 
         { "$ifNull": ["$name", "led"] }, 
         "led" 
        ]}, 
       ] 
      }, 
      "then": "$$DESCEND", 
      "else": "$$PRUNE" 
     } 
    }} 
]) 

Tak że w zasadzie gwarantuje, że jeśli pole nie istnieje w ogóle podczas zstępowania, a następnie "sztucznie" wytworzy taką, która będzie pasować, aby poziom nie został "przycięty". Tam, gdzie istnieje pole, używana jest wartość znaleziona, a jeśli nie spełnia warunku, zostanie oczyszczona.

+0

Wielkie dzięki - zaoszczędzisz mi dzień ;-) – Ralf

+0

@Ralf Cieszę się, że mogę Ci pomóc. $ redact jest trudną rzeczą dla ludzi, którzy chcą zrozumieć, jak nowa koncepcja. Oba podejścia są ważne, tylko zależy od tego, która składnia wydaje się najwyraźniejsza w twoim celu dla ciebie i współpracowników. –

Powiązane problemy