2014-07-01 11 views
10

ich die folgenden zwei Objects in einer Mongo Sammlung cars benannt haben:

{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 
    } 
] 
} 

Ich spiele um mit der neuen Funktion für die Aggregation redact in Mongo 2.6 eingeführt Ich möchte eine Abfrage durchführen, um eine Liste von Autos mit gefilterten Ausrüstungsteilen zu erhalten. In Worten: Gib mir alle Autos vom Typ Coupé und nur die Informationen über LED und AIRBAG Ausrüstung.

Ich habe diese Aggregationen versucht:

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

Aber das führt zu folgenden Fehler:

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

Was mache ich falsch? Gibt es einen anderen Weg, um das Ziel zu erreichen?

Ich würde erwarten, wieder dieses Ergebnis von Mongo zu erhalten:

{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 
    } 
] 
} 

Vorschläge, wie dies zu erreichen?

Cheers, Ralf

+0

Sie möchten '$ setIsSubset' anstelle von' $ in' verwenden, wenn Sie in MongoDB Aggregationen (einschließlich Redigieren) durchführen. Siehe http://docs.mongodb.org/manual/reference/operator/aggregation/setIsSubset – Paul

+0

Sie können '$ filter' mit' $ in' von Version 3.4 verwenden. Etwas wie 'db.cars.aggregate ([ { " $ match ": { " klasse ":" coupe ", " equipment.name ": { " $ in ": [" airbag "," led " ] } } }, { "$ addFields": { "Ausrüstung": { "$ filter": { "Input": "$ Ausrüstung", "wie": "Ausrüstung", "cond": { "$ in": [ "$$ equipment.name", [ "Airbag", "geführt"] ] } } } } } ]) ' – Veeram

Antwort

4

Die $redact Pipeline-Betreiber ist wirklich nicht die, die Sie für diesen Fall wollen. Was es tun möchte, ist rekursiv durch die Dokumentenstruktur "abzusteigen" und die Bedingungen auf jeder "Ebene" zu bewerten, um zu sehen, welche Aktionen es unternehmen wird.

In Ihrem Fall gibt es kein "Name" -Feld auf der obersten Ebene des Dokuments, um die Bedingung zu erfüllen, so dass das gesamte Dokument "abgeschnitten" wird.

Sie sind nach Arrays Filterung, und für diesen Fall, dass Sie nicht wollen, $unwind und $match verwenden, dann können Sie die neuen Operatoren wie $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] 
      ] 
     } 
    }} 
]) 

Der $map Operator mit dem Array arbeitet und wertet eine logische Bedingung für alle Elemente aus, in diesem Fall innerhalb von $cond. Dasselbe wie , das kein logischer Operator in diesem Sinne ist, verwendet $or, was ein logischer Operator für das Aggregationsframework ist.

Da jedes Element, das die Bedingung nicht erfüllt, false zurückgibt, müssen Sie jetzt alle false Werte "entfernen". Dies wird durch $setDifference unterstützt, die dies zum Vergleich tun wird.

Das Ergebnis ist das, was Sie wollen:

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

Wenn Sie es wirklich Absicht waren mit $redact dann ist immer das erfundene Beispiel:

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

dass also im Grunde stellt sicher, dass, wenn das Feld existiert überhaupt nicht, wenn es absteigt, dann wird es "künstlich" einen erzeugen, der zusammenpassen wird, so dass das Level nicht "beschnitten" wird. Wo das Feld existiert, wird der gefundene Wert verwendet, und wenn dieser nicht mit der Bedingung übereinstimmt, wird er bereinigt.

+0

Vielen Dank - Sie haben meinen Tag gerettet ;-) – Ralf

+0

@Ralf Froh, zu helfen. $ redact ist eine harte Sache für die Menschen, sich als neues Konzept zu verstehen. Jeder Ansatz ist gültig, hängt nur davon ab, welche Syntax für Sie und Ihre Mitarbeiter am klarsten ist. –