$ setIntersection失败,包含不在集合中的子文档数组

时间:2016-04-15 05:52:37

标签: mongodb mongodb-query aggregation-framework

考虑以下文件:

{
    "item1" : [
        {
            "a" : 1,
            "b" : 2
        }
    ],
    "item2" : [ "a", "b" ]
}

以下查询:

db.test.aggregate([ 
    { "$project": { "items": { "$setIntersection": [ "$item1", "$item2" ] } }}
])

返回预期结果:

{ "_id" : ObjectId("5710785387756a4a75cbe0d1"), "a" : [ ] }

如果文档如下所示:

{ "item2" : [ "a", "b" ] }

然后:

db.test.aggregate([ { "$project": { 
    "a": { "$setIntersection": [ "$item2", [ "a" ] ] } } } 
])

收率:

{ "_id" : ObjectId("5710785387756a4a75cbe0d1"), "a" : [ "a" ] }

但是

db.test.aggregate([         
    { "$project": { "items": { "$setIntersection": [ "$item2", [ { "a" : 1, "b" : 2 } ] ] } } } 
])

失败了:

  

" ERRMSG" :" $ expressions;#34;

中不允许使用字段包含

db.test.aggregate([ { "$project": { 
    "items": { "$setIntersection": [ "$item2", [ { "a": "b" } ] ] } } } 
])

失败了:

  

" ERRMSG" :" FieldPath' b'不以$"

开头

使这项工作的唯一方法是使用$literal运算符。

如果$literal参数是子文档数组而不是文档中的字段,我们为什么要使用$setIntersection运算符?

2 个答案:

答案 0 :(得分:1)

这似乎是MongoDB 3.2的一个工件,它包含了一个允许在直接插入文档属性时注释数组的更改。

例如,使用如下文档:

 { "a": 1, "b": 2, "c": 3, "d": 4 }

然后你“现在”允许在数组中标记这些元素,例如:

db.collection.aggregate([
    { "$project": {
        "array": [
           { "field1": "$a", "field2": "$b" },
           { "field1": "$c", "field2": "$d" }
        ]
    }}
])

在以前的版本(在本例中为MongoDB 2.6)中,您需要使用此$map表达式:

db.collection.aggregate([
    { "$project": {
        "array": {
            "$map": {
                "input": ["A","B"],
                "as": "el",
                "in": {
                    "$cond": {
                        "if": { "$eq": [ "$$el", "A" ] },
                        "then": { "field1": "$a", "field2": "$b" },
                        "else": { "field1": "$c", "field2": "$c" }
                    }
                }
            }
        }
    }}
])

或者在之前的版本中,使用$unwind$group进行更长时间的渲染,但是将“源”数组与其他数据进行转置的基本原则相同。但重点是MongoDB 3.2中允许的符号变化,否则在先前版本中会出现“错误”。

因此在先前的版本中,比如支持$setIntersection的MongoDB 2.6.x,那么以下工作正常,因为除非实际引用文档中存在的数组,否则所有值都被视为“文字”:

db.collection.aggregate([
  { "$project": {
    "a": { 
      "$setIntersection": [
        [{"a": 1}],
        [{"a": 1}]
      ]
    }
  }}
])

当然,"collection"作为一个集合实际上有一些东西。

但是由于MongoDB 3.2为“插值数组”提供了不同的语法,现在它希望“右侧”从文档或其他有效表达式中评估属性。所以现在需要$literal语法:

db.collection.aggregate([
  { "$project": {
    "a": { 
      "$setIntersection": [
        { "$literal": [{"a": 1}] },
        { "$literal": [{"a": 1}] }
      ]
    }
  }}
])

这通常归结为“你不能吃蛋糕而且吃得太多”的说法。 “新”语法允许您以一种很好的方式用“插值”表达数组内容,而无需借助其他表达式将内容“强制”为数组形式。

这样做的结果是每个这样的表达式现在都期望“值”解析为属性或表达式,而不是直接被视为“文字”,并且你的意思是这样的,您现在需要使用$literal运算符表达。

因此,它实际上是版本之间允许语法的“突破”变化。但大多数人应该容易接受的一个。

答案 1 :(得分:0)

这似乎是MongoDB 3.2中的兼容性更改,因此是Aggregation Compatibility Changes in MongoDB 3.2中提到的预期行为:

  

数组元素不再被视为聚合管道中的文字。相反,数组的每个元素现在都被解析为表达式。要将元素视为文字而不是表达式,请使用$literal运算符创建文字值。