MongoDB:使用$ in的聚合聚合失败

时间:2014-07-01 08:21:01

标签: mongodb mongodb-query aggregation-framework

我在名为Objects的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
    }
 ]
}

我正在使用Mongo 2.6中引入的新聚合功能redact 我想执行查询以获取带有过滤设备部件的汽车列表。 用语言:给我所有类型的轿跑车,只提供有关LEDAIRBAG设备的信息。

我已尝试过这种聚合:

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

但这会导致以下错误:

  

断言:命令失败:{" errmsg" :"异常:无效的运算符   ' $ in'","代码" :15999," ok" :0}:聚合失败

我做错了什么?是否有另一种方法来实现目标。

我希望从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
    }
 ]
}

有关如何实现这一目标的任何建议吗?

干杯, 拉尔夫

1 个答案:

答案 0 :(得分:4)

对于这种情况,$redact管道运算符实际上不是您想要的那个。它想要做的是递归"下降"通过文件结构并评估每个级别的条件"看看它将采取什么行动。

在你的情况下,没有"名称"文档顶层的字段,以满足条件,因此整个文档被修剪"。

您正在过滤数组,对于您不想使用$unwind$match的情况,您可以使用新的运算符,例如$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]
            ]
        }
    }}
])

$map运算符使用数组并针对所有元素计算逻辑条件,在本例中为$cond。与 $in 相同的是,在这个意义上它不是逻辑运算符,而是使用$or,它是聚合框架的逻辑运算符。

由于任何不符合条件的项目都会返回false,您现在需要做的是"删除"所有false值。这得到$setDifference的帮助,通过比较可以做到这一点。

结果就是你想要的:

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

如果您真的打算使用 $redact ,那么总有这个人为的例子:

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

所以这基本上确保如果该字段在下降时根本不存在那么它将"人为地"产生一个匹配的,以便该等级不会被修剪"。如果该字段存在,则使用找到的值,如果该值与条件不匹配,则将对其进行修剪。