1

This is my JSON schema:

{"emp_no": ..,
"salary": ..,
"from_date": ..,
"to_date": ..,
"type" : "salaries"}

{"emp_no": ..,
"title": ..,
"from_date": ..,
"to_date" : ..,
"type" : "titles"}

What i wanted to do, is to find the average salary for each active title. Active titles are document with "from_date" attribute set to "9999-01-01"

Here is my Map Function

function(doc) {
if (doc.type == 'salaries') {
        var dateSalaries = null;
        dateSalaries = doc.to_date.split("-");
        if(dateSalaries[0].localeCompare("9999") == 0){
            emit(doc.emp_no, ["salary", doc.salary] );
        }
    } else if (doc.type == 'titles') {
        var dateTitles = null;
        dateTitles = doc.to_date.split("-");
        if(dateTitles[0].localeCompare("9999") == 0){
            emit(doc.emp_no, ["title", doc.title]);
        }
    }
}

Here is the resulting key value pairs emited:

http://i.imgur.com/o1Qxz.png

Now, i want to reduce it into single key-value pair, with the value outputted is set into javascript object like this

{
    "engineer" : 64342,
    "senior engineer" : 123111,
    "staff" : ...,
    "senior staf" : ...,
    .
    .
    .
}

Here's how i planned to do it: First, in reduce step, i'm gonna return object that merge properties from the same emp_no. Then, in reduce step, i'm gonna create a new object that has properties name based on reduced value before.

It's hard to explain, so here is my reduce function:

function(keys, values, rereduce) {
    var i, l, attr, sal, rv = {};
    if (rereduce) {
        for (i = 0, l = values.length; i<l ; ++i) {
            if (values[i].hasOwnProperty('salary')) {
                attr = values[i].title;
                sal = values[i].salary;
                if (rv[attr] instanceof Array) {
                    rv[attr].push(sal);
                } else{
                    rv[attr] = [];
                    rv[attr].push(sal);
                }
            }           
        }

        for (var x in rv) {
            if (rv.hasOwnProperty(x)) {
                var totalSalary = 0;
                for (i = 0, l = values.length; i<l ; i++) {
                    totalSalary += rv[x][i];
                }
                rv[x] = totalSalary / rv[x].length;
            }
        }

    } else {
        for (i = 0, l = values.length; i<l ; i++) {
            switch (values[i][0]) {
                case "title" : rv["title"] = values[i][1]; break;
                case "salary": rv["salary"] = values[i][1]; break;
            }
        }
    }
    return rv;
}

The resulting value here is reduced value, which is what i expected: http://i.imgur.com/SnlOU.png

But, when i set the grouping value to 'none' in futon, it's not what i wanted:

{Senior Engineer: null, Assistant Engineer: null, Technique Leader: null}

Could someone help me to solves this?

4

2 回答 2

2

您正在将 CouchDB 推到非常接近它的极限 — 使用 reduce 函数来执行连接和所有操作。

您的问题来自这样一个事实,即 CouchDB 可能应用零个、一个或多个 rereduce 步骤,但您的代码假定将执行一个 rereduce 步骤。我怀疑null你得到的结果来自这样一个事实,即最后的 rereduce 步骤应用于一些来自 reduce 步骤的结果和一些来自 rereduce 步骤的结果。

这是一个小图。M是map step,R是reduce step,RR是rereduce step。

[X] [X] [X] [X] [X] [X] [X] [X] [X] [X] 
 |   |   |   |   |   |   |   |   |   | 
(M) (M) (M) (M) (M) (M) (M) (M) (M) (M)
 |   |   |   |   |   |   |   |   |   | 
(==R==) (==R==) (==R==) (==R==) (==R==)
   |       |       |       |       | 
  (== R R ==)     (== R R ==)      | 
       |               |           | 
      (====== R R ======)          | 
               |                   | 
              (======== R R ========)
                         |
                         v
                        [X]

对于 CouchDB reduce 视图,reduce 步骤输出的数据必须与 rereduce 步骤输出的数据具有相同的格式。特别是,这意味着您需要存储 (sum,count) 对,而不是存储平均值。

于 2012-06-26T14:14:10.220 回答
0

如果您可以将头衔和薪水放在同一个员工文档中,那会让您的生活变得更轻松。

{
"name" : "Joe",
"title" : "Plumber",
"salary" : 60000
}

然后您可以轻松地emit(doc.title, doc.salary)使用内置的_statsreduce 功能并查看每个职位的薪水统计信息。

于 2012-06-27T00:25:25.487 回答