使用map / reduce来映射集合中的属性

更新: MongoDB的后续获取集合中所有键的名称 。

正如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" ] 

只要我们只想得到位于第一级深度的按键,这就可以正常工作了。 但是,它将无法检索到位于更深层次的那些密钥。 如果我们添加一个新logging:

 db.things.insert({foo: {bar: {baaar: true}}}) 

我们再次运行上面的map-reduce +截取的片段,我们会得到:

 [ "_id", "egg", "foo", "hello", "type" ] 

但是我们不会得到嵌套在数据结构中的barbaaar键。 问题是:我怎么检索所有的键,不pipe他们的深度水平? 理想的情况下,我真的喜欢这个脚本走到所有的深度,产生一个输出,如:

 ["_id","egg","foo","foo.bar","foo.bar.baaar","hello","type"] 

先谢谢你!

好吧,这有点复杂,因为你需要使用一些recursion。

为了使recursion发生,您需要能够在服务器上存储一些函数。

第1步:定义一些function,并把它们放在服务器端

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

第二步:定义地图并减lessfunction

 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(在蛋和types上)

第4步:一些可能的修复

对于问题1 ,修复相对容易。 只要修改mapfunction。 改变这个:

 emit(base + "." + key, null); if( isArray... 

对此:

 if(key != "_id") { emit(base + "." + key, null); if( isArray... } 

问题#2更有点冒险。 你想要所有的钥匙和技术上“egg.0” 一个有效的关键。 您可以修改m_sub来忽略这些数字键。 但是也很容易看到这种情况发生。 假设你在一个常规数组中有一个关联数组,那么你想要显示“0”。 我将把剩下的解决scheme留给你。

以Gates VP和Kristina的答案为灵感,我创build了一个名为Variety的开源工具,它完全实现了这个function: https : //github.com/variety/variety

希望你会发现它是有用的。 如果您有任何问题或使用它的任何问题,请告诉我。