如何选择包含值数组的数组项的文档?

时间:2015-08-05 14:55:10

标签: mongodb mongodb-query aggregation-framework

我收集了mongodb(3.0):

{
    _id: 1, 
    m: [{_id:11, _t: 'type1'},
        {_id:12, _t: 'type2'}, 
        {_id:13, _t: 'type3'}]
},    
{
    _id: 2, 
    m: [{_id:21, _t: 'type1'},
        {_id:22, _t: 'type21'}, 
        {_id:23, _t: 'type3'}]
}

我想查找m个属性的文档,其中m._t包含['type1','type2']。

像这样:

{
    _id: 1, 
    m: [{_id:11, _t: 'type1'},
        {_id:12, _t: 'type2'}]
},    
{
    _id: 2, 
    m: [{_id:21, _t: 'type1'}]
}

我尝试使用$和$ elemMatch,但无法获得所需的结果。 怎么做,使用find()? 请帮帮我!谢谢!

2 个答案:

答案 0 :(得分:1)

因为 $elemMatch 运算符会将查询结果中 m 数组字段的内容限制为仅包含 first < / strong>匹配 $elemMatch 条件的元素,以下内容仅返回带有第一个匹配元素的数组

{
     "_id" : 11,
    "_t" : "type1"
}

{
    "_id" : 21,
    "_t" : "type1"
}

使用 $elemMatch 投影查询:

db.collection.find(
    { 
        "m._t": {
            "$in": ["type1", "type2"]
        }
    },
    {
        "m": {
            "$elemMatch": {
                "_t": {
                    "$in": ["type1", "type2"]
                }
            }
        }
    }
)

<强>结果

/* 0 */
{
    "_id" : 1,
    "m" : [ 
        {
            "_id" : 11,
            "_t" : "type1"
        }
    ]
}

/* 1 */
{
    "_id" : 2,
    "m" : [ 
        {
            "_id" : 21,
            "_t" : "type1"
        }
    ]
}

您可以采取的一种方法是 aggregation framework ,其中您的管道包含 $match 运算符,类似于上面的查找查询过滤初始文档流。下一个管道步骤将是关键的 $unwind 运算符,它将数组元素“拆分”,以便与另一个 $match 运算符进一步简化,然后最终 $group 管道,使用累加器运算符 $push 恢复原始数据结构。

以下说明了这条道路:

db.collection.aggregate([
    {
        "$match": {
            "m._t": {
                "$in": ["type1", "type2"]
            }
        }
    },
    {
        "$unwind": "$m"
    },
    {
        "$match": {
            "m._t": {
                "$in": ["type1", "type2"]
            }
        }
    },
    {
        "$group": {
            "_id": "$_id",
            "m": {
                "$push": "$m"
            }
        }
    }
])

示例输出:

/* 0 */
{
    "result" : [ 
        {
            "_id" : 2,
            "m" : [ 
                {
                    "_id" : 21,
                    "_t" : "type1"
                }
            ]
        }, 
        {
            "_id" : 1,
            "m" : [ 
                {
                    "_id" : 11,
                    "_t" : "type1"
                }, 
                {
                    "_id" : 12,
                    "_t" : "type2"
                }
            ]
        }
    ],
    "ok" : 1
}

答案 1 :(得分:1)

让你的&#34;过滤&#34;结果,带有聚合管道的$redact是最快的方法:

db.junk.aggregate([
  { "$match": { "m._t": { "$in": ["type1", "type2"] } } },
  { "$redact": {
    "$cond": {
      "if": { 
        "$or": [
          { "$eq": [ { "$ifNull": ["$_t", "type1"] }, "type1" ] },
          { "$eq": [ { "$ifNull": ["$_t", "type2"] }, "type2" ] }
        ],
      },
      "then": "$$DESCEND",
      "else": "$$PRUNE"
    }
  }}
])

$redact运算符为文档设置逻辑过滤器,该过滤器也可以遍历数组级别。请注意,这在文档的所有级别上都匹配_t,因此请确保没有其他元素共享此名称。

查询使用$in进行选择,就像逻辑过滤器使用$or一样。任何不匹配的东西都会得到修剪&#34;。

{
    "_id" : 1,
    "m" : [
            {
                    "_id" : 11,
                    "_t" : "type1"
            },
            {
                    "_id" : 12,
                    "_t" : "type2"
            }
    ]
}
{ 
    "_id" : 2, 
    "m" : [ { "_id" : 21, "_t" : "type1" } ]
}

简短而又简单。

稍微麻烦一点,但使用$map$setDifference这个结构过滤结果会更合理一些:

db.junk.aggregate([
  { "$match": { "m._t": { "$in": ["type1", "type2"] } } },
  { "$project": {
    "m": {
      "$setDifference": [
        { "$map": {
          "input": "$m",
          "as": "el",
          "in": {
            "$cond": {
              "if": {
                "$or": [
                  { "$eq": [ "$$el._t", "type1" ] },
                  { "$eq": [ "$$el._t", "type2" ] }
                ]
              },
              "then": "$$el",
              "else": false
            }
          }
        }},
        [false]
      ]
    }
  }}      
])

$map评估每个元素的条件,$setDifference删除任何返回false而不是数组内容的条件。与上面的编辑中的$cond非常相似,但它只是专门用于一个数组,而不是整个文档。

在未来的MongoDB版本中(目前在开发版本中可用),将有$filter运算符,这非常容易理解:

db.junk.aggregate([
  { "$match": { "m._t": { "$in": ["type1", "type2"] } } },
  { "$project": {
    "m": {
      "$filter": {
        "input": "$m",
        "as": "el",
        "cond": {
          "$or": [
            { "$eq": [ "$$el._t", "type1" ] },
            { "$eq": [ "$$el._t", "type2" ] }
          ]
        }
      }
    }
  }}
])

这将删除任何与指定条件不匹配的数组元素。

如果要在服务器上过滤数组内容,则可以使用聚合框架。