Найти записи с поля во вложенном документе, когда родительские поля не известны
С коллекцией с документами, как показано ниже, мне нужно найти документы, в которых определенное поле - например. 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 вывод для того, что вы ищете. Время выполнения будет аналогично счету коллекции.