当父字段未知时,在嵌套文档中查找包含字段的记录

时间:2017-02-04 05:49:19

标签: mongodb

使用下面的文档集合,我需要找到特定字段的文档 - 例如。 lev3_field2(在下面的文档中)存在。

我尝试了以下操作,但这并没有返回任何结果,但某些文档中存在字段lev3_field2

db.getCollection('some_collection').find({"lev3_field2": { $exists: true, $ne: null } })

{
    "_id" : ObjectId("5884de15bebf420cf8bb2857"),
    "lev1_field1" : "139521721",
    "lev1_field2" : "276183",
    "lev1_field3" : {
        "lev2_field1" : "4",
        "lev2_field2" : {
            "lev3_field1" : "1",
            "lev3_field2" : {
                "lev4_field1" : "1",
                "lev4_field2" : "1"
            },
            "lev3_field3" : "5"
        },
        "lev2_field3" : {
            "lev3_field3" : "0",
            "lev3_field4" : "0"
        }
    }
}

update1 :这是一个示例,但是在真实文档中,不知道要查找的字段的父字段是什么。因此,我会寻找`levM_fieldN'而不是lev3_field2

update2 :速度不是我主要关注的问题,我可以使用相对慢一点的选项,因为主要功能是找到符合所讨论标准的文档,一旦文档是找到并且理解了模式,可以通过包含父键来重写查询以提高性能。

3 个答案:

答案 0 :(得分:8)

要搜索嵌套文档中的键,需要递归迭代文档字段,可以借助MongoDB中的 $ where 方法在JavaScript中执行此操作 以下查询将搜索文档及其子文档中是否存在键名。

我已经用你给出的例子检查了这个,并且它工作得很好。

db.getCollection('test').find({ $where: function () {
    var search_key = "lev3_field2";

    function check_key(document) {
      return Object.keys(document).some(function(key) {
        if ( typeof(document[key]) == "object" ) {
            if ( key == search_key ) {
                return true;
            } else {
                return check_key(document[key]);
            }
        } else {
          return ( key == search_key );
        }
      });
    }
    return check_key(this);
  }}

);

答案 1 :(得分:3)

没有内置函数来迭代MongoDB中的文档键,但您可以使用MapReduce实现此功能。主要优点是所有代码都直接在MongoDB数据库中执行,而不是在js客户端中执行,因此没有网络开销,因此它应该比客户端js更快

这是脚本:

    var found;

    // save a function in MongoDB to iterate over documents key and check for 
    // key name. Need to be done only once
    db.system.js.save({
        _id: 'findObjectByLabel',
        value: function(obj, prop) {
            Object.keys(obj).forEach(function(key) {
                if (key === prop) {
                    found = true
                }
                if (!found && typeof obj[key] === 'object') {
                    findObjectByLabel(obj[key], prop)
                }
            })
        }
    })

// run the map reduce fonction 
    db.ex.mapReduce(
        function() {
            found = false;
            var key = this._id
            findObjectByLabel(this, 'lev3_field2')
            value = found;
            if (found) {
              // if the document contains the key we are looking for,
              // emit {_id: ..., value: true }
              emit(key, value)
            }
        },
        function(key, values) {
            return values
        }, {
            'query': {},
            'out': {inline:1}
        }
    )

此输出(在4个示例文档上运行,只有一个包含' lev3_field2')

{
    "results" : [
        {
            "_id" : ObjectId("5884de15bebf420cf8bb2857"),
            "value" : true
        }
    ],
    "timeMillis" : 18,
    "counts" : {
        "input" : 4,
        "emit" : 1,
        "reduce" : 0,
        "output" : 1
    },
    "ok" : 1
}

运行脚本,将其复制到文件名" script.js"例如,然后从你的shell运行

mongo databaseName < script.js

答案 2 :(得分:0)

这是因为您正在尝试查看嵌套字段是否存在。这是您想要的查询:

db.some_collection.find({"lev1_field3.lev2_field2.lev3_field2": { $exists: true, $ne: null } })