Javascript作用域规则和mongo map / reduce函数

时间:2022-03-20 04:52:04

I would like to make some javascript functions that perform parametric map/reduce jobs in mongo, but I'm getting confused about the scoping of JavaScript. For example, the following code gives me counts of the "gender" variable; i.e. it will tell me how many "male" and "female" records I have:

我想制作一些在mongo中执行参数化map / reduce作业的javascript函数,但我对JavaScript的范围很困惑。例如,以下代码给出了“性别”变量的计数;即它会告诉我有多少“男性”和“女性”记录:

// count categories
db.responses.mapReduce(
    function(){
        emit(this["gender"], {count: 1})
    }, function(state, values){
        var result = {count: 0};
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    }, {out: { inline : 1}}
);

This works perfectly fine. In the next step I would like to create a function that does this for an arbitrary property

这完全没问题。在下一步中,我想创建一个为任意属性执行此操作的函数

function countCategories(item) { 
    function mapper(it){
        fn = function(){
            if(this[it]){
                emit(this[it], {count: 1});
            }
        };
        return fn;
    }(item); 
    var reducer = function(state, values){
        var result = {count: 0};
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    };
    var out = {out: { inline : 1}};
    var results = db.responses.mapReduce(
        mapper, 
        reducer, 
        out
    );
    return results;
}   

countCategories("gender")

However when I try:

但是,当我尝试:

countCategories("gender")
{
    "results" : [ ],
    "timeMillis" : 48,
    "counts" : {
        "input" : 2462,
        "emit" : 0,
        "reduce" : 0,
        "output" : 0
    },
    "ok" : 1,
}

The emit function has never been called. What has gone wrong here? My guess is something with the scoping of the emit function that mongo supplies, but I'm not quite sure why it is not being called, nor throwing an error.

从未调用emit函数。这里出了什么问题?我的猜测是mongo提供的emit函数的范围,但我不确定为什么它没有被调用,也没有抛出错误。

2 个解决方案

#1


2  

From what I read in the docs, the scope of functions to be used in database command is not their default javascript scope, but can (and must, if needed) be set manually. So, it think this should work:

从我在文档中看到的内容,数据库命令中使用的函数范围不是它们的默认javascript范围,但可以(并且必须,如果需要)手动设置。所以,它认为这应该工作:

var mapper = function(){
    if(item in this){
        emit(this[item], {count: 1});
    }
};
...
db.responses.mapReduce(
    mapper, 
    reducer, 
    {
       out: {"inline": 1},
       scope: {"item": item}
    }
);

#2


0  

I think you have a problem in the declaration of mapper:

我认为你在mapper的声明中有问题:

Your code:

你的代码:

function mapper(it){
    fn = function(){
        if(this[it]){
            emit(this[it], {count: 1});
        }
    };
    return fn;
}(item); 

Is equivalent, due to the JavaScript syntax/hoisting/semicolon insertion stuff, to:

由于JavaScript语法/提升/分号插入内容,等效于:

function mapper(it){
    fn = function(){
        if(this[it]){
            emit(this[it], {count: 1});
        }
    };
    return fn;
}

item; 

Your function statement declares a function that you want to execute immediately, but you don't.

您的函数语句声明了一个您想要立即执行的函数,但您没有。

And item; on it's own like that is perfectly ok JavaScript. It doesn't do anything, but it's valid. (Like "use strict";)

和项目;就像它自己就是完全可以的JavaScript。它没有做任何事情,但它是有效的。 (比如“使用严格”;)

What you want is a function expression :

你想要的是一个函数表达式:

var mapper = (function (it){
    fn = function(){
        if(this[it]){
            emit(this[it], {count: 1});
        }
    };
    return fn;
})(item); 

The important part here is not having function at the start of line by itself. The () around the function expression aren't required, but it indicates that you execute the function immediately. They would be required if you didn't assign to a variable; to avoid starting a line with function.

这里的重要部分是在行的开头没有功能。函数表达式周围的()不是必需的,但它表示您立即执行该函数。如果你没有分配变量,它们将是必需的;避免用函数启动一行。

#1


2  

From what I read in the docs, the scope of functions to be used in database command is not their default javascript scope, but can (and must, if needed) be set manually. So, it think this should work:

从我在文档中看到的内容,数据库命令中使用的函数范围不是它们的默认javascript范围,但可以(并且必须,如果需要)手动设置。所以,它认为这应该工作:

var mapper = function(){
    if(item in this){
        emit(this[item], {count: 1});
    }
};
...
db.responses.mapReduce(
    mapper, 
    reducer, 
    {
       out: {"inline": 1},
       scope: {"item": item}
    }
);

#2


0  

I think you have a problem in the declaration of mapper:

我认为你在mapper的声明中有问题:

Your code:

你的代码:

function mapper(it){
    fn = function(){
        if(this[it]){
            emit(this[it], {count: 1});
        }
    };
    return fn;
}(item); 

Is equivalent, due to the JavaScript syntax/hoisting/semicolon insertion stuff, to:

由于JavaScript语法/提升/分号插入内容,等效于:

function mapper(it){
    fn = function(){
        if(this[it]){
            emit(this[it], {count: 1});
        }
    };
    return fn;
}

item; 

Your function statement declares a function that you want to execute immediately, but you don't.

您的函数语句声明了一个您想要立即执行的函数,但您没有。

And item; on it's own like that is perfectly ok JavaScript. It doesn't do anything, but it's valid. (Like "use strict";)

和项目;就像它自己就是完全可以的JavaScript。它没有做任何事情,但它是有效的。 (比如“使用严格”;)

What you want is a function expression :

你想要的是一个函数表达式:

var mapper = (function (it){
    fn = function(){
        if(this[it]){
            emit(this[it], {count: 1});
        }
    };
    return fn;
})(item); 

The important part here is not having function at the start of line by itself. The () around the function expression aren't required, but it indicates that you execute the function immediately. They would be required if you didn't assign to a variable; to avoid starting a line with function.

这里的重要部分是在行的开头没有功能。函数表达式周围的()不是必需的,但它表示您立即执行该函数。如果你没有分配变量,它们将是必需的;避免用函数启动一行。