diff --git a/lib/query.js b/lib/query.js index 83419e6d..b90d4351 100644 --- a/lib/query.js +++ b/lib/query.js @@ -20,10 +20,15 @@ Mingo.Query.prototype = { assert(isObject(this.__criteria), 'Criteria must be of type Object') + var whereOperator; + for (var field in this.__criteria) { if (has(this.__criteria, field)) { var expr = this.__criteria[field] - if (inArray(['$and', '$or', '$nor', '$where'], field)) { + // save $where operators to be executed after other operators + if ('$where' === field) { + whereOperator = {field: field, expr: expr}; + } else if (inArray(['$and', '$or', '$nor'], field)) { this._processOperator(field, field, expr) } else { // normalize expression @@ -35,6 +40,11 @@ Mingo.Query.prototype = { } } } + + if(whereOperator) { + this._processOperator(whereOperator.field, whereOperator.field, whereOperator.expr); + } + } }, diff --git a/mingo.js b/mingo.js index f06bfd32..37a9430b 100644 --- a/mingo.js +++ b/mingo.js @@ -1327,10 +1327,15 @@ Mingo.Query.prototype = { assert(isObject(this.__criteria), 'Criteria must be of type Object') + var whereOperator; + for (var field in this.__criteria) { if (has(this.__criteria, field)) { var expr = this.__criteria[field] - if (inArray(['$and', '$or', '$nor', '$where'], field)) { + // save $where operators to be executed after other operators + if ('$where' === field) { + whereOperator = {field: field, expr: expr}; + } else if (inArray(['$and', '$or', '$nor'], field)) { this._processOperator(field, field, expr) } else { // normalize expression @@ -1342,6 +1347,11 @@ Mingo.Query.prototype = { } } } + + if(whereOperator) { + this._processOperator(whereOperator.field, whereOperator.field, whereOperator.expr); + } + } }, diff --git a/test/collections.js b/test/collections.js index c8928a79..babd1a5d 100644 --- a/test/collections.js +++ b/test/collections.js @@ -63,3 +63,51 @@ test('Match $all with $elemMatch on nested elements', function (t) { var result = Mingo.find(data, criteria).count() t.ok(result === 1, 'can match using $all with $elemMatch on nested elements') }) + +test('Evaluate $where last', function (t) { + t.plan(2) + + var data = [ + { + user: { + username: 'User1', + projects: [ + {name: 'Project 1', rating: {complexity: 6}}, + {name: 'Project 2', rating: {complexity: 2}} + ], + color: 'green', + number: 42 + } + }, + { + user: { + username: 'User2', + projects: [ + {name: 'Project 1', rating: {complexity: 6}}, + {name: 'Project 2', rating: {complexity: 8}} + ] + } + } + ] + + var criteria = { + 'user.color': {$exists: true}, + 'user.number': {$exists: true}, + $where: 'this.user.color === "green" && this.user.number === 42' + } + // It should return one user object + var result = Mingo.find(data, criteria).count() + t.ok(result === 1, 'can safely reference properties on this using $where and $exists') + + criteria = { + 'user.color': {$exists: true}, + 'user.number': {$exists: true}, + $and: [ + { $where: 'this.user.color === "green"' }, + { $where: 'this.user.number === 42' } + ] + } + // It should return one user object + var result = Mingo.find(data, criteria).count() + t.ok(result === 1, 'can safely reference properties on this using multiple $where operators and $exists') +})