问题
This is follow up from this question How to merge objects attributes from reduce to rereduce function in CouchDB
I've been following the accepted answer from the previous question. For quick review, this is my JSON schema:
{"emp_no": ..,
"salary": ..,
"from_date": ..,
"to_date": ..,
"type" : "salaries"}
{"emp_no": ..,
"title": ..,
"from_date": ..,
"to_date" : ..,
"type" : "titles"}
I want to find out the average salaries for each active titles (denoted by "from_date" = "9999-01-01"). Since the rereduce's output must be the same as the reduce one, the value will be in the form of
{"Title":[sum,count],"Title":[sum,count]}
Based on that form, i changed the reduce function from the previous question into this:
function(keys, values, rereduce) {
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}
var i, l, sal, count, rv = {}, kobj;
if (rereduce) {
for (i = 0, l = values.length; i<l ; ++i) {
if (i === 0) {
for (kobj in values[i]) {
if(values[i].hasOwnProperty(kobj)) {
if (isArray(values[i][kobj]) ) {
rv[kobj] = values[i][kobj];
}
}
}
} else {
for (kobj in values[i]) {
if(values[i].hasOwnProperty(kobj)) {
if (isArray(values[i][kobj])) {
sal = values[i][kobj][0];
count = values[i][kobj][1];
if ( rv.hasOwnProperty(kobj) && isArray(rv[kobj])) {
rv[kobj][0] += sal;
rv[kobj][1] += count;
} else {
rv[kobj] = [sal,count];
}
}
}
}
}
}
} else {
var t;
for (i = 0, l = values.length; i<l ; i++) {
switch (values[i][0]) {
case "title" :
rv[values[i][1]] = null;
t = i;
break;
case "salary":
rv[values[t][1]] = [values[i][1], 1];
break;
default:
rv[values[t][1]] = null;
break;
}
}
}
return rv;
}
Here is the reduced value : http://i.imgur.com/WbmxL.png
And here is the value when rereduce step is applied :
{Senior Engineer: [417153, 6], Engineer: [171293, 3], Assistant Engineer: [66313, 1], Senior Staff: [248397, 3], Technique Leader: [134222, 2], Staff: [72527, 1]}
What makes me puzzled, is when each time the view is created, the rereduced value is always change! Even though i just added comment (//) in the end of the function. Also, the value generated is always wrong. Here's the value after the view is recreated:
{Senior Engineer: [540708, 7], Senior Staff: [179364, 2], Engineer: [488026, 6], Staff: [174196, 3], Assistant Engineer: [66313, 1], Technique Leader: [120310, 2]}
I'm really confused, and pretty much blind since i don't know how to do debugging and follow the execution of MapReduce function in CouchDB. Could someone expert in javascript / MapReduce help me point out what's wrong?
Here's my salaries and titles document. Each one contain 1000 document that is ready to be inserted into CouchDB with HTTP BULK API https://docs.google.com/open?id=0B2o1vMJ7XFKydUxGR3R3NU9QOEE
回答1:
You're relying on adjacent records being present. You can't predict how Couch will split the set of values it passes to your reduce function. You will absolutely get called with the split happening between title and salary records for the same employee. Meaning that in a given invocation of your reduce function you will have a title but not the salary, or vice versa.
I don't think there's any way to do what you're trying to with your current data structure. You should do the average calculations on the client side, or change your document structure so that it stores both the salary and the title in the same document (the latter would be my approach).
来源:https://stackoverflow.com/questions/11231108/merging-objects-attribute-in-rereduce-function-resulted-in-wrong-value-each-tim