summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Wahlin <james.wahlin@10gen.com>2017-01-12 17:44:52 -0500
committerJames Wahlin <james.wahlin@10gen.com>2017-01-19 12:37:23 -0500
commit8a5f459539b651cccefc35ebe209e30af2b1b9f1 (patch)
tree830563ecb8347f25e9f8bcc66332f2ed9b1c5b1b
parent4e62cebd32bfc6b5f3283b111a9a79b25ca3d791 (diff)
downloadmongo-8a5f459539b651cccefc35ebe209e30af2b1b9f1.tar.gz
SERVER-27649 $elemMatch serialization $regex support
(cherry picked from commit 04b16905b7c3b6b3fce5fbc7f6ba17522311a7c4)
-rw-r--r--jstests/core/regex.js36
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp13
-rw-r--r--src/mongo/db/matcher/expression_leaf.h2
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp100
4 files changed, 135 insertions, 16 deletions
diff --git a/jstests/core/regex.js b/jstests/core/regex.js
index d6982678f97..e891ddf77fa 100644
--- a/jstests/core/regex.js
+++ b/jstests/core/regex.js
@@ -1,32 +1,56 @@
(function() {
'use strict';
- var t = db.jstests_regex;
+ const t = db.jstests_regex;
+
+ const isMaster = db.runCommand("ismaster");
+ assert.commandWorked(isMaster);
+ const isMongos = (isMaster.msg === "isdbgrid");
t.drop();
- t.save({a: "bcd"});
+ assert.writeOK(t.save({a: "bcd"}));
assert.eq(1, t.count({a: /b/}), "A");
assert.eq(1, t.count({a: /bc/}), "B");
assert.eq(1, t.count({a: /bcd/}), "C");
assert.eq(0, t.count({a: /bcde/}), "D");
t.drop();
- t.save({a: {b: "cde"}});
+ assert.writeOK(t.save({a: {b: "cde"}}));
assert.eq(1, t.count({'a.b': /de/}), "E");
t.drop();
- t.save({a: {b: ["cde"]}});
+ assert.writeOK(t.save({a: {b: ["cde"]}}));
assert.eq(1, t.count({'a.b': /de/}), "F");
t.drop();
- t.save({a: [{b: "cde"}]});
+ assert.writeOK(t.save({a: [{b: "cde"}]}));
assert.eq(1, t.count({'a.b': /de/}), "G");
t.drop();
- t.save({a: [{b: ["cde"]}]});
+ assert.writeOK(t.save({a: [{b: ["cde"]}]}));
assert.eq(1, t.count({'a.b': /de/}), "H");
+ //
+ // Confirm match and explain serialization for $elemMatch with $regex.
+ //
+ t.drop();
+ assert.writeOK(t.insert({x: ["abc"]}));
+
+ const query = {x: {$elemMatch: {$regex: 'ABC', $options: 'i'}}};
+ assert.eq(1, t.count(query));
+
+ const result = t.find(query).explain();
+ assert.commandWorked(result);
+
+ if (!isMongos) {
+ assert(result.hasOwnProperty("queryPlanner"));
+ assert(result.queryPlanner.hasOwnProperty("parsedQuery"), tojson(result));
+ assert.eq(result.queryPlanner.parsedQuery, query);
+ }
+
+ //
// Disallow embedded null bytes when using $regex syntax.
+ //
t.drop();
assert.throws(function() {
t.find({a: {$regex: "a\0b", $options: "i"}}).itcount();
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index 90b0cc985dc..d562bff141e 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -319,6 +319,17 @@ void RegexMatchExpression::debugString(StringBuilder& debug, int level) const {
}
void RegexMatchExpression::serialize(BSONObjBuilder* out) const {
+ BSONObjBuilder regexBuilder(out->subobjStart(path()));
+ regexBuilder.append("$regex", _regex);
+
+ if (!_flags.empty()) {
+ regexBuilder.append("$options", _flags);
+ }
+
+ regexBuilder.doneFast();
+}
+
+void RegexMatchExpression::serializeToBSONTypeRegex(BSONObjBuilder* out) const {
out->appendRegex(path(), _regex, _flags);
}
@@ -596,7 +607,7 @@ void InMatchExpression::serialize(BSONObjBuilder* out) const {
}
for (auto&& _regex : _regexes) {
BSONObjBuilder regexBob;
- _regex->serialize(&regexBob);
+ _regex->serializeToBSONTypeRegex(&regexBob);
arrBob.append(regexBob.obj().firstElement());
}
arrBob.doneFast();
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 26568b20b81..128d3070737 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -247,6 +247,8 @@ public:
virtual void serialize(BSONObjBuilder* out) const;
+ void serializeToBSONTypeRegex(BSONObjBuilder* out) const;
+
void shortDebugString(StringBuilder& debug) const;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index 572ae25d585..13a2aaa172d 100644
--- a/src/mongo/db/matcher/expression_serialization_test.cpp
+++ b/src/mongo/db/matcher/expression_serialization_test.cpp
@@ -166,6 +166,28 @@ TEST(SerializeBasic, ExpressionElemMatchValueSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
+TEST(SerializeBasic, ExpressionElemMatchValueWithRegexSerializesCorrectly) {
+ const CollatorInterface* collator = nullptr;
+ const auto match = BSON("x" << BSON("$elemMatch" << BSON("$regex"
+ << "abc"
+ << "$options"
+ << "i")));
+ Matcher original(match, ExtensionsCallbackNoop(), collator);
+ Matcher reserialized(
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), match);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
+
+ BSONObj obj = fromjson("{x: ['abc', 'xyz']}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+
+ obj = fromjson("{x: ['ABC', 'XYZ']}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+
+ obj = fromjson("{x: ['def', 'xyz']}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+}
+
TEST(SerializeBasic, ExpressionElemMatchValueWithEmptyStringSerializesCorrectly) {
const CollatorInterface* collator = nullptr;
Matcher original(
@@ -233,7 +255,12 @@ TEST(SerializeBasic, ExpressionAllWithRegex) {
fromjson("{x: {$all: [/a.b.c/, /.d.e./]}}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: /a.b.c/}, {x: /.d.e./}]}"));
+
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("$and" << BSON_ARRAY(BSON("x" << BSON("$regex"
+ << "a.b.c"))
+ << BSON("x" << BSON("$regex"
+ << ".d.e.")))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abcde'}");
@@ -276,6 +303,26 @@ TEST(SerializeBasic, ExpressionNeSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
+TEST(SerializeBasic, ExpressionNeWithRegexObjectSerializesCorrectly) {
+ const CollatorInterface* collator = nullptr;
+ Matcher original(BSON("x" << BSON("$ne" << BSON("$regex"
+ << "abc"))),
+ ExtensionsCallbackNoop(),
+ collator);
+ Matcher reserialized(
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$eq" << BSON("$regex"
+ << "abc"))))));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
+
+ BSONObj obj = fromjson("{x: {a: 1}}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+
+ obj = fromjson("{x: {a: [1, 2]}}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+}
+
TEST(SerializeBasic, ExpressionLtSerializesCorrectly) {
const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: {$lt: 3}}"), ExtensionsCallbackNoop(), collator);
@@ -341,7 +388,9 @@ TEST(SerializeBasic, ExpressionRegexWithObjSerializesCorrectly) {
Matcher original(fromjson("{x: {$regex: 'a.b'}}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$regex: 'a.b'}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("x" << BSON("$regex"
+ << "a.b")));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -351,12 +400,16 @@ TEST(SerializeBasic, ExpressionRegexWithObjSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
-TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
+TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) {
const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: /a.b/i}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$regex: 'a.b', $options: 'i'}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("x" << BSON("$regex"
+ << "a.b"
+ << "$options"
+ << "i")));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -366,12 +419,14 @@ TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
-TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) {
+TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: /a.b/}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$regex: 'a.b'}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("x" << BSON("$regex"
+ << "a.b")));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -510,6 +565,25 @@ TEST(SerializeBasic, ExpressionNinSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
+TEST(SerializeBasic, ExpressionNinWithRegexValueSerializesCorrectly) {
+ const CollatorInterface* collator = nullptr;
+ Matcher original(
+ fromjson("{x: {$nin: [/abc/, /def/, /xyz/]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher reserialized(
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ fromjson("{$nor: [{x: {$in: [/abc/, /def/, /xyz/]}}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
+
+ BSONObj obj = fromjson("{x: 'abc'}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+
+ obj = fromjson("{x: 'def'}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+ obj = fromjson("{x: [/abc/, /def/]}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+}
+
TEST(SerializeBasic, ExpressionBitsAllSetSerializesCorrectly) {
const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: {$bitsAllSet: [1, 3]}}"), ExtensionsCallbackNoop(), collator);
@@ -626,7 +700,9 @@ TEST(SerializeBasic, ExpressionNotWithRegexObjSerializesCorrectly) {
Matcher original(fromjson("{x: {$not: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: /a.b/}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
+ << "a.b")))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -641,7 +717,9 @@ TEST(SerializeBasic, ExpressionNotWithRegexValueSerializesCorrectly) {
Matcher original(fromjson("{x: {$not: /a.b/}}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: /a.b/}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
+ << "a.b")))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -656,7 +734,11 @@ TEST(SerializeBasic, ExpressionNotWithRegexValueAndOptionsSerializesCorrectly) {
Matcher original(fromjson("{x: {$not: /a.b/i}}"), ExtensionsCallbackNoop(), collator);
Matcher reserialized(
serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: /a.b/i}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
+ << "a.b"
+ << "$options"
+ << "i")))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");