2012-09-13 21 views
0

我想弄清楚在mongo/python中执行以下操作的优雅方法:我有两个集合:一个包含人员和属性列表,一个包含子集那些是“人口子集”的人。我想运行地图缩减作业来计算大型列表中的某些汇总统计信息,但仅使用出现在人口样本中的人员的姓名。下面是一组记录的例子:筛选器映射减少到匹配不同集合的记录

master_list: [{ Name: Jim }, { Age: 24} 
       { Name: Bill}, { Age: 38} 
       { Name: Mary}, { Age: 55}] 

subset : [{ Name: Jim} 
      { Name: Mary}] 

的想法是计算的年龄,但只使用两个的三个记录在master_list平均,作为集上市。我意识到,mongo中的map_reduce支持查询参数,但不清楚处理上述问题的最佳方式是否给予了不加入。一种选择是我预处理master_list并创建一个属性'include'来标记要使用的记录,然后在map_reduce过滤器中对其进行操作。似乎kludgy虽然并在我的数据库中创建一个永久性的标志是由于各种原因恼人。

UPDATE

阅读建议在查询中嵌入列表后,我能得到什么,我需要用下面

map_reduce(mapper, reducer, out = {'merge': 'Stats'}, 
      finalize = finalizer, scope = {'atts': f}, 
      query = {'Name' : { '$in' : pop }}) 

哪里流行为名称的Python列表。谢谢!

+0

子集合有多大? –

+0

子集很小,大概有1500-2000个名字 –

回答

3

在MongoDB中有两种方法可以解决这个问题。

  1. 如果你的子集是相当小的,你可以做的子集的查询查找所有成员,并使用该查询的结果作为初始query的地图,减少通话。

  2. 但是,如果你有非常大的子集,这可能是不可能的。你可以做什么,是simulate a join using two map-reduce calls与'减少'输出选项减少到相同的目标集合。

    {Name: Jim, Age: 24, inSubset: true} 
    {Name: Bill, Age: 38, inSubset: false} 
    {Name: Mary, Age: 55, inSubset: true} 
    

    最后,您可以执行第三地图减轻这中间收集到平均值所有具有inSubset: true文件:其中的文件看起来像这样这将创建一个中间收集。

下面是2.选项的代码(三地图降低)在Python,使用pymongo驱动程序:

from pymongo import Connection 
from bson import ObjectId, Code 

con = Connection(port=30000) # add host/port here if different from default 
db = con['test'] # or the database name you are using 

# insert documents 
db.master.insert({'_id': ObjectId(), 'Name': 'Jim', 'Age': 24}) 
db.master.insert({'_id': ObjectId(), 'Name': 'Bill', 'Age': 38}) 
db.master.insert({'_id': ObjectId(), 'Name': 'Mary', 'Age': 55}) 

db.subset.insert({'_id': ObjectId(), 'Name': 'Jim'}) 
db.subset.insert({'_id': ObjectId(), 'Name': 'Mary'}) 

# map function for master collection 
mapf_master = Code(""" function() { 
    emit(this.Name, {'age': this.Age, 'inSubset': false}); 
} """) 

# map function for subset collection 
mapf_subset = Code(""" function() { 
    emit(this.Name, {'age': 0, 'inSubset': true}); 
} """) 

# reduce function for both master and subset 
reducef = Code(""" function(key, values) { 
    var result = {'age': 0, 'inSubset': false}; 

    values.forEach(function(value) { 
     result.age += value.age; 
     result.inSubset = result.inSubset || value.inSubset; 
    }); 

    return result; 
} """) 

# call map-reduce on master and subset (simulates a join) 
db.master.map_reduce(mapf_master, reducef, out={'reduce': 'join'}) 
db.subset.map_reduce(mapf_subset, reducef, out={'reduce': 'join'}) 


# final map function for third map-reduce call 
mapf_final = Code(""" function() { 
    if (this.value.inSubset) { 
     emit('total', {'age': this.value.age, 'count': 1}); 
    } 
} """) 

# final reduce function for third map-reduce call 
reducef_final = Code(""" function(key, values) { 
    var result = {'age': 0, 'count': 0}; 

    values.forEach(function(value) { 
     result.age += value.age; 
     result.count += value.count; 
    }); 

    return result; 
} """) 


# final finalize function, calculates the average 
finalizef_final = Code(""" function(key, value) { 
    if (value.count > 0) { 
     value.averageAge = value.age/value.count; 
    } 
    return value; 
} """) 


# call final map-reduce 
db.join.map_reduce(mapf_final, reducef_final, finalize=finalizef_final, out={'merge': 'result'}) 

结果集合是这样的(从蒙戈壳查询):

> db.result.find() 
{ "_id" : "total", "value" : { "age" : 79, "count" : 2, "averageAge" : 39.5 } } 

并且最终的平均值存储在value.averageAge字段中。

+0

谢谢,我想我会选择1,因为我的子集的名字是1500-2000。这种大小你认为是否合适,如果是这样,你会如何提供一个如何有效地将它传递给地图缩减调用的例子? –

+0

我认为你的列表大小应该适用于选项1 - 对于小型子集,它将比选项2快得多。您可能只需指定需要与主集合匹配的关键字段,以免在查询中返回任何额外的东西(如果有任何额外的东西)。 –