Stefano Piovesan
Stefano Piovesan

Reputation: 1215

Meteorjs collection find with $where

I have a meteorjs application with a collection with a ticker field.
I need the $where statement because I want to compare two fields in the same collection:

Tickers.find({$where: function() { return (this.price < this.value); }})

It didn't worked and I did a simpler test with $where.
On server side, when I run this query:

var t = Tickers.find({ticker:'AAPL'});

t contains the right value: one item with 'AAPL' ticker value. When I use:

t = Tickers.find({$where: function() { return (this.ticker === 'AAPL'); }});

t contains all the items in the collection.
The same $where query instead on client side works, but I don't want to publish the (huge) collection to do the query on client side.

Upvotes: 2

Views: 388

Answers (1)

chridam
chridam

Reputation: 103305

You need to pass the $where function as a string, not as an actual function. This is needed for Mongo to process and run the function (it is not being done in Node.js). Follow this forum thread for more on this.

Thus change your query to

Tickers.find({"$where": "function() { return (this.price < this.value); }"})

or simply pass in the string comparison

Tickers.find("this.price < this.value");

However, bear in mind that this won't perfom very well since using the $where operator calls the JavaScript engine to evaluate Javascript code on every document and checks the condition for each. It is advisable to combine with indexed queries if you can so that the query may be faster.

Some considerations you have to look at when using $where:

Do not use global variables.

$where evaluates JavaScript and cannot take advantage of indexes. Therefore, query performance improves when you express your query using the standard MongoDB operators (e.g., $gt, $in). In general, you should use $where only when you can’t express your query using another operator. If you must use $where, try to include at least one other standard query operator to filter the result set. Using $where alone requires a table scan. Using normal non-$where query statements provides the following performance advantages:

MongoDB will evaluate non-$where components of query before $where statements. If the non-$where statements match no documents, MongoDB will not perform any query evaluation using $where. The non-$where query statements may use an index.

Another suggestion that avoids using the $where operator would be to create an additional computed denormalized field say price_difference that is the difference between price and value (value - price) which you can then query as:

Tickers.find({ "price_difference": { "$gt": 0 }});

But still, such low-selectivity fields don't yield good index performance if the collection is very large thus the candidate set for indexing is large with this approach.

Upvotes: 3

Related Questions