Найти записи с поля во вложенном документе, когда родительские поля не известны

С коллекцией с документами, как показано ниже, мне нужно найти документы, в которых определенное поле - например. 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: это пример, однако в реальном документе неизвестно, какие родительские поля предназначены для поиска. Поэтому вместо lev3_field2 я бы искал `levM_fieldN '.

update2: скорость не является для меня основной проблемой, я могу работать с относительно более медленными параметрами, поскольку основная функция заключается в поиске документов с обсуждаемыми критериями и после того, как документ и схема понятна, запрос может быть переписан для производительности, включив родительские ключи.

Ответы

Ответ 1

Чтобы выполнить поиск ключа во вложенном документе, вам нужно повторить итерацию полей документов, вы можете сделать это в JavaScript с помощью метода $where в MongoDB Следующий запрос будет искать, существует ли ключевое имя в документах и ​​его вложенных документах.

Я проверил это с примером, который вы указали, и работает отлично.

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);
  }}

);

Ответ 2

Нет встроенной функции для перебора ключей документов в MongoDB, но вы можете добиться этого с помощью MapReduce. Основное преимущество заключается в том, что весь код выполняется непосредственно в базе данных MongoDB, а не в js-клиенте, поэтому нет сетевых накладных расходов, следовательно, он должен быть быстрее, чем клиентская сторона js

здесь находится script:

    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 примерах doc, причем только один содержит "lev3_field2" )

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

для запуска script, скопируйте его на имя файла script.js ", а затем запустите из своей оболочки

mongo databaseName < script.js

Ответ 3

Это потому, что вы пытаетесь увидеть, существует ли вложенное поле. Это требуемый запрос:

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

Ответ 4

вы можете использовать variety, анализатор схемы mongodb.

git клонировать его и печатать все поля коллекции для максимальная глубина:

mongo db_name --eval "var collection = 'collection_name'" variety.js

grep вывод для того, что вы ищете. Время выполнения будет аналогично счету коллекции.