diff options
author | Arun Banala <arun.banala@mongodb.com> | 2019-03-14 17:06:06 +0000 |
---|---|---|
committer | Arun Banala <arun.banala@mongodb.com> | 2019-03-20 18:42:33 +0000 |
commit | b72e0f72f94f219fcc19654e0aa037482c0f7cee (patch) | |
tree | a64707651e0fff5a871d4a79c51183b7612a92d8 /src/mongo/db | |
parent | 6d774652650dff718a8fa89c2bc845c3b11aa051 (diff) | |
download | mongo-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.cpp | 39 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 11 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 27 |
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 { |