summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorArun Banala <arun.banala@mongodb.com>2019-03-14 17:06:06 +0000
committerArun Banala <arun.banala@mongodb.com>2019-03-20 18:42:33 +0000
commitb72e0f72f94f219fcc19654e0aa037482c0f7cee (patch)
treea64707651e0fff5a871d4a79c51183b7612a92d8 /src/mongo/db
parent6d774652650dff718a8fa89c2bc845c3b11aa051 (diff)
downloadmongo-b72e0f72f94f219fcc19654e0aa037482c0f7cee.tar.gz
SERVER-39694 Implement $regexMatch as syntactic sugar on top of $regexFind
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/pipeline/expression.cpp39
-rw-r--r--src/mongo/db/pipeline/expression.h11
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp27
3 files changed, 64 insertions, 13 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index f2bd565989e..629bfd847f8 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -5674,6 +5674,22 @@ public:
}
}
+ int execute(int startBytePos) {
+ int execResult = pcre_exec(_pcre,
+ 0,
+ &_input.front(),
+ _input.size(),
+ startBytePos,
+ 0, // No need to overwrite the options set during pcre_compile.
+ &_capturesBuffer.front(),
+ _capturesBuffer.size());
+ // The 'execResult' will be '(_numCaptures + 1)' if there is a match, negative if there is
+ // no match, and zero if _capturesBuffer's capacity is not sufficient to hold all the
+ // results; the latter scenario should never actually occur.
+ invariant(execResult < 0 || execResult == _numCaptures + 1);
+ return execResult;
+ }
+
/**
* The function will match '_input' string based on the regex pattern present in '_pcre'. If
* there is a match, the function will return a 'Value' object encapsulating the matched string,
@@ -5688,21 +5704,11 @@ public:
// Use input as StringData throughout the function to avoid copying the string on 'substr'
// calls.
StringData input = _input;
- int execResult = pcre_exec(_pcre,
- 0,
- input.rawData(),
- input.size(),
- *startBytePos,
- 0, // No need to overwrite the options set during pcre_compile.
- &_capturesBuffer.front(),
- _capturesBuffer.size());
+ int execResult = execute(*startBytePos);
// No match.
if (execResult < 0) {
return Value(BSONNULL);
}
- // The 'execResult' will be zero if _capturesBuffer's size is not big enough to hold all
- // the captures, which should never be the case.
- invariant(execResult == _numCaptures + 1);
// The first and second entries of the '_capturesBuffer' will have the start and limit
// indices of the matched string, as byte offsets. '(limit - startIndex)' would be the
@@ -5905,4 +5911,15 @@ const char* ExpressionRegexFindAll::getOpName() const {
return "$regexFindAll";
}
+Value ExpressionRegexMatch::evaluate(const Document& root) const {
+ RegexMatchHandler regex(vpOperand[0]->evaluate(root));
+ // Return output of execute only if regex is not nullish.
+ return regex.nullish() ? Value(false) : Value(regex.execute(0) > 0);
+}
+
+REGISTER_EXPRESSION(regexMatch, ExpressionRegexMatch::parse);
+const char* ExpressionRegexMatch::getOpName() const {
+ return "$regexMatch";
+}
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index b0949cca3fc..f5b7ec4fc12 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -2093,7 +2093,7 @@ private:
boost::intrusive_ptr<Expression> _onNull;
};
-class ExpressionRegexFind : public ExpressionFixedArity<ExpressionRegexFind, 1> {
+class ExpressionRegexFind final : public ExpressionFixedArity<ExpressionRegexFind, 1> {
public:
explicit ExpressionRegexFind(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionRegexFind, 1>(expCtx) {}
@@ -2110,4 +2110,13 @@ public:
Value evaluate(const Document& root) const final;
const char* getOpName() const final;
};
+
+class ExpressionRegexMatch final : public ExpressionFixedArity<ExpressionRegexMatch, 1> {
+public:
+ explicit ExpressionRegexMatch(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : ExpressionFixedArity<ExpressionRegexMatch, 1>(expCtx) {}
+
+ Value evaluate(const Document& root) const final;
+ const char* getOpName() const final;
+};
}
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index c9bc46a2c8b..2cdc6bb5a30 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -5975,7 +5975,6 @@ TEST(ExpressionRegexFindTest, ExtendedRegexOptions) {
TEST(ExpressionRegexFindTest, FailureCase) {
Value input(
fromjson("{input: 'FirstLine\\nSecondLine', regex: {invalid : 'regex'} , options: 'mi'}"));
- BSONObj expectedOut(fromjson("{match: 'Second', idx:10, captures:[]}"));
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
ExpressionRegexFind regexF(expCtx);
regexF.addOperand(ExpressionConstant::create(expCtx, input));
@@ -6036,6 +6035,32 @@ TEST(ExpressionRegexFindAllTest, InvalidUTF8InRegex) {
ASSERT_THROWS_CODE(regexF.evaluate(Document()), DBException, 51111);
}
+TEST(ExpressionRegexMatchTest, NoMatch) {
+ Value input(fromjson("{input: 'asdf', regex: '^sd' }"));
+ Value expectedOut(false);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ExpressionRegexMatch regexMatchExpr(expCtx);
+ regexMatchExpr.addOperand(ExpressionConstant::create(expCtx, input));
+ ASSERT_VALUE_EQ(regexMatchExpr.evaluate(Document()), expectedOut);
+}
+
+TEST(ExpressionRegexMatchTest, ExtendedRegexOptions) {
+ Value input(fromjson("{input: 'FirstLine\\nSecondLine', regex: '^second' , options: 'mi'}"));
+ Value expectedOut(true);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ExpressionRegexMatch regexMatchExpr(expCtx);
+ regexMatchExpr.addOperand(ExpressionConstant::create(expCtx, input));
+ ASSERT_VALUE_EQ(regexMatchExpr.evaluate(Document()), expectedOut);
+}
+
+TEST(ExpressionRegexMatchTest, FailureCase) {
+ Value input(fromjson("{regex: 'valid', input: {invalid : 'input'} , options: 'mi'}"));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ExpressionRegexMatch regexMatchExpr(expCtx);
+ regexMatchExpr.addOperand(ExpressionConstant::create(expCtx, input));
+ ASSERT_THROWS_CODE(regexMatchExpr.evaluate(Document()), DBException, 51104);
+}
+
} // namespace ExpressionRegexTest
class All : public Suite {