聚合匹配另一个数组

时间:2016-01-26 00:45:14

标签: mongodb mongodb-query aggregation-framework

我试图根据这个中的值返回数组的字段,但是在数组中存在的字段中使用第二个$ eq(特定于数组的数组),系统地给出了一个空结果

这是我的收藏的结构部分:

    ...
    _id: ObjectId("S"),
  array: [
     {
        X: "A",
        Y: NumberInt(0),
        Z: [NumberInt(5),NumberInt(6)]
     },
     {
        X: "B",
        Y: NumberInt(1),
        Z: [NumberInt(5),NumberInt(8)]
     },
     {
        X: "C",
        Y: NumberInt(0)
     }
  ],
    ...

和我的部分代码:

db.collection.aggregate(
   [
      { $match: { _id: ObjectId("56a108914b179d6efafca6f3") } },
      { $project: { _id: 1, list: { $setDifference: [ { $map: { input: "$array", as: "el", in: { $cond: [ { $and: [ { $eq: [ "$$el.Y", 0 ] }, { $eq: [ "$$el.Z", 5 ] } ] }, "$$el.X", false ] } } }, [false] ] } } } 
   ]
);

但是第二个$ eq参数不起作用...当我删除它时我有很好的结果(没有$$ el.Z相等)。

我想要回复:

{ "_id" : ObjectId("S"), "list" : [ "A" ] }

提前谢谢你,

最好的问候。

1 个答案:

答案 0 :(得分:1)

作为带有数组的聚合条件的$eq测试的工作方式与$match或常规查询中的有点不同。主要的不同之处在于它不会测试数组的每个元素,而是测试整个"数组"代替。

所以你基本上需要逻辑返回数组"元素"如果他们符合条件。这可以是另一个$map操作来进行转换,然后使用$anyElementTrue进行测试。或者使用$setIsSubset

db.collection.aggregate(
   [
      { "$match": { "_id": ObjectId("56a108914b179d6efafca6f3") } },
      { "$project":{ 
          "list": { 
              "$setDifference": [ 
                  { "$map": { 
                      "input": "$array", 
                      "as": "el",
                      "in": { 
                          "$cond": [ 
                              { "$and": [ 
                                  { "$eq": [ "$$el.Y", 0 ] }, 
                                  { "$setIsSubSet": [ [5], "$$el.Z" ] }
                              ] }, 
                              "$$el.X",
                              false
                          ]
                      }
                  }},
                  [false]
              ]
          }
      }} 
   ]
);

当然,如果您使用的是MongoDB 3.2或更高版本,那么使用$filter的语法变得更简单一些,以减少数组:

db.collection.aggregate(
   [
      { "$match": { "_id": ObjectId("56a108914b179d6efafca6f3") } },
      { "$project":{ 
          "list": { 
              "$map": {
                  "input": { "$filter": { 
                      "input": "$array", 
                      "as": "el",
                      "cond": { 
                         "$and": [ 
                             { "$eq": [ "$$el.Y", 0 ] }, 
                             { "$setIsSubSet": [ [5], "$$el.Z" ] }
                         ]
                      }
                  }},
                  "as": "el",
                  "in": "$$el.X"
              }}
          }
      }} 
   ]
);

但基本情况是这个逻辑测试需要应用于"数组"而不是将查看数组的每个成员的查询表单。因此必须使用正确的检查操作。

当然,数组聚合操作也不像基本查询运算符那样容易匹配元素。因此,如果数组甚至不存在,那么您需要在语句中替换该元素以获取有效的内容。 $ifNull运算符在这里有用:

db.collection.aggregate(
   [
      { "$match": { "_id": ObjectId("56a108914b179d6efafca6f3") } },
      { "$project":{ 
          "list": { 
              "$map": {
                  "input": { "$filter": { 
                      "input": "$array", 
                      "as": "el",
                      "cond": { 
                         "$and": [ 
                             { "$eq": [ "$$el.Y", 0 ] }, 
                             { "$setIsSubSet": [ [5], { "$ifNull": [ "$$el.Z", [] ] } ] }
                         ]
                      }
                  }},
                  "as": "el",
                  "in": "$$el.X"
              }}
          }
      }} 
   ]
);

同样在MongoDB 3.2中也有$isArray,但为此目的在语法上有点冗长,因为它将与$cond结合使用。仍有正当理由,如后面所示。

但实际上,对于你的目标,在测试条件基本上存在于数组元素的某个地方甚至应用数组操作和过滤之前是有意义的。因此,请将其作为$match条件的一部分:

db.collection.aggregate(
   [
      { "$match": { 
          "_id": ObjectId("56a108914b179d6efafca6f3"),
          "array": { "$elemMatch": { "Y": 0, "Z": 5 } }
      }},
      { "$project":{ 
          "list": { 
              "$map": {
                  "input": { "$filter": { 
                      "input": "$array", 
                      "as": "el",
                      "cond": { 
                         "$and": [ 
                             { "$eq": [ "$$el.Y", 0 ] }, 
                             { "$setIsSubSet": [ 
                                 [5], 
                                { "$cond": [ 
                                    { "$isArray": "$$el.Z" }.
                                    "$$el.Z",
                                    ["$$el.Z"]
                                ]}
                             ]}
                         ]
                      }
                  }},
                  "as": "el",
                  "in": "$$el.X"
              }}
          }
      }} 
   ]
);

再次使用paranoind检查$$el.Z实际上是一个数组,或者在评估中将其替换为实际上是数组的东西,以便与需要数组的运算符一起处理。

查询组件中的$elemMatch至少只选择要在过滤器中应用相同条件的文档,即使该检查本身并不能确保"Z"实际上是一个数组本身。