From a2c0f15d6dc9fcda389b18b54287c4fcb5be44cd Mon Sep 17 00:00:00 2001 From: James Wahlin Date: Wed, 30 Jan 2019 16:33:34 -0500 Subject: SERVER-13779 Allow $not to be applied to $regex --- jstests/core/not2.js | 81 +++++++++++++++--------------- src/mongo/db/matcher/expression_parser.cpp | 6 --- 2 files changed, 41 insertions(+), 46 deletions(-) diff --git a/jstests/core/not2.js b/jstests/core/not2.js index 21e8d9aa9c4..610d79c4d8f 100644 --- a/jstests/core/not2.js +++ b/jstests/core/not2.js @@ -6,14 +6,12 @@ const coll = db.jstests_not2; coll.drop(); - function check(query, expected, size) { - if (size === undefined) { - size = 1; - } - assert.eq(size, coll.find(query).itcount(), tojson(query)); - if (size > 0) { - const cursor = coll.find(query).sort({i: 1}); - assert.eq(expected, cursor.toArray()[0].i, tojson(query)); + function check(query, expected) { + const resultList = coll.find(query).sort({i: 1}).toArray(); + assert.eq(expected.length, resultList.length, query); + + for (let x = 0; x < expected.length; ++x) { + assert.eq(expected[x], resultList[x].i, query); } } @@ -30,53 +28,56 @@ // TODO SERVER-12735: We currently do not handle double negatives during query // canonicalization. fail({i: {$not: {$not: "a"}}}); - check({i: {$not: {$not: {$gt: "a"}}}}, "b"); + check({i: {$not: {$not: {$gt: "a"}}}}, ["b"]); fail({i: {$not: "a"}}); fail({i: {$not: {$ref: "foo"}}}); fail({i: {$not: {}}}); - check({i: {$gt: "a"}}, "b"); - check({i: {$not: {$gt: "a"}}}, "a"); - check({i: {$not: {$ne: "a"}}}, "a"); - check({i: {$not: {$gte: "b"}}}, "a"); - check({i: {$exists: true}}, "a", 2); - check({i: {$not: {$exists: true}}}, "", 0); - check({j: {$not: {$exists: false}}}, "", 0); - check({j: {$not: {$exists: true}}}, "a", 2); - check({i: {$not: {$in: ["a"]}}}, "b"); - check({i: {$not: {$in: ["a", "b"]}}}, "", 0); - check({i: {$not: {$in: ["g"]}}}, "a", 2); - check({i: {$not: {$nin: ["a"]}}}, "a"); - check({i: {$not: /a/}}, "b"); - check({i: {$not: /(a|b)/}}, "", 0); - check({i: {$not: /a/, $regex: "a"}}, "", 0); - check({i: {$not: /aa/}}, "a", 2); - fail({i: {$not: {$regex: "a"}}}); + check({i: {$gt: "a"}}, ["b"]); + check({i: {$not: {$gt: "a"}}}, ["a"]); + check({i: {$not: {$ne: "a"}}}, ["a"]); + check({i: {$not: {$gte: "b"}}}, ["a"]); + check({i: {$exists: true}}, ["a", "b"]); + check({i: {$not: {$exists: true}}}, []); + check({j: {$not: {$exists: false}}}, []); + check({j: {$not: {$exists: true}}}, ["a", "b"]); + check({i: {$not: {$in: ["a"]}}}, ["b"]); + check({i: {$not: {$in: ["a", "b"]}}}, []); + check({i: {$not: {$in: ["g"]}}}, ["a", "b"]); + check({i: {$not: {$nin: ["a"]}}}, ["a"]); + check({i: {$not: /a/}}, ["b"]); + check({i: {$not: /(a|b)/}}, []); + check({i: {$not: /a/, $regex: "a"}}, []); + check({i: {$not: /aa/}}, ["a", "b"]); + check({i: {$not: {$regex: "a"}}}, ["b"]); + check({i: {$not: {$regex: "A", $options: "i"}}}, ["b"]); + check({i: {$not: {$regex: "[ab]"}}}, []); + check({i: {$not: {$regex: "^foo"}}}, ["a", "b"]); fail({i: {$not: {$options: "a"}}}); - check({i: {$type: 2}}, "a", 2); - check({i: {$not: {$type: 1}}}, "a", 2); - check({i: {$not: {$type: 2}}}, "", 0); + check({i: {$type: 2}}, ["a", "b"]); + check({i: {$not: {$type: 1}}}, ["a", "b"]); + check({i: {$not: {$type: 2}}}, []); assert.writeOK(coll.remove({})); assert.writeOK(coll.insert({i: 1})); - check({i: {$not: {$mod: [5, 1]}}}, null, 0); - check({i: {$mod: [5, 2]}}, null, 0); - check({i: {$not: {$mod: [5, 2]}}}, 1, 1); + check({i: {$not: {$mod: [5, 1]}}}, []); + check({i: {$mod: [5, 2]}}, []); + check({i: {$not: {$mod: [5, 2]}}}, [1]); assert.writeOK(coll.remove({})); assert.writeOK(coll.insert({i: ["a", "b"]})); - check({i: {$not: {$size: 2}}}, null, 0); - check({i: {$not: {$size: 3}}}, ["a", "b"]); - check({i: {$not: {$gt: "a"}}}, null, 0); - check({i: {$not: {$gt: "c"}}}, ["a", "b"]); - check({i: {$not: {$all: ["a", "b"]}}}, null, 0); - check({i: {$not: {$all: ["c"]}}}, ["a", "b"]); + check({i: {$not: {$size: 2}}}, []); + check({i: {$not: {$size: 3}}}, [["a", "b"]]); + check({i: {$not: {$gt: "a"}}}, []); + check({i: {$not: {$gt: "c"}}}, [["a", "b"]]); + check({i: {$not: {$all: ["a", "b"]}}}, []); + check({i: {$not: {$all: ["c"]}}}, [["a", "b"]]); assert.writeOK(coll.remove({})); assert.writeOK(coll.insert({i: [{j: "a"}]})); assert.writeOK(coll.insert({i: [{j: "b"}]})); - check({i: {$not: {$elemMatch: {j: "a"}}}}, [{j: "b"}]); - check({i: {$not: {$elemMatch: {j: "f"}}}}, [{j: "a"}], 2); + check({i: {$not: {$elemMatch: {j: "a"}}}}, [[{j: "b"}]]); + check({i: {$not: {$elemMatch: {j: "f"}}}}, [[{j: "a"}], [{j: "b"}]]); } // Run the test without any index. diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index 8a055626399..e75fa98d8e0 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -1372,12 +1372,6 @@ StatusWithMatchExpression parseNot(StringData name, return parseStatus; } - for (size_t i = 0; i < theAnd->numChildren(); i++) { - if (theAnd->getChild(i)->matchType() == MatchExpression::REGEX) { - return {ErrorCodes::BadValue, "$not cannot have a regex"}; - } - } - return {stdx::make_unique(theAnd.release())}; } -- cgit v1.2.1