我在使用map reduce调整数据集时遇到问题。我一直在使用MongoDB食谱寻求帮助,但我遇到了一些奇怪的错误。我想取下面的集合并对其进行调整,以便每个用户都有一个所有评论评级的列表。
我的收藏如下:
{
'type': 'review',
'business_id': (encrypted business id),
'user_id': (encrypted user id),
'stars': (star rating),
'text': (review text),
}
映射函数(用Python封装):
map = Code(""""
function(){
key = {user : this.user_id};
value = {ratings: [this.business_id, this.stars]};
emit(key, value);
}
""")
map函数应该返回一个与键关联的值数组。。。Reduce函数(封装在Python中):
reduce = Code("""
function(key, values){
var result = { value: [] };
temp = [];
for (var i = 0; i < values.length; i++){
temp.push(values[i].ratings);
}
result.value = temp;
return result;
}
""")
但是,结果返回的评分比总分少一分。事实上,有些用户返回了None,这是不可能的。一些条目如下所示:
{u'_id': {u'user: u'zwZytzNIayFoQVEG8Xcvxw'}, u'value': [None, [u'e9nN4XxjdHj4qtKCOPQ_vg', 3.0], None, [...]...]
我无法确定代码中的原因。如果有3条评论,那么它们在文档中都有业务ID和评级。另外,在循环条件中使用"values.length+1"会由于某种原因破坏values[i]。
编辑1
我已经接受了reduce本身被多次调用的事实,所以下面是我的新reduce。这将返回一个[业务,评级,业务,评级]数组。知道如何输出[业务,评级]数组而不是一个巨大的数组吗?
function(key, value){
var result = { ratings:[] };
var temp = [];
values.forEach(function(value){
value.ratings.forEach(function(rating){
if(temp.indexof(rating) == -1){
temp.push(rating);
}
});
});
result. rartings = temp;
return result;
}
下面是一个测试示例:
1) 添加一些样本数据:
db.test.drop();
db.test.insert(
[{
'type': 'review',
'business_id': 1,
'user_id': 1,
'stars': 1,
},
{
'type': 'review',
'business_id': 2,
'user_id': 1,
'stars': 2,
},
{
'type': 'review',
'business_id': 2,
'user_id': 2,
'stars': 3,
}]
);
2) 地图功能
var map = function() {
emit(this.user_id, [[this.business_id, this.stars]]);
};
在这里,我们将结果设置为我们希望它们在过程结束时看起来的样子。为什么?因为如果用户只有一次审查(我们分组的关键),那么结果就不会经过减少阶段。
3) 减少功能
var reduce = function(key, values) {
var result = { ratings: [] };
values.forEach(function(value){
result.ratings.push(value[0]);
});
return result;
};
在这里,我们收集所有的值,记住我们将它们嵌套在map方法中,这样我们就可以为每组结果挑选出第一个值。
4) 运行地图缩小:
db.test.mapReduce(map, reduce, {finalize: final, out: { inline: 1 }});
备选方案-使用聚合框架:
db.test.aggregate({
$group: {
_id: "$user_id",
ratings: {$addToSet: {business_id: "$business_id", stars: "$stars"}}
}
});