5

I have a MongoDB query that searches all properties for a value defined in the search variable. It works the following way:

 db.collection.findOne({
                $where: function() {
                    var search = 'searchstring';
                    for (var key in this) {
                        if (this[key] === search) {
                          return true;
                        }
                        return false;
                    }
                }
            });

However, I would like to define the search variable outside the query.

But when I do so, I get an error that it is not referenced (i.e. scoping issue):

"ReferenceError: search is not defined near '[key] === search

How can I use or pass the variable to the query filter?

7
  • And where would that variable be defined in regards to the query above? The only solution is to define the variable in scope ? Commented Dec 22, 2015 at 21:06
  • Can you explain the logic of that for loop first? Commented Dec 22, 2015 at 21:07
  • 1
    You cannot. The function gets executed at the server side. The variable you declare resides on the client. Commented Dec 22, 2015 at 21:23
  • @SoluableNonagon The for loop simply iterates over all properties at the top level of the collection and checks if the property value is equal to the searchstring. Commented Dec 22, 2015 at 21:46
  • @adeneo I would like to use it like that: var search = 'searchstring'; db.collection.findOne({ $where: function() { for (var key in this) { if (this[key] === search) { return true; } return false; } } }); Commented Dec 22, 2015 at 21:47

3 Answers 3

11

You can try something like this:

var searchstring = 'whatever';
var params = {};
params.$where = 'function() {' +
    'var search = "' + searchstring + '";' +
    'for (var key in this) {' +
        'if (this[key] === search) {' +
            'return true;' +
        '}' +
        'return false;' +
    '}' +
'}';
db.collection.findOne(params);

(Stringify your function and concat with external variable)

Worked for me via mongoose

Sign up to request clarification or add additional context in comments.

3 Comments

Injecting an opaque string in executable code, what could go wrong ?
Nothing wrong with this solution as long as you're in control of your variables or sanitising your input properly!
Brilliant solution, Aida
1

search is a variable that you define in your client, may be the shell, or any client API.

The function that you define for the $where clause, will not be executed on the client side, but on the mongodb server. so, when the function is being interpreted in the server side and it looks for the search variable, it was never defined on the server and hence you get the error.

In your case, you want the variable search, to be replaced with its content, by the client, before being executed on the server. And this is not possible, unless you build the function content itself in the client side.

The client never really interprets anything you write inside the anonymous function. The server does. The error you see is from the server. You can validate it by firing this query and looking onto the server logs:

2015-12-22T19:03:44.011-0800 I QUERY    [conn1] assertion 16722 ReferenceError:
searc is not defined
    at _funcs1 (_funcs1:1:39) near 's.key === searc){retu'  ns:test.t query:{ $w
here: function (){if(this.key === searc){return true}} }

There is a better way to write what you wish to achieve using the $exists operator.

var search = "title";
var find = {};
find[search] = {$exists:true};
db.collection.findOne(find);

This works, because, you build the query parameter fully on the client side, before passing it on to the findOne() method.

Comments

1

You can solve this problem using mongodb map reduce and scope functionality. Scope allows you to pass variables into map reduce job.

function map() {
    for (var key in this) {
        if (this[key] === search) {
            return emit(this._id, this);
        }
    }
}

function reduce(key, values) {
    return values[0];
}


db.collection.mapReduce(map, reduce, {
        out: {inline: 1},
        scope: {
            search: 'searchstring'
        }
    }
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.