Mongodb根据记录中的字段数进行查询

Mongodb Query based on number of fields in a record

本文关键字:查询 字段 记录 Mongodb      更新时间:2023-09-26

我不太擅长在谷歌上搜索这个答案。

我有大约115个不同的字段,可能在每个记录中。Collection是一个巨大数据集上的mapreduce的输出。

看起来像这样:

{_id:'number1', value:{'a':1, 'b':2, 'f':5}},
{_id:'number2', value:{'e':2, 'f':114, 'h':12}},
{_id:'number3', value:{'i':2, 'j':22, 'z':12, 'za':111, 'zb':114}}

关于如何找到填充了5个字段的记录,有什么想法吗?

这仍然不是一个很好的查询,但有一种更现代的方法可以通过$objectToArray$redact 来实现

db.collection.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$eq": [
          { "$size": { "$objectToArray": "$value" } },
          3
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

其中$objectToArray基本上将对象强制为数组形式,就像JavaScript中Object.keys().map()的组合一样。

这仍然不是一个好主意,因为它确实需要扫描整个集合,但至少聚合框架操作使用"本地代码",而不是像使用$where那样使用JavaScript解释。

因此,为了进行最有效的查询操作,通常还是建议更改数据结构,尽可能使用自然数组以及存储的"大小"属性。


是的,这是可能的,但不是最好的方式。这是因为您实际上使用的是$where运算符查询,该查询使用JavaScript求值来匹配内容。不是最有效的方法,因为这永远不能使用索引,并且需要测试所有文档:

db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })

这将查找匹配"三个"元素的条件,然后只返回列出的两个文档:

{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }

或者,对于"五个"字段或更多字段,您可以执行大致相同的操作:

db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })

因此,该运算符的参数实际上是在服务器上评估的JavaScript语句,以返回其中的true

更有效的方法是将元素的"计数"存储在文档本身中。通过这种方式,您可以"索引"该字段,查询效率更高,因为由其他条件选择的集合中的每个文档都不需要扫描来确定长度:

{_id:'number1', value:{'a':1, 'b':2, 'f':5} count: 3},
{_id:'number2', value:{'e':2, 'f':114, 'h':12}, count: 3},
{_id:'number3', value:{'i':2, 'j':22, 'z':12, 'za':111, 'zb':114}, count: 5}

然后,要获得包含"五个"元素的文档,您只需要简单的查询:

db.collection.find({ "count": 5 })

这通常是最理想的形式。但另一点是,MongoDB在一般情况下"玩得很好"的不是你可能从一般实践中满意的通用"对象"结构。问题是对象中元素的"遍历",这样一来,当您使用"数组"时,MongoDB会更快乐。即使是这种形式:

{
    '_id': 'number1', 
    'values':[
        { 'key': 'a', 'value': 1 },
        { 'key': 'b', 'value': 2 }, 
        { 'key': 'f', 'value': 5 }
    ],
},
{
    '_id': 'number2', 
    'values':[
        { 'key': 'e', 'value': 2 }, 
        { 'key': 'f', 'value': 114 }, 
        { 'key': 'h', 'value': 12 }
    ],
},
{
    '_id':'number3', 
    'values': [
        { 'key': 'i', 'values': 2 }, 
        { 'key': 'j', 'values': 22 }, 
        { 'key': 'z'' 'values': :12 }, 
        { 'key': 'za', 'values': 111 },
        { 'key': 'zb', 'values': 114 }
    ]
}

因此,如果您真的切换到这样的"数组"格式,那么您可以使用$size运算符的一个版本来执行精确的数组长度:

db.collection.find({ "values": { "$size": 5 } })

该运算符可以为数组长度的精确值工作,因为这是使用该运算符可以执行的操作的基本规定。你不能做的事情记录在"平等"比赛中。为此,您需要MongoDB的"聚合框架",它是JavaScript和mapReduce操作的更好替代方案:

db.collection.aggregate([
    // Project a size of the array
    { "$project": {
        "values": 1,
        "size": { "$size": "$values" }
    }},
    // Match on that size
    { "$match": { "size": { "$gte": 5 } } },
    // Project just the same fields 
    {{ "$project": {
        "values": 1
    }}
])

所以这些是候补。有一种"本机"方法可用于聚合和数组类型。但是,JavaScript评估也是MongoDB的"原生",只是没有在原生代码中实现,这一点是有争议的。

由于MongoDB版本3.6,您也可以使用$jsonSchema(此处为文档):

db.getCollection('YOURCOLLECTION').find({
   "$jsonSchema":{
      "properties":{
         "value":{"minProperties": 5}
      }
   }
})