Flexible JavaScript functions (for MongoDB)

By : Josh K
Source: Stackoverflow.com
Question!

I'm working on some Map / Reduce queries for MongoDB and am trying to do the following (in a nutshell).

m = function()
{
    this.convert = function(val)
    {
        return val+1;
    }

    emit(convert(this.age), this.doesWearGlasses);
}

r = function(k, v)
{
    count = 0;
    for(var i = 0; i < v.length; i++)
    {
        count += v[i];
    }
    return count;
}

if(tuned)
{
    m.convert = function(val)
    {
        return val;
    }
}

/**
* Continue to running the M/R query
*/

It should be noted that I'm writing this as a Node.js application, but I assume most of the same principals apply to any JavaScript.

The issue is I don't think I can change it without creating an object, ala mTmp = new m();, however I can't because emit() (and everything else) isn't defined.

I tried using m.prototype.convert = function, but this doesn't work. The value of m.toString() doesn't change.

By : Josh K


Answers

Since you're calling the constructor after defining the prototype function, this.convert will overwrite the already-defined prototype function.

You could overwrite the entire m constructor:

m = function()
{
    this.convert = function(val)
    {
        return val 1;
    }
}

if(tuned) {
    m = (function(mOld) {
        return function()
        {
            mOld.apply(this, arguments);
            this.convert = function(val){ return val; }
        }
    })(m);
}

// Test code
function doStuff(obj) {
    alert(obj.toString());
}

var mObj = new m();
doStuff(mObj);

It should work, but I don't know what else it may affect in your actual script.

Note that it's using a self-invocation pattern, which creates a closure.

I've added test code that demonstrates that this works given the constraints in the OP.

By : palswim


I think that you’re trying to part of m (the map function) behave differently depending on whether tuned is set. Is that right?

Unfortunately, since both the map and reduce functions get passed to MongoDB as strings with just the bodies of the functions, they won’t have access to any outside code or data — so any properties you set on the function just won’t be there when it’s executed.

If I’m understanding you correctly, I think these are your three best options:

  1. Define a different version of m for each case.

  2. Assemble m as a string, plunking in the right chunk of code depending on the value of tuned.

  3. Make m decide how to behave based on a variable, like this:

    m = function()
    {
        var result;
        if (tuned) {
            result = this.age;
        } else {
            result = this.age   1;
        }
        emit(convert(this.age), this.doesWearGlasses);
    }
    

    …and give the tuned variable a second life inside MongoDB when you call mapReduce:

    collection.mapReduce(m, r, { scope: { tuned: tuned } });
    

    (FWIW, if the difference is, in fact, this simple, you could write m like this instead:)

    m = function()
    {
        emit(convert(tuned ? this.age : this.age   1), this.doesWearGlasses);
    }
    

…let me know if you mean something different.



This video can help you solving your question :)
By: admin