如何查询MongoDB中数组的字段数超过n次的文档

时间:2015-10-13 11:56:46

标签: mongodb mongodb-query aggregation-framework

我有一个MongoDB集合world,其中包含以下格式的文档:

{
  _id : ObjectId("4e8ae86d08101908e1000001"),
  country : [
      {
          state: "Newyork",
          type: 1
      },
      {
          state: "California",
          type: 1
      },
      {
          state: "Texas",
          type: 2
      }
  ]
}

我们可以轻松获取档案中有四种或更多州的文件:

db.world.find({'country.4': {$exists: true} })

但是,我怎样才能获得具有四个或更多状态type: 1的国家/地区数组的文档?

另外,我想避免在查询中使用$where运算符。

修改1

Blakes Seven 的回答对我来说似乎是对的,但是当我尝试反向时,得到 的文件比n字段的文件然后我得到错误的结果:

以下是查询:

db.world.aggregate([
    { "$redact": {
        "$cond": {
            "if": {
                "$lte": [
                    { "$size": { "$setDifference": [
                        { "$map": {
                            "input": "$country",
                            "as": "el",
                            "in": {
                                "$cond": {
                                    "if": { "$eq": [ "$$el.type", 769 ] },
                                    "then": "$$el",
                                    "else": false
                                }
                            }
                        }},
                        [false]
                    ]}},
                    4
                ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}
]);

1 个答案:

答案 0 :(得分:3)

最重要的是,您需要过滤掉不匹配项并“计算”匹配项的出现次数,以确定文档是否符合您的条件。这可以通过过滤数组上的$size运算符完成,作为$redact逻辑测试的一部分。

$setIsSubset在其他地方提出的建议无效,因为“套装”基本上取消了任何重复的项目。这意味着任何匹配都将减少到:

"$setIsSubset": [[1,0],[1]]

这当然是false条件。这是因为大多数情况下阵列成员不匹配(从而产生0)并且每个“集合”被有效地减少为“唯一”成员。即使“所有”成员匹配,结果也会减少到:

"$setIsSubset": [[1],[1]]

虽然这是一个积极的匹配,但绝对没有断言确实符合所需的“数量”匹配。

因此,只要数组成员本身实际上是“唯一的”,那么您可以采用这种方法来过滤和计算匹配项:

db.world.aggregate([
    { "$match": { "country.3": { "$exists": true } }},
    { "$redact": {
        "$cond": {
            "if": {
                "$gte": [
                    { "$size": { "$setDifference": [
                        { "$map": {
                            "input": "$country",
                            "as": "el",
                            "in": {
                                "$cond": {
                                    "if": { "$eq": [ "$$el.type", 1 ] },
                                    "then": "$$el",
                                    "else": false
                                }
                            }
                        }},
                        [false]
                    ]}},
                    4
                ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}
])

因此,返回整个元素以进行$setDifference的“设置”比较,以过滤掉所返回的任何false值。然后测试没有匹配的结果数组$size以查看是否满足必要的匹配,并且文档通过$$PRUNE被丢弃,而不是。{/ p>

当然$map这里处理每个元素的工作要么返回整个原始元素,要么在它不满足条件的地方交替false

如果实际上在数组中说“加利福尼亚”的重复信息是重要的,那么未来的MongoDB版本将会$filter,这两个版本都会简化过程,最重要的是不会删除重复项。减少到“集合”:

db.world.aggregate([
    { "$match": { "country.3": { "$exists": true } }},
    { "$redact": {
        "$cond": {
            "if": {
                "$gte": [
                    { "$size": { "$filter": {
                        "input": "$country",
                        "as": "el",
                        "cond": {
                            "$eq": [ "$$el.type", 1 ]
                        }
                    }}},
                    4
                ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}
])

但是当然在该版本发布之前,您需要一种更传统的方法,使用$unwind$match来过滤数组,同时保留重复数据,然后通过$group获取“计数”第一:

db.world.aggregate([
    { "$match": { "country.3": { "$exists": true } }},
    { "$project": { "country": 1, "countryCopy": "$country" } },
    { "$unwind": "$country" },
    { "$match": { "country.type": 1 } },
    { "$group": {
        "_id": "$_id",
        "country": { "$first": "$countryCopy" }
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$gte": 4 } }}
])

但希望你在数组中没有重复项,所以没关系。

使用初始$match通过测试至少存在的最小索引并丢弃文档来立即过滤掉数组而不需要可能匹配的数组(数组索引为n-1)也很重要在这种情况下,处理时总共少于4个元素。

这是$exists测试,在这里很有用。由于没有足够的元素可以开始,因此会减少尝试匹配后续处理中的文档,这些文档无法满足所需的匹配计数。