我有一个包含字符串数组的数据库。示例表:
name | words | ...
Ash | ["Apple", "Pear", "Plum"] | ...
Joe | ["Walnut", "Peanut"] | ...
Max | ["Pineapple", "Apple", "Plum"] | ...
现在我想将此表与给定的单词数组相匹配,并按照匹配率对文档进行分组。
具有预期结果的示例输入:
// matched for input = ["Walnut", "Peanut", "Apple"]
{
"1.00": [{name:"Joe", match:"1.00"}],
"0.33": [{name:"Ash", match:"0.33"}, {name:"Max", match:"0.33"}]
}
我正在使用以下map
函数发出匹配率为关键字的文档:
function map() {
var matches = 0.0;
for(var i in input)
if(this.words.indexOf(input[i]) !== -1) matches+=1;
matches /= input.length;
var key = ""+matches.toFixed(2);
emit(key, {name: this.name, match: key});
}
现在缺少一个匹配的reduce
函数,用于将发出的KV对组合到结果对象中。
我试过这样的话:
function reduce(key, value) {
var res = {};
res[key] = values;
return res;
}
但是我的规范
有问题MongoDB可以多次调用reduce函数 键。在这种情况下,从reduce函数的前一个输出 该键将成为下一个reduce的输入值之一 该键的函数调用。
...导致嵌套的结果对象。通过匹配对文档进行分组的正确方法是什么?
答案 0 :(得分:1)
对同一个键多次调用reduce函数。
那是idempotence,而reduce函数必须尊重它。
但是,为了简单起见,你只需要确保地图输出的格式与reduce格式相同。
对于你的情况,这样的事情会起作用:
db.col.insert({"name": "Ash", "words": ["Apple", "Pear", "Plum"]})
db.col.insert({"name": "Joe", "words": ["Walnut", "Peanut"]})
db.col.insert({"name": "Max", "words": ["Pineapple", "Apple", "Plum"]})
function map() {
input = ["Walnut", "Peanut", "Apple"]
var matches = 0.0;
for(var i in input)
if(this.words.indexOf(input[i]) !== -1) matches+=1;
matches /= input.length;
var key = ""+matches.toFixed(2);
emit(key, {users: [{name: this.name, match: key}]});
}
function reduce(key, value) {
ret = value[0]
for(var i=1; i<value.length; i++){
ret.users = ret.users.concat(value[i].users)
}
return ret
}
db.col.mapReduce(map, reduce, {"out": {inline:1}})
输出:
{
"results" : [
{
"_id" : "0.33",
"value" : {
"users" : [
{
"name" : "Ash",
"match" : "0.33"
},
{
"name" : "Max",
"match" : "0.33"
}
]
}
},
{
"_id" : "0.67",
"value" : {
"users" : [
{
"name" : "Joe",
"match" : "0.67"
}
]
}
}
],
"timeMillis" : 22,
"counts" : {
"input" : 3,
"emit" : 3,
"reduce" : 1,
"output" : 2
},
"ok" : 1
}