基于记录中字段数量的Mongodb查询

4

我在谷歌上找不到这个答案。

每条记录可能有大约115个不同的字段。该集合是对一个非常大的数据集进行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个字段的记录吗?


1
是的。在计算Map/Reduce中的记录时,计算填充字段的数量并将其存储在记录中。 - wdberkeley
谢谢 - 我刚在想这应该是一个标准函数! - Malcolm Murdoch
2个回答

7

这仍然不是一个好的查询方式,但是可以通过$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" })

这个条件是寻找匹配"three"元素的情况,然后只返回你列出的文件中的两个:

{ "_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" })

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

一种更有效的方法是将文档中元素的“计数”存储在文档本身中。通过这种方式,您可以对此字段进行“索引”,查询更加高效,因为不需要扫描集合中满足其他条件的每个文档以确定长度:

{_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本来就应该原生支持的事情之一! - Malcolm Murdoch
@MalcolmMurdoch 花了一些时间才回到这个问题。更多的信息和解释表明这是MongoDB的“本地”功能。它完全是服务器处理,而不是客户端处理。主要是指出在文档中保留字段是一个好主意。 - Neil Lunn

1
自MongoDB 3.6版本以来,您还可以使用$jsonSchema文档在此处)进行此操作:
db.getCollection('YOURCOLLECTION').find({
   "$jsonSchema":{
      "properties":{
         "value":{"minProperties": 5}
      }
   }
})


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接