summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher
diff options
context:
space:
mode:
authorJason Rassi <rassi@10gen.com>2015-03-12 18:52:56 -0400
committerJason Rassi <rassi@10gen.com>2015-03-12 18:55:39 -0400
commit0144f59236f507bf4a75a1e2b698100bcd75e4da (patch)
tree7debcec36431febf47ca2bb15f6acd912143b04f /src/mongo/db/matcher
parent86f3f05da7238cabb8d260677e81a07399a0906b (diff)
downloadmongo-0144f59236f507bf4a75a1e2b698100bcd75e4da.tar.gz
SERVER-17437 $caseSensitive option for $text query operator
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r--src/mongo/db/matcher/expression_parser_text.cpp25
-rw-r--r--src/mongo/db/matcher/expression_parser_text_test.cpp48
-rw-r--r--src/mongo/db/matcher/expression_text.cpp18
-rw-r--r--src/mongo/db/matcher/expression_text.h4
4 files changed, 76 insertions, 19 deletions
diff --git a/src/mongo/db/matcher/expression_parser_text.cpp b/src/mongo/db/matcher/expression_parser_text.cpp
index ad8114c88e8..0a7cb0b01ab 100644
--- a/src/mongo/db/matcher/expression_parser_text.cpp
+++ b/src/mongo/db/matcher/expression_parser_text.cpp
@@ -44,16 +44,20 @@ namespace mongo {
// Validate queryObj, but defer construction of FTSQuery (which requires access to the
// target namespace) until stage building time.
+ int expectedFieldCount = 1;
+
if ( mongo::String != queryObj["$search"].type() ) {
- return StatusWithMatchExpression( ErrorCodes::BadValue, "$search needs a String" );
+ return StatusWithMatchExpression( ErrorCodes::TypeMismatch,
+ "$search requires a string value" );
}
string language = "";
BSONElement languageElt = queryObj["$language"];
if ( !languageElt.eoo() ) {
+ expectedFieldCount++;
if ( mongo::String != languageElt.type() ) {
- return StatusWithMatchExpression( ErrorCodes::BadValue,
- "$language needs a String" );
+ return StatusWithMatchExpression( ErrorCodes::TypeMismatch,
+ "$language requires a string value" );
}
language = languageElt.String();
Status status =
@@ -65,12 +69,23 @@ namespace mongo {
}
string query = queryObj["$search"].String();
- if ( queryObj.nFields() != ( languageElt.eoo() ? 1 : 2 ) ) {
+ BSONElement caseSensitiveElt = queryObj["$caseSensitive"];
+ bool caseSensitive = fts::FTSQuery::caseSensitiveDefault;
+ if ( !caseSensitiveElt.eoo() ) {
+ expectedFieldCount++;
+ if ( mongo::Bool != caseSensitiveElt.type() ) {
+ return StatusWithMatchExpression( ErrorCodes::TypeMismatch,
+ "$caseSensitive requires a boolean value" );
+ }
+ caseSensitive = caseSensitiveElt.trueValue();
+ }
+
+ if ( queryObj.nFields() != expectedFieldCount ) {
return StatusWithMatchExpression( ErrorCodes::BadValue, "extra fields in $text" );
}
auto_ptr<TextMatchExpression> e( new TextMatchExpression() );
- Status s = e->init( query, language );
+ Status s = e->init( query, language, caseSensitive );
if ( !s.isOK() ) {
return StatusWithMatchExpression( s );
}
diff --git a/src/mongo/db/matcher/expression_parser_text_test.cpp b/src/mongo/db/matcher/expression_parser_text_test.cpp
index b2f42018178..42eb9ce2464 100644
--- a/src/mongo/db/matcher/expression_parser_text_test.cpp
+++ b/src/mongo/db/matcher/expression_parser_text_test.cpp
@@ -39,23 +39,53 @@
namespace mongo {
- TEST( MatchExpressionParserText, Parse1 ) {
- BSONObj query = fromjson( "{$text:{$search:\"awesome\", $language:\"english\"}}" );
+ TEST( MatchExpressionParserText, Basic ) {
+ BSONObj query = fromjson( "{$text: {$search:\"awesome\", $language:\"english\"}}" );
StatusWithMatchExpression result = MatchExpressionParser::parse( query );
ASSERT_TRUE( result.isOK() );
- MatchExpression* exp = result.getValue();
- ASSERT_EQUALS( MatchExpression::TEXT, exp->matchType() );
-
- TextMatchExpression* textExp = static_cast<TextMatchExpression*>( exp );
+ ASSERT_EQUALS( MatchExpression::TEXT, result.getValue()->matchType() );
+ boost::scoped_ptr<TextMatchExpression> textExp(
+ static_cast<TextMatchExpression*>( result.getValue() ) );
ASSERT_EQUALS( textExp->getQuery(), "awesome" );
ASSERT_EQUALS( textExp->getLanguage(), "english" );
- delete exp;
+ ASSERT_EQUALS( textExp->getCaseSensitive(), fts::FTSQuery::caseSensitiveDefault );
+ }
+
+ TEST( MatchExpressionParserText, LanguageError ) {
+ BSONObj query = fromjson( "{$text: {$search:\"awesome\", $language:\"spanglish\"}}" );
+
+ StatusWithMatchExpression result = MatchExpressionParser::parse( query );
+ ASSERT_FALSE( result.isOK() );
+ }
+
+ TEST( MatchExpressionParserText, CaseSensitiveTrue ) {
+ BSONObj query = fromjson( "{$text: {$search:\"awesome\", $caseSensitive: true}}" );
+
+ StatusWithMatchExpression result = MatchExpressionParser::parse( query );
+ ASSERT_TRUE( result.isOK() );
+
+ ASSERT_EQUALS( MatchExpression::TEXT, result.getValue()->matchType() );
+ boost::scoped_ptr<TextMatchExpression> textExp(
+ static_cast<TextMatchExpression*>( result.getValue() ) );
+ ASSERT_EQUALS( textExp->getCaseSensitive(), true );
+ }
+
+ TEST( MatchExpressionParserText, CaseSensitiveFalse ) {
+ BSONObj query = fromjson( "{$text: {$search:\"awesome\", $caseSensitive: false}}" );
+
+ StatusWithMatchExpression result = MatchExpressionParser::parse( query );
+ ASSERT_TRUE( result.isOK() );
+
+ ASSERT_EQUALS( MatchExpression::TEXT, result.getValue()->matchType() );
+ boost::scoped_ptr<TextMatchExpression> textExp(
+ static_cast<TextMatchExpression*>( result.getValue() ) );
+ ASSERT_EQUALS( textExp->getCaseSensitive(), false );
}
- TEST( MatchExpressionParserText, Parse2 ) {
- BSONObj query = fromjson( "{$text:{$search:\"awesome\", $language:\"spanglish\"}}" );
+ TEST( MatchExpressionParserText, CaseSensitiveError ) {
+ BSONObj query = fromjson( "{$text:{$search:\"awesome\", $caseSensitive: 0}}" );
StatusWithMatchExpression result = MatchExpressionParser::parse( query );
ASSERT_FALSE( result.isOK() );
diff --git a/src/mongo/db/matcher/expression_text.cpp b/src/mongo/db/matcher/expression_text.cpp
index 3754d431f57..320136c8751 100644
--- a/src/mongo/db/matcher/expression_text.cpp
+++ b/src/mongo/db/matcher/expression_text.cpp
@@ -35,9 +35,12 @@ namespace mongo {
using std::string;
- Status TextMatchExpression::init( const string& query, const string& language ) {
+ Status TextMatchExpression::init( const string& query,
+ const string& language,
+ bool caseSensitive ) {
_query = query;
_language = language;
+ _caseSensitive = caseSensitive;
return initPath( "_fts" );
}
@@ -50,7 +53,9 @@ namespace mongo {
void TextMatchExpression::debugString( StringBuilder& debug, int level ) const {
_debugAddSpace(debug, level);
- debug << "TEXT : query=" << _query << ", language=" << _language << ", tag=";
+ debug << "TEXT : query=" << _query << ", language="
+ << _language << ", caseSensitive="
+ << _caseSensitive << ", tag=";
MatchExpression::TagData* td = getTag();
if ( NULL != td ) {
td->debugString( &debug );
@@ -62,7 +67,9 @@ namespace mongo {
}
void TextMatchExpression::toBSON(BSONObjBuilder* out) const {
- out->append("$text", BSON("$search" << _query << "$language" << _language));
+ out->append("$text", BSON("$search" << _query <<
+ "$language" << _language <<
+ "$caseSensitive" << _caseSensitive));
}
bool TextMatchExpression::equivalent( const MatchExpression* other ) const {
@@ -79,12 +86,15 @@ namespace mongo {
if ( realOther->getLanguage() != _language ) {
return false;
}
+ if ( realOther->getCaseSensitive() != _caseSensitive ) {
+ return false;
+ }
return true;
}
LeafMatchExpression* TextMatchExpression::shallowClone() const {
TextMatchExpression* next = new TextMatchExpression();
- next->init( _query, _language );
+ next->init( _query, _language, _caseSensitive );
if ( getTag() ) {
next->setTag( getTag()->clone() );
}
diff --git a/src/mongo/db/matcher/expression_text.h b/src/mongo/db/matcher/expression_text.h
index 6ebea78ba3d..8d853de5621 100644
--- a/src/mongo/db/matcher/expression_text.h
+++ b/src/mongo/db/matcher/expression_text.h
@@ -41,7 +41,7 @@ namespace mongo {
TextMatchExpression() : LeafMatchExpression( TEXT ) {}
virtual ~TextMatchExpression() {}
- Status init( const std::string& query, const std::string& language );
+ Status init( const std::string& query, const std::string& language, bool caseSensitive );
virtual bool matchesSingleElement( const BSONElement& e ) const;
@@ -55,9 +55,11 @@ namespace mongo {
const std::string& getQuery() const { return _query; }
const std::string& getLanguage() const { return _language; }
+ bool getCaseSensitive() const { return _caseSensitive; }
private:
std::string _query;
std::string _language;
+ bool _caseSensitive;
};
} // namespace mongo