diff --git a/packages/minimongo/minimongo_tests.js b/packages/minimongo/minimongo_tests.js index 07475e5c94..5f19e703e0 100644 --- a/packages/minimongo/minimongo_tests.js +++ b/packages/minimongo/minimongo_tests.js @@ -427,6 +427,9 @@ Tinytest.add("minimongo - selector_compiler", function (test) { nomatch({a: {$ne: 1}}, {a: [1, 2]}); nomatch({a: {$ne: 2}}, {a: [1, 2]}); match({a: {$ne: 3}}, {a: [1, 2]}); + nomatch({'a.b': {$ne: 1}}, {a: [{b: 1}, {b: 2}]}); + nomatch({'a.b': {$ne: 2}}, {a: [{b: 1}, {b: 2}]}); + match({'a.b': {$ne: 3}}, {a: [{b: 1}, {b: 2}]}); nomatch({a: {$ne: {x: 1}}}, {a: {x: 1}}); match({a: {$ne: {x: 1}}}, {a: {x: 2}}); @@ -456,7 +459,9 @@ Tinytest.add("minimongo - selector_compiler", function (test) { nomatch({a: {$nin: [1, 2, 3]}}, {a: [2]}); // tested against mongodb nomatch({a: {$nin: [{x: 1}, {x: 2}, {x: 3}]}}, {a: [{x: 2}]}); nomatch({a: {$nin: [1, 2, 3]}}, {a: [4, 2]}); + nomatch({'a.b': {$nin: [1, 2, 3]}}, {a: [{b:4}, {b:2}]}); match({a: {$nin: [1, 2, 3]}}, {a: [4]}); + match({'a.b': {$nin: [1, 2, 3]}}, {a: [{b:4}]}); // $size match({a: {$size: 0}}, {a: []}); @@ -564,7 +569,9 @@ Tinytest.add("minimongo - selector_compiler", function (test) { match({x: {$not: {$lt: 10, $gt: 7}}}, {x: 6}); match({x: {$not: {$gt: 7}}}, {x: [2, 3, 4]}); + match({'x.y': {$not: {$gt: 7}}}, {x: [{y:2}, {y:3}, {y:4}]}); nomatch({x: {$not: {$gt: 7}}}, {x: [2, 3, 4, 10]}); + nomatch({'x.y': {$not: {$gt: 7}}}, {x: [{y:2}, {y:3}, {y:4}, {y:10}]}); match({x: {$not: /a/}}, {x: "dog"}); nomatch({x: {$not: /a/}}, {x: "cat"}); diff --git a/packages/minimongo/selector.js b/packages/minimongo/selector.js index 7cf8fa0a5d..057e0d9195 100644 --- a/packages/minimongo/selector.js +++ b/packages/minimongo/selector.js @@ -551,9 +551,25 @@ var compileDocumentSelector = function (docSelector) { perKeySelectors.push(function (doc) { var branchValues = lookUpByIndex(doc); // We apply the selector to each "branched" value and return true if any - // match. This isn't 100% consistent with MongoDB; eg, see: - // https://jira.mongodb.org/browse/SERVER-8585 - return _.any(branchValues, valueSelectorFunc); + // match. However, for "negative" selectors like $ne or $not we actually + // require *all* elements to match. + // + // This is because {'x.tag': {$ne: "foo"}} applied to {x: [{tag: 'foo'}, + // {tag: 'bar'}]} should NOT match even though there is a branch that + // matches. (This matches the fact that $ne uses a negated + // _anyIfArrayPlus, for when the last level of the key is the array, + // which deMorgans into an 'all'.) + // + // XXX This isn't 100% consistent with MongoDB in 'null' cases: + // https://jira.mongodb.org/browse/SERVER-8585 + // XXX this still isn't right. consider {a: {$ne: 5, $gt: 6}}. the + // $ne needs to use the "all" logic and the $gt needs the "any" + // logic + var combiner = (subSelector && + (subSelector.$not || subSelector.$ne || + subSelector.$nin)) + ? _.all : _.any; + return combiner(branchValues, valueSelectorFunc); }); } });