我试图找出一种在mongo / python中执行以下操作的优雅方法:我有两个集合:一个包含人员和属性列表,另一个包含人口子集“人口子集”。我想运行一个map reduce工作来计算大型列表中的一些聚合统计数据,但只使用人口样本中出现的人员名称。以下是一组示例记录:
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并且在我的数据库中创建了一个永久性标志,这种标志因各种原因而烦人。
更新
在阅读了查询中嵌入列表的建议之后,我能够通过以下内容得到我需要的内容
map_reduce(mapper, reducer, out = {'merge': 'Stats'},
finalize = finalizer, scope = {'atts': f},
query = {'Name' : { '$in' : pop }})
其中pop是一个python名称列表。谢谢!
答案 0 :(得分:4)
在MongoDB中有两种方法可以解决这个问题。
如果您的子集相当小,您只需对子集进行查询即可查找所有成员,并将该查询的结果用作map-reduce调用的初始query。
但是,如果您有非常大的子集,则可能无法实现。那么你可以做的是使用'reduce'输出选项将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}
最后,您可以对此中间集合执行第三个map reduce,以平均所有具有inSubset: true
的文档。
以下是Python中使用pymongo驱动程序的2.选项(三个map-reduced)的代码:
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'})
结果集合看起来像这样(从mongo shell查询):
> db.result.find()
{ "_id" : "total", "value" : { "age" : 79, "count" : 2, "averageAge" : 39.5 } }
,最终平均值存储在value.averageAge字段中。