更新: MongoDB Get names of all keys in collection 的后续行动.
正如 Kristina 所指出的那样,可以使用 Mongodb 的 map/reduce 列出集合中的键:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] });
db.things.insert( { hello : [] } );
mr = db.runCommand({"mapreduce" : "things",
"map" : function() {
for (var key in this) { emit(key, null); }
},
"reduce" : function(key, stuff) {
return null;
}})
db[mr.result].distinct("_id")
//output: [ "_id", "egg", "hello", "type" ]
只要我们只想获取位于第一层深度的键,就可以了。但是,它将无法检索位于更深层次的那些 key 。如果我们添加一条新记录:
db.things.insert({foo: {bar: {baaar: true}}})
然后我们再次运行上面的 map-reduce +distinct 片段,我们将得到:
[ "_id", "egg", "foo", "hello", "type" ]
但是我们不会得到嵌套在数据结构中的 bar 和 baaar 键。问题是:我如何检索所有键,无论它们的深度如何?理想情况下,我实际上希望脚本深入到所有深度,产生如下输出:
["_id","egg","foo","foo.bar","foo.bar.baaar","hello","type"]
提前谢谢你!
最佳答案
好的,这有点复杂,因为您需要使用一些递归。
要实现递归,您需要能够在服务器上存储一些函数。
第一步:定义一些函数,放到服务器端
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 和 reduce 函数
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 步:运行 map reduce 并查看结果
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 有点冒险。您想要 all 键,并且从技术上讲,“egg.0”is 是一个有效的键。您可以修改 m_sub
以忽略此类数字键。但也很容易看到事与愿违的情况。假设您在常规数组中有一个关联数组,那么您希望“0”出现。我将把剩下的解决方案留给你。
关于mongodb - 使用 map/reduce 映射集合中的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2997004/