Devin Dixon
Devin Dixon

Reputation: 12363

Calling db.system.js Function in $where

As a basic example, I have this function saved:

db.system.js.save({_id: "sum", value: function (x, y) { return x + y; }});

But when I try to called in the $where I get a Reference not exist.

<?php

$collection = $link -> selectCollection('foo' ,'bar');
$collection -> find(array(
'$where' =>'sum(this.x, this.y)'
));

?>

And the error

ReferenceError: sum is not defined :

Should I save it or access it another way?

Upvotes: 1

Views: 899

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151092

Are you connecting to the same database namespace in your PHP code that you connected to using the shell? I think not!

Anyway you misunderstand the concept of $where in this context, since you can only evaluate and not return modified results other than the data already contained in the collection.

The only things that can actually return something different than the existing documents are .mapReduce() and .aggregate().

So to demonstrate, in the "same namespace" if you define a collection:

db.djunk.insert({ "x": 1, "y": 2 })

And then run a .mapReduce()

db.dbjunk.mapReduce(
    function() {
        emit(this._id, sum(this.x,this.y))
    },
    function() {}, // does nothing here where already unique
    { "out": { "inline": 1 } }
)

That would return an actual summed result:

{
    "_id" : ObjectId("571a9bb951d042a7a896fd92"),
    "value" : 3
}

All that $where can do, is "logically" select the document:

db.djunk.find(function() {
    return sum(this.x,this.y) > 3
})

Which would not meet the condition.

But of course you really don't need to do this, and should generally be avoiding any server execution of JavaScript where possible. It's much slower than native operators, and you can do quite a bit with native operators.

So instead of .mapReduce() call .aggregate():

db.djunk.aggregate([
    { "$project": {
        "total": { "$add": [ "$x", "$y" ] }
    }}
])

And instead of JavaScript evaluation, call .aggregate() again, with $redact for "logical" filtering:

db.djunk.aggregate([
    { "$redact": {
        "$cond": {
            "if": { "$gt": [ { "$add": [ "$x", "$y" ] }, 3 ] },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }      
    }}
])

So in most cases there is always a better alternative to using JavaScript evaluation. And certainly there are actually very few cases to really need server stored functions in the cases where JavaScript evaluation is actually required.

But your basic error here will be because the function was in a different namespace, or indeed that you restarted the server in between. But the overall point is you probably should not be using stored functions anyway.

Upvotes: 1

Related Questions