我有一个数据结构,可以跟踪不同城市的人:
//in db.persons
{
name: "John",
city: "Seattle
},
{
name: "Bill",
city: "Portland"
}
我想运行一个地图缩减来获得每个城市有多少人的列表,所以结果会是这样的:
{
_id: "Seattle",
value: 10
}
我的地图缩减功能如下:
map = function(){
var city = this.city
emit(city, 1);
};
reduce = function(key, values){
var result = 0;
values.forEach(function(value){
result += 1;
});
return result;
}
非常简单的东西,我想它会把city
作为一个键,然后为它找到的每个匹配城市的结果加一个。但是,在生成的贴图reduce上,该值被很大一个因素禁用。将我的reduce功能切换为:
reduce = function(key, values){
var result = 0;
values.forEach(function(value){
result += value;
});
return result;
}
将value
添加到结果(应该是1,正如我从emit
函数中理解的那样)返回了正确的结果。
为什么结果不同?在reduce函数中,我的value
不是1吗?
之所以会发生这种情况,是因为MongoDB可以为同一个键多次调用reduce函数。下面是一个简单的例子:
假设您的数据库中只有三个文档,每个文档的"城市"都是"西雅图"。在发射阶段之后,你会有一组发射的物体,看起来像
{'Seattle' : 1}. {'Seattle' : 1}. {'Seattle' : 1}
在发射阶段完成之后,还原阶段开始。在最简单的情况下,reduce函数将被调用为reduce('Seattle', [1,1,1])
。在这种情况下,您的第一个函数将正常工作。然而,reduce函数可能被调用多次:
reduce('Seattle', [1,1]) -> {'Seattle' : 2}, {'Seattle', 1}
reduce('Seattle', [2,1])
在这种情况下,第一个reduce函数将在第二次reduce调用后返回2
,因为值列表中有两项。在第二个reduce函数中,您可以正确地将值加在一起,而不仅仅是计算它们,这会给出正确的答案。
我个人认为,CouchDB文档对这一点的解释稍微好一点,说明了为什么需要为其值数组输入交换和关联的reduce函数。