2014-07-01 3 views
10

ho ottenuto il seguente due Objects in una collezione denominata Mongo 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 
    } 
] 
} 

Sto giocando intorno con la nuova funzione di aggregazione redact introdotta in Mongo 2.6 Voglio eseguire una query per ottenere un elenco di auto con parti di apparecchiature filtrate. In parole: Dammi tutte le auto del tipo coupé e solo le informazioni su LED e AIRBAG equipaggiamento.

Ho provato questo aggregazioni:

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

Ma che conduce alla seguente errore:

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

Quello che sto facendo di sbagliato? C'è un altro modo per raggiungere l'obiettivo.

mi aspetto di tornare questo risultato da 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 
    } 
] 
} 

Qualche suggerimento su come raggiungere questo obiettivo?

Cheers, Ralf

+0

Si desidera utilizzare '$ setIsSubset' invece di' $ in' quando si eseguono aggregazioni (inclusa la redazione) in MongoDB. Vedi http://docs.mongodb.org/manual/reference/operator/aggregation/setIsSubset – Paul

+0

Puoi usare '$ filter' con' $ in' dalla versione 3.4. Qualcosa come 'db.cars.aggregate ([ { " $ match ": { " class ":" coupé ", " equipment.name ": { " $ in ": [" airbag "," led " ] } } }, { "$ addFields": { "attrezzature": { "$ Filtro": { "input": "$ equipaggiamento", "come": "equipaggiamento", "cond": { "$ in": [ "$$ equipment.name", [ "airbag", "led"] ] }} } } } ]) ' – Veeram

risposta

4

L'operatore pipeline $redact non è davvero quello che si desidera per questo caso. Quello che vuole fare è "scendere" in modo ricorsivo attraverso la struttura del documento e valutare le condizioni ad ogni "livello" per vedere quali azioni intraprendere.

Nel tuo caso, non esiste un campo "nome" al livello superiore del documento per soddisfare la condizione, pertanto l'intero documento viene "potato".

siete dopo il filtraggio array, e per questo caso in cui non si desidera utilizzare $unwind e $match, quindi è possibile utilizzare i nuovi operatori come $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] 
      ] 
     } 
    }} 
]) 

L'operatore $map lavora con l'array e valuta una condizione logica contro tutti gli elementi, in questo caso entro $cond. La stessa cosa di $in che non è un operatore logico in questo senso sta utilizzando $or, che è un operatore logico per il framework di aggregazione.

Come qualsiasi elemento che non soddisfa la condizione restituire false, ciò che è necessario fare ora è "rimuovere" tutti i valori false. Questo è aiutato da $setDifference che lo farà al confronto.

Il risultato è quello che si vuole:

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

Se tu fossi veramente intenzionato a utilizzare $redact poi c'è sempre questo esempio inventato:

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

In modo che rende praticamente certo che se il campo non esiste affatto quando scende quindi produrrà "artificialmente" quello che sta per coincidere, in modo tale che il livello non venga "potato". Dove esiste il campo, viene utilizzato il valore trovato e, se ciò non corrisponde alla condizione, verrà eliminato.

+0

Grazie mille - hai salvato la mia giornata ;-) – Ralf

+0

@Ralf Felice di aiutare. $ redact è una cosa difficile per le persone per avere la testa come un nuovo concetto. Entrambi gli approcci sono validi, dipende solo da ciò che la sintassi sembra più chiara nel suo scopo per te e per i tuoi colleghi di lavoro. –