如何在CouchDB中将对象属性从reduce合并到rereduce函数

时间:2012-06-26 13:07:02

标签: javascript views couchdb reduce

这是我的JSON架构:

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

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

我想做的是找到每个活跃标题的平均工资。活动标题是“from_date”属性设置为“9999-01-01”

的文档

这是我的地图功能

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]);
        }
    }
}

以下是生成的键值对:

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

现在,我想将它缩减为单个键值对,输出的值被设置为像这样的javascript对象

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

这是我计划如何做到的:首先,在reduce步骤中,我将返回合并来自同一emp_no的属性的对象。然后,在reduce步骤中,我将创建一个新对象,该对象具有基于之前减少的值的属性名称。

很难解释,所以这是我的reduce函数:

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;
}

此处的结果值是减少的值,这是我所期望的: http://i.imgur.com/SnlOU.png

但是,当我在蒲团中将分组值设置为“无”时,这不是我想要的:

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

有人可以帮我解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

你在这里推动CouchDB非常接近它的极限 - 使用reduce函数来执行连接和所有事情。

您的问题来自于CouchDB可能会应用零个,一个或多个重新减少步骤这一事实,但您的代码假定将执行一个重新减少步骤。我怀疑你得到的null结果来自最终的rereduce步骤适用于来自reduce步骤的一些结果以及来自rereduce步骤的一些结果。

这是一张小图。 M是映射步骤,R是缩减步骤,RR是重新缩减步骤。

[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步骤输出的数据必须与rereduce步骤输出的数据格式相同。特别是,这意味着您需要存储(求和,计数)对,而不是存储平均值。

答案 1 :(得分:0)

如果您可以将标题和薪水放在同一份员工文件中,这将使您的生活更轻松。

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

然后您可以使用内置的emit(doc.title, doc.salary) reduce函数轻松_stats并获取每个标题的工资统计信息。