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

标签 mongodb mapreduce

更新: 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" ] 

但是我们不会得到嵌套在数据结构中的 barbaaar 键。问题是:我如何检索所有键,无论它们的深度如何?理想情况下,我实际上希望脚本深入到所有深度,产生如下输出:

["_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/

相关文章:

java - 在 Hadoop 中并行化 Ruby reducer?

mongodb - mongodb 中日期的分钟差异

hadoop - 将命令行参数传递给 jvm(java) 映射器任务

javascript - 设计 MongoDB 数据库 - 许多嵌入式文档可以吗?

javascript - 基于先前调用的响应的异步调用,避免回调 hell

c# - MongoDB C# 驱动程序 : Ignore Property on Insert

java - 具有 HAR 文件输出的 MapReduce 作业

python - 如何使用 python 在 MongoDB 中创建模式?

mongodb - NOT AND 在 mongodb 中

java - Hadoop Map-Reduce 。记录阅读器