ОК, это немного сложнее, потому что вам нужно использовать некоторую рекурсию.
Чтобы рекурсия состоялась, вам нужно иметь возможность хранить некоторые функции на сервере.
Шаг 1: определите некоторые функции и разместите их на стороне сервера
isArray = function (v) {
return v && typeof v === 'object' && typeof v.length === 'number' && !(v.propertyIsEnumerable('length'));
}
m_sub = function(base, value){
for(var key in value) {
emit(base + "." + key, null);
if( isArray(value[key]) || typeof value[key] == 'object'){
m_sub(base + "." + key, value[key]);
}
}
}
db.system.js.save( { _id : "isArray", value : isArray } );
db.system.js.save( { _id : "m_sub", value : m_sub } );
Шаг 2: определить карту и сократить функции
map = function(){
for(var key in this) {
emit(key, null);
if( isArray(this[key]) || typeof this[key] == 'object'){
m_sub(key, this[key]);
}
}
}
reduce = function(key, stuff){ return null; }
Шаг 3: запустите карту и посмотрите на результаты
mr = db.runCommand({"mapreduce" : "things", "map" : map, "reduce" : reduce,"out": "things" + "_keys"});
db[mr.result].distinct("_id");
Результаты, которые вы получите:
["_id", "_id.isObjectId", "_id.str", "_id.tojson", "egg", "egg.0", "foo", "foo.bar", "foo.bar.baaaar", "hello", "type", "type.0", "type.1"]
Здесь есть одна очевидная проблема, мы добавляем несколько неожиданных полей:
1. _id данные
2. .0 (на яйце и типа)
Шаг 4: Некоторые возможные исправления
Для задачи # 1 решение относительно просто. Просто измените функцию map
. Изменить это:
emit(base + "." + key, null); if( isArray...
к этому:
if(key != "_id") { emit(base + "." + key, null); if( isArray... }
Задача № 2 немного сложнее. Вы хотели все ключи и технически "egg.0" является действительным ключом. Вы можете изменить m_sub
, чтобы игнорировать такие цифровые клавиши. Но также легко увидеть ситуацию, когда это имеет неприятные последствия. Скажем, у вас есть ассоциативный массив внутри обычного массива, затем вы хотите, чтобы появилось «0». Я оставлю вам остальное от этого решения.