summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/exec/plan_stats.h2
-rw-r--r--src/mongo/db/exec/stagedebug_cmd.cpp10
-rw-r--r--src/mongo/db/fts/SConscript13
-rw-r--r--src/mongo/db/fts/fts_matcher_test.cpp66
-rw-r--r--src/mongo/db/fts/fts_query.h107
-rw-r--r--src/mongo/db/fts/fts_query_impl.cpp44
-rw-r--r--src/mongo/db/fts/fts_query_impl.h47
-rw-r--r--src/mongo/db/fts/fts_query_impl_test.cpp181
-rw-r--r--src/mongo/db/fts/fts_query_noop.cpp48
-rw-r--r--src/mongo/db/fts/fts_query_noop.h49
-rw-r--r--src/mongo/db/fts/fts_query_noop_test.cpp57
-rw-r--r--src/mongo/db/fts/fts_util.h7
-rw-r--r--src/mongo/db/matcher/SConscript18
-rw-r--r--src/mongo/db/matcher/expression_text.cpp72
-rw-r--r--src/mongo/db/matcher/expression_text.h14
-rw-r--r--src/mongo/db/matcher/expression_text_base.cpp36
-rw-r--r--src/mongo/db/matcher/expression_text_base.h31
-rw-r--r--src/mongo/db/matcher/expression_text_noop.cpp23
-rw-r--r--src/mongo/db/matcher/expression_text_noop.h10
-rw-r--r--src/mongo/db/matcher/extensions_callback_noop.cpp4
-rw-r--r--src/mongo/db/matcher/extensions_callback_real.cpp4
-rw-r--r--src/mongo/db/query/SConscript2
-rw-r--r--src/mongo/db/query/planner_access.cpp7
-rw-r--r--src/mongo/db/query/planner_ixselect.h1
-rw-r--r--src/mongo/db/query/query_planner_test_lib.cpp8
-rw-r--r--src/mongo/db/query/query_solution.cpp13
-rw-r--r--src/mongo/db/query/query_solution.h9
-rw-r--r--src/mongo/db/query/stage_builder.cpp42
-rw-r--r--src/mongo/dbtests/extensions_callback_real_test.cpp194
-rw-r--r--src/mongo/s/chunk_manager.cpp6
31 files changed, 812 insertions, 314 deletions
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 133e2147427..d36f661e414 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -58,7 +58,6 @@ env.Library(
'$BUILD_DIR/mongo/db/log_process_details',
'$BUILD_DIR/mongo/db/matcher/expressions',
'$BUILD_DIR/mongo/db/matcher/expressions_geo',
- '$BUILD_DIR/mongo/db/matcher/expressions_text',
'$BUILD_DIR/mongo/db/repl/isself',
'$BUILD_DIR/mongo/db/repl/repl_coordinator_global',
'$BUILD_DIR/mongo/db/server_options',
diff --git a/src/mongo/db/exec/plan_stats.h b/src/mongo/db/exec/plan_stats.h
index 211b975b9dc..14e07391920 100644
--- a/src/mongo/db/exec/plan_stats.h
+++ b/src/mongo/db/exec/plan_stats.h
@@ -601,7 +601,7 @@ struct TextStats : public SpecificStats {
std::string indexName;
- // Human-readable form of the FTSQueryImpl associated with the text stage.
+ // Human-readable form of the FTSQuery associated with the text stage.
BSONObj parsedTextQuery;
// Index keys that precede the "text" index key.
diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp
index 623745f0047..378a0d7ba4d 100644
--- a/src/mongo/db/exec/stagedebug_cmd.cpp
+++ b/src/mongo/db/exec/stagedebug_cmd.cpp
@@ -459,11 +459,11 @@ public:
params.spec = fam->getSpec();
- if (!params.query.parse(search,
- fam->getSpec().defaultLanguage().str().c_str(),
- TextMatchExpressionBase::kCaseSensitiveDefault,
- TextMatchExpressionBase::kDiacriticSensitiveDefault,
- fam->getSpec().getTextIndexVersion()).isOK()) {
+ params.query.setQuery(search);
+ params.query.setLanguage(fam->getSpec().defaultLanguage().str());
+ params.query.setCaseSensitive(TextMatchExpressionBase::kCaseSensitiveDefault);
+ params.query.setDiacriticSensitive(TextMatchExpressionBase::kDiacriticSensitiveDefault);
+ if (!params.query.parse(fam->getSpec().getTextIndexVersion()).isOK()) {
return NULL;
}
diff --git a/src/mongo/db/fts/SConscript b/src/mongo/db/fts/SConscript
index 300f05c04e0..1a752c1cae8 100644
--- a/src/mongo/db/fts/SConscript
+++ b/src/mongo/db/fts/SConscript
@@ -58,6 +58,16 @@ baseEnv.Library('base', [
"$BUILD_DIR/third_party/shim_stemmer",
])
+env.Library(
+ target='fts_query_noop',
+ source=[
+ 'fts_query_noop.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ],
+)
+
env.Library( 'server_common', [
'fts_enabled.cpp'
],
@@ -96,6 +106,9 @@ env.CppUnitTest( "fts_matcher_test", "fts_matcher_test.cpp",
env.CppUnitTest( "fts_query_impl_test", "fts_query_impl_test.cpp",
LIBDEPS=["base"] )
+env.CppUnitTest( "fts_query_noop_test", "fts_query_noop_test.cpp",
+ LIBDEPS=["fts_query_noop"] )
+
env.CppUnitTest( "fts_spec_test", "fts_spec_test.cpp",
LIBDEPS=["base"] )
diff --git a/src/mongo/db/fts/fts_matcher_test.cpp b/src/mongo/db/fts/fts_matcher_test.cpp
index 02f6c2b2ba0..9b590fa67a3 100644
--- a/src/mongo/db/fts/fts_matcher_test.cpp
+++ b/src/mongo/db/fts/fts_matcher_test.cpp
@@ -38,7 +38,11 @@ namespace fts {
TEST(FTSMatcher, NegWild1) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("foo -bar", "english", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("foo -bar");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("$**"
<< "text")))));
@@ -52,7 +56,11 @@ TEST(FTSMatcher, NegWild1) {
// Regression test for SERVER-11994.
TEST(FTSMatcher, NegWild2) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("pizza -restaurant", "english", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("pizza -restaurant");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("$**"
<< "text")))));
@@ -65,7 +73,11 @@ TEST(FTSMatcher, NegWild2) {
TEST(FTSMatcher, Phrase1) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("foo \"table top\"", "english", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("foo \"table top\"");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("$**"
<< "text")))));
@@ -87,7 +99,11 @@ TEST(FTSMatcher, Phrase1) {
TEST(FTSMatcher, Phrase2) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("foo \"table top\"", "english", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("foo \"table top\"");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -98,7 +114,11 @@ TEST(FTSMatcher, Phrase2) {
// language.
TEST(FTSMatcher, ParsesUsingDocLanguage) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("-glad", "none", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("-glad");
+ q.setLanguage("none");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -112,7 +132,11 @@ TEST(FTSMatcher, ParsesUsingDocLanguage) {
// Test the matcher does not filter out stop words from positive terms
TEST(FTSMatcher, MatcherDoesNotFilterStopWordsNeg) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("-the", "none", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("-the");
+ q.setLanguage("none");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -124,7 +148,11 @@ TEST(FTSMatcher, MatcherDoesNotFilterStopWordsNeg) {
// Test the matcher does not filter out stop words from negative terms
TEST(FTSMatcher, MatcherDoesNotFilterStopWordsPos) {
FTSQueryImpl q;
- ASSERT_OK(q.parse("the", "none", false, false, TEXT_INDEX_VERSION_3));
+ q.setQuery("the");
+ q.setLanguage("none");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -137,7 +165,11 @@ TEST(FTSMatcher, MatcherDoesNotFilterStopWordsPos) {
// case-sensitive text query 'search'.
static bool docHasPositiveTermWithCase(const std::string& doc, const std::string& search) {
FTSQueryImpl q;
- ASSERT_OK(q.parse(search, "english", true, false, TEXT_INDEX_VERSION_3));
+ q.setQuery(search);
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -164,7 +196,11 @@ TEST(FTSMatcher, HasPositiveTermCaseSensitive) {
// case-sensitive text query 'search'.
static bool docHasNegativeTermWithCase(const std::string& doc, const std::string& search) {
FTSQueryImpl q;
- ASSERT_OK(q.parse(search, "english", true, false, TEXT_INDEX_VERSION_3));
+ q.setQuery(search);
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -191,7 +227,11 @@ TEST(FTSMatcher, HasNegativeTermCaseSensitive) {
// from case-sensitive text query 'search'.
static bool docPositivePhrasesMatchWithCase(const std::string& doc, const std::string& search) {
FTSQueryImpl q;
- ASSERT_OK(q.parse(search, "english", true, false, TEXT_INDEX_VERSION_3));
+ q.setQuery(search);
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
@@ -214,7 +254,11 @@ TEST(FTSMatcher, PositivePhrasesMatchWithCase) {
// from case-sensitive text query 'search'.
static bool docNegativePhrasesMatchWithCase(const std::string& doc, const std::string& search) {
FTSQueryImpl q;
- ASSERT_OK(q.parse(search, "english", true, false, TEXT_INDEX_VERSION_3));
+ q.setQuery(search);
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
FTSMatcher m(q,
FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x"
<< "text")))));
diff --git a/src/mongo/db/fts/fts_query.h b/src/mongo/db/fts/fts_query.h
new file mode 100644
index 00000000000..c2b2fd87deb
--- /dev/null
+++ b/src/mongo/db/fts/fts_query.h
@@ -0,0 +1,107 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "mongo/db/fts/fts_util.h"
+
+namespace mongo {
+namespace fts {
+
+/**
+ * An FTSQuery represents a parsed text search query.
+ */
+class FTSQuery {
+public:
+ virtual ~FTSQuery() {}
+
+ void setQuery(std::string query) {
+ _query = std::move(query);
+ }
+
+ void setLanguage(std::string language) {
+ _language = std::move(language);
+ }
+
+ void setCaseSensitive(bool caseSensitive) {
+ _caseSensitive = caseSensitive;
+ }
+
+ void setDiacriticSensitive(bool diacriticSensitive) {
+ _diacriticSensitive = diacriticSensitive;
+ }
+
+ const std::string& getQuery() const {
+ return _query;
+ }
+
+ const std::string& getLanguage() const {
+ return _language;
+ }
+
+ bool getCaseSensitive() const {
+ return _caseSensitive;
+ }
+
+ bool getDiacriticSensitive() const {
+ return _diacriticSensitive;
+ }
+
+ /**
+ * Returns true iff '*this' and 'other' have the same unparsed form.
+ */
+ bool equivalent(const FTSQuery& other) const {
+ return _query == other._query && _language == other._language &&
+ _caseSensitive == other._caseSensitive &&
+ _diacriticSensitive == other._diacriticSensitive;
+ }
+
+ /**
+ * Parses the text search query. Before parsing, the FTSQuery needs to be initialized with
+ * the set*() methods above.
+ *
+ * Returns Status::OK() if parsing was successful; returns an error Status otherwise.
+ */
+ virtual Status parse(TextIndexVersion textIndexVersion) = 0;
+
+ /**
+ * Returns a copy of this FTSQuery.
+ */
+ virtual std::unique_ptr<FTSQuery> clone() const = 0;
+
+private:
+ std::string _query;
+ std::string _language;
+ bool _caseSensitive = false;
+ bool _diacriticSensitive = false;
+};
+
+} // namespace fts
+} // namespace mongo
diff --git a/src/mongo/db/fts/fts_query_impl.cpp b/src/mongo/db/fts/fts_query_impl.cpp
index af03d2ed5da..674c8e84ab2 100644
--- a/src/mongo/db/fts/fts_query_impl.cpp
+++ b/src/mongo/db/fts/fts_query_impl.cpp
@@ -37,6 +37,7 @@
#include "mongo/db/fts/fts_tokenizer.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/stringutils.h"
+#include "mongo/stdx/memory.h"
namespace mongo {
@@ -49,18 +50,11 @@ using std::string;
using std::stringstream;
using std::vector;
-Status FTSQueryImpl::parse(const string& query,
- StringData language,
- bool caseSensitive,
- bool diacriticSensitive,
- TextIndexVersion textIndexVersion) {
- StatusWithFTSLanguage swl = FTSLanguage::make(language, textIndexVersion);
- if (!swl.getStatus().isOK()) {
- return swl.getStatus();
+Status FTSQueryImpl::parse(TextIndexVersion textIndexVersion) {
+ StatusWithFTSLanguage ftsLanguage = FTSLanguage::make(getLanguage(), textIndexVersion);
+ if (!ftsLanguage.getStatus().isOK()) {
+ return ftsLanguage.getStatus();
}
- _language = swl.getValue();
- _caseSensitive = caseSensitive;
- _diacriticSensitive = diacriticSensitive;
// Build a space delimited list of words to have the FtsTokenizer tokenize
string positiveTermSentence;
@@ -71,7 +65,7 @@ Status FTSQueryImpl::parse(const string& query,
unsigned quoteOffset = 0;
- FTSQueryParser i(query);
+ FTSQueryParser i(getQuery());
while (i.more()) {
QueryToken t = i.next();
@@ -105,7 +99,7 @@ Status FTSQueryImpl::parse(const string& query,
// end of a phrase
unsigned phraseStart = quoteOffset + 1;
unsigned phraseLength = t.offset - phraseStart;
- StringData phrase = StringData(query).substr(phraseStart, phraseLength);
+ StringData phrase = StringData(getQuery()).substr(phraseStart, phraseLength);
if (inNegation) {
_negatedPhrases.push_back(phrase.toString());
} else {
@@ -124,7 +118,7 @@ Status FTSQueryImpl::parse(const string& query,
}
}
- std::unique_ptr<FTSTokenizer> tokenizer(_language->createTokenizer());
+ std::unique_ptr<FTSTokenizer> tokenizer(ftsLanguage.getValue()->createTokenizer());
_addTerms(tokenizer.get(), positiveTermSentence, false);
_addTerms(tokenizer.get(), negativeTermSentence, true);
@@ -132,6 +126,20 @@ Status FTSQueryImpl::parse(const string& query,
return Status::OK();
}
+std::unique_ptr<FTSQuery> FTSQueryImpl::clone() const {
+ auto clonedQuery = stdx::make_unique<FTSQueryImpl>();
+ clonedQuery->setQuery(getQuery());
+ clonedQuery->setLanguage(getLanguage());
+ clonedQuery->setCaseSensitive(getCaseSensitive());
+ clonedQuery->setDiacriticSensitive(getDiacriticSensitive());
+ clonedQuery->_positiveTerms = _positiveTerms;
+ clonedQuery->_negatedTerms = _negatedTerms;
+ clonedQuery->_positivePhrases = _positivePhrases;
+ clonedQuery->_negatedPhrases = _negatedPhrases;
+ clonedQuery->_termsForBounds = _termsForBounds;
+ return std::move(clonedQuery);
+}
+
void FTSQueryImpl::_addTerms(FTSTokenizer* tokenizer, const string& sentence, bool negated) {
tokenizer->reset(sentence.c_str(), FTSTokenizer::kFilterStopWords);
@@ -150,21 +158,21 @@ void FTSQueryImpl::_addTerms(FTSTokenizer* tokenizer, const string& sentence, bo
// Compute the string corresponding to 'token' that will be used for the matcher.
// For case and diacritic insensitive queries, this is the same string as 'boundsTerm'
// computed above.
- if (!_caseSensitive && !_diacriticSensitive) {
+ if (!getCaseSensitive() && !getDiacriticSensitive()) {
activeTerms.insert(word);
}
}
- if (!_caseSensitive && !_diacriticSensitive) {
+ if (!getCaseSensitive() && !getDiacriticSensitive()) {
return;
}
FTSTokenizer::Options newOptions = FTSTokenizer::kFilterStopWords;
- if (_caseSensitive) {
+ if (getCaseSensitive()) {
newOptions |= FTSTokenizer::kGenerateCaseSensitiveTokens;
}
- if (_diacriticSensitive) {
+ if (getDiacriticSensitive()) {
newOptions |= FTSTokenizer::kGenerateDiacriticSensitiveTokens;
}
diff --git a/src/mongo/db/fts/fts_query_impl.h b/src/mongo/db/fts/fts_query_impl.h
index dbd266983d3..888cec08e13 100644
--- a/src/mongo/db/fts/fts_query_impl.h
+++ b/src/mongo/db/fts/fts_query_impl.h
@@ -30,31 +30,23 @@
#pragma once
+#include <set>
#include <string>
#include <vector>
-#include "mongo/base/status.h"
-#include "mongo/db/fts/stemmer.h"
-#include "mongo/db/fts/stop_words.h"
-#include "mongo/util/stringutils.h"
+#include "mongo/db/fts/fts_query.h"
namespace mongo {
namespace fts {
-class FTSQueryImpl {
+class FTSTokenizer;
+
+class FTSQueryImpl final : public FTSQuery {
public:
- // Initializes an FTSQueryImpl. Note that the parsing of "language" depends on the text
- // index version, since a query which doesn't specify a language and is against a
- // version 1 text index with a version 1 default language string needs to be parsed as
- // version 1 (see fts_language.cpp for a list of language strings specific to version
- // 1). Note that the diacritic sensitive option has no effect on FTS queries below index version
- // 3.
- Status parse(const std::string& query,
- StringData language,
- bool caseSensitive,
- bool diacriticSensitive,
- TextIndexVersion textIndexVersion);
+ Status parse(TextIndexVersion textIndexVersion) final;
+
+ std::unique_ptr<FTSQuery> clone() const final;
const std::set<std::string>& getPositiveTerms() const {
return _positiveTerms;
@@ -73,16 +65,6 @@ public:
return _termsForBounds;
}
- const FTSLanguage& getLanguage() const {
- return *_language;
- }
- bool getCaseSensitive() const {
- return _caseSensitive;
- }
- bool getDiacriticSensitive() const {
- return _diacriticSensitive;
- }
-
std::string toString() const;
std::string debugString() const;
@@ -92,23 +74,10 @@ public:
private:
void _addTerms(FTSTokenizer* tokenizer, const std::string& tokens, bool negated);
- const FTSLanguage* _language;
- bool _caseSensitive;
- bool _diacriticSensitive;
-
- // Positive terms.
std::set<std::string> _positiveTerms;
-
- // Negated terms.
std::set<std::string> _negatedTerms;
-
- // Positive phrases.
std::vector<std::string> _positivePhrases;
-
- // Negated phrases.
std::vector<std::string> _negatedPhrases;
-
- // Terms for bounds.
std::set<std::string> _termsForBounds;
};
}
diff --git a/src/mongo/db/fts/fts_query_impl_test.cpp b/src/mongo/db/fts/fts_query_impl_test.cpp
index 9f336e3c75b..538ee3755bc 100644
--- a/src/mongo/db/fts/fts_query_impl_test.cpp
+++ b/src/mongo/db/fts/fts_query_impl_test.cpp
@@ -37,7 +37,11 @@ namespace fts {
TEST(FTSQueryImpl, Basic1) {
FTSQueryImpl q;
- ASSERT(q.parse("this is fun", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
+ q.setQuery("this is fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(false, q.getCaseSensitive());
ASSERT_EQUALS(1U, q.getPositiveTerms().size());
@@ -50,7 +54,11 @@ TEST(FTSQueryImpl, Basic1) {
TEST(FTSQueryImpl, ParsePunctuation) {
FTSQueryImpl q;
- ASSERT(q.parse("hello.world", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
+ q.setQuery("hello.world");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(false, q.getCaseSensitive());
ASSERT_EQUALS(2U, q.getPositiveTerms().size());
@@ -64,7 +72,11 @@ TEST(FTSQueryImpl, ParsePunctuation) {
TEST(FTSQueryImpl, Neg1) {
FTSQueryImpl q;
- ASSERT(q.parse("this is -really fun", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
+ q.setQuery("this is -really fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(1U, q.getPositiveTerms().size());
ASSERT_EQUALS("fun", *q.getPositiveTerms().begin());
@@ -75,8 +87,11 @@ TEST(FTSQueryImpl, Neg1) {
TEST(FTSQueryImpl, Phrase1) {
FTSQueryImpl q;
- ASSERT(q.parse("doing a \"phrase test\" for fun", "english", false, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("doing a \"phrase test\" for fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(3U, q.getPositiveTerms().size());
ASSERT_EQUALS(0U, q.getNegatedTerms().size());
@@ -90,29 +105,42 @@ TEST(FTSQueryImpl, Phrase1) {
TEST(FTSQueryImpl, Phrase2) {
FTSQueryImpl q;
- ASSERT(q.parse("doing a \"phrase-test\" for fun", "english", false, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("doing a \"phrase-test\" for fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(1U, q.getPositivePhr().size());
ASSERT_EQUALS("phrase-test", q.getPositivePhr()[0]);
}
TEST(FTSQueryImpl, NegPhrase1) {
FTSQueryImpl q;
- ASSERT(
- q.parse("doing a -\"phrase test\" for fun", "english", false, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("doing a -\"phrase test\" for fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS("fun||||||phrase test", q.debugString());
}
TEST(FTSQueryImpl, CaseSensitiveOption) {
FTSQueryImpl q;
- ASSERT(q.parse("this is fun", "english", true, false, TEXT_INDEX_VERSION_3).isOK());
+ q.setQuery("this is fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(true, q.getCaseSensitive());
}
TEST(FTSQueryImpl, CaseSensitivePositiveTerms) {
FTSQueryImpl q;
- ASSERT(q.parse("This is Positively fun", "english", true, false, TEXT_INDEX_VERSION_3).isOK());
+ q.setQuery("This is Positively fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(2U, q.getTermsForBounds().size());
ASSERT_EQUALS(1,
@@ -128,8 +156,11 @@ TEST(FTSQueryImpl, CaseSensitivePositiveTerms) {
TEST(FTSQueryImpl, CaseSensitiveNegativeTerms) {
FTSQueryImpl q;
- ASSERT(q.parse("-This -is -Negatively -miserable", "english", true, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("-This -is -Negatively -miserable");
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(0U, q.getPositiveTerms().size());
ASSERT_EQUALS(0U, q.getTermsForBounds().size());
@@ -142,8 +173,11 @@ TEST(FTSQueryImpl, CaseSensitiveNegativeTerms) {
TEST(FTSQueryImpl, CaseSensitivePositivePhrases) {
FTSQueryImpl q;
- ASSERT(q.parse("doing a \"Phrase Test\" for fun", "english", true, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("doing a \"Phrase Test\" for fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(1U, q.getPositivePhr().size());
ASSERT_EQUALS(0U, q.getNegatedPhr().size());
@@ -152,8 +186,11 @@ TEST(FTSQueryImpl, CaseSensitivePositivePhrases) {
TEST(FTSQueryImpl, CaseSensitiveNegativePhrases) {
FTSQueryImpl q;
- ASSERT(q.parse("doing a -\"Phrase Test\" for fun", "english", true, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("doing a -\"Phrase Test\" for fun");
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(0U, q.getPositivePhr().size());
ASSERT_EQUALS(1U, q.getNegatedPhr().size());
@@ -162,17 +199,34 @@ TEST(FTSQueryImpl, CaseSensitiveNegativePhrases) {
TEST(FTSQueryImpl, Mix1) {
FTSQueryImpl q;
- ASSERT(
- q.parse("\"industry\" -Melbourne -Physics", "english", false, false, TEXT_INDEX_VERSION_3)
- .isOK());
+ q.setQuery("\"industry\" -Melbourne -Physics");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS("industri||melbourn|physic||industry||", q.debugString());
}
TEST(FTSQueryImpl, NegPhrase2) {
FTSQueryImpl q1, q2, q3;
- ASSERT(q1.parse("foo \"bar\"", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
- ASSERT(q2.parse("foo \"-bar\"", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
- ASSERT(q3.parse("foo \" -bar\"", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
+
+ q1.setQuery("foo \"bar\"");
+ q1.setLanguage("english");
+ q1.setCaseSensitive(false);
+ q1.setDiacriticSensitive(false);
+ ASSERT(q1.parse(TEXT_INDEX_VERSION_3).isOK());
+
+ q2.setQuery("foo \"-bar\"");
+ q2.setLanguage("english");
+ q2.setCaseSensitive(false);
+ q2.setDiacriticSensitive(false);
+ ASSERT(q2.parse(TEXT_INDEX_VERSION_3).isOK());
+
+ q3.setQuery("foo \" -bar\"");
+ q3.setLanguage("english");
+ q3.setCaseSensitive(false);
+ q3.setDiacriticSensitive(false);
+ ASSERT(q3.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(2U, q1.getPositiveTerms().size());
ASSERT_EQUALS(2U, q2.getPositiveTerms().size());
@@ -193,9 +247,24 @@ TEST(FTSQueryImpl, NegPhrase2) {
TEST(FTSQueryImpl, NegPhrase3) {
FTSQueryImpl q1, q2, q3;
- ASSERT(q1.parse("foo -\"bar\"", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
- ASSERT(q2.parse("foo -\"-bar\"", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
- ASSERT(q3.parse("foo -\" -bar\"", "english", false, false, TEXT_INDEX_VERSION_3).isOK());
+
+ q1.setQuery("foo -\"bar\"");
+ q1.setLanguage("english");
+ q1.setCaseSensitive(false);
+ q1.setDiacriticSensitive(false);
+ ASSERT(q1.parse(TEXT_INDEX_VERSION_3).isOK());
+
+ q2.setQuery("foo -\"-bar\"");
+ q2.setLanguage("english");
+ q2.setCaseSensitive(false);
+ q2.setDiacriticSensitive(false);
+ ASSERT(q2.parse(TEXT_INDEX_VERSION_3).isOK());
+
+ q3.setQuery("foo -\" -bar\"");
+ q3.setLanguage("english");
+ q3.setCaseSensitive(false);
+ q3.setDiacriticSensitive(false);
+ ASSERT(q3.parse(TEXT_INDEX_VERSION_3).isOK());
ASSERT_EQUALS(1U, q1.getPositiveTerms().size());
ASSERT_EQUALS(1U, q2.getPositiveTerms().size());
@@ -218,7 +287,11 @@ TEST(FTSQueryImpl, NegPhrase3) {
// stemmer and stopword list.
TEST(FTSQueryImpl, TextIndexVersion1LanguageEnglish) {
FTSQueryImpl q;
- ASSERT(q.parse("the running", "english", false, false, TEXT_INDEX_VERSION_1).isOK());
+ q.setQuery("the running");
+ q.setLanguage("english");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_1).isOK());
ASSERT_EQUALS(1U, q.getPositiveTerms().size());
ASSERT_EQUALS("run", *q.getPositiveTerms().begin());
ASSERT_EQUALS(0U, q.getNegatedTerms().size());
@@ -230,7 +303,11 @@ TEST(FTSQueryImpl, TextIndexVersion1LanguageEnglish) {
// no stopword list.
TEST(FTSQueryImpl, TextIndexVersion1LanguageEng) {
FTSQueryImpl q;
- ASSERT(q.parse("the running", "eng", false, false, TEXT_INDEX_VERSION_1).isOK());
+ q.setQuery("the running");
+ q.setLanguage("eng");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_1).isOK());
ASSERT_EQUALS(2U, q.getPositiveTerms().size());
ASSERT_EQUALS(1, std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "the"));
ASSERT_EQUALS(1, std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "run"));
@@ -243,7 +320,11 @@ TEST(FTSQueryImpl, TextIndexVersion1LanguageEng) {
// and no stopword list will be used.
TEST(FTSQueryImpl, TextIndexVersion1LanguageInvalid) {
FTSQueryImpl q;
- ASSERT(q.parse("the running", "invalid", false, false, TEXT_INDEX_VERSION_1).isOK());
+ q.setQuery("the running");
+ q.setLanguage("invalid");
+ q.setCaseSensitive(false);
+ q.setDiacriticSensitive(false);
+ ASSERT(q.parse(TEXT_INDEX_VERSION_1).isOK());
ASSERT_EQUALS(2U, q.getPositiveTerms().size());
ASSERT_EQUALS(1, std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "the"));
ASSERT_EQUALS(1,
@@ -252,5 +333,45 @@ TEST(FTSQueryImpl, TextIndexVersion1LanguageInvalid) {
ASSERT_EQUALS(0U, q.getPositivePhr().size());
ASSERT_EQUALS(0U, q.getNegatedPhr().size());
}
+
+TEST(FTSQueryImpl, CloneUnparsedQuery) {
+ FTSQueryImpl q;
+ q.setQuery("foo");
+ q.setLanguage("bar");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(true);
+
+ auto clone = q.clone();
+ ASSERT_EQUALS(clone->getQuery(), q.getQuery());
+ ASSERT_EQUALS(clone->getLanguage(), q.getLanguage());
+ ASSERT_EQUALS(clone->getCaseSensitive(), q.getCaseSensitive());
+ ASSERT_EQUALS(clone->getDiacriticSensitive(), q.getDiacriticSensitive());
+}
+
+TEST(FTSQueryImpl, CloneParsedQuery) {
+ FTSQueryImpl q;
+ q.setQuery("Foo -bar \"baz\" -\"quux\"");
+ q.setLanguage("english");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(true);
+ ASSERT_OK(q.parse(TEXT_INDEX_VERSION_3));
+ ASSERT(std::set<std::string>({"Foo", "baz"}) == q.getPositiveTerms());
+ ASSERT(std::set<std::string>({"bar"}) == q.getNegatedTerms());
+ ASSERT(std::vector<std::string>({"baz"}) == q.getPositivePhr());
+ ASSERT(std::vector<std::string>({"quux"}) == q.getNegatedPhr());
+ ASSERT(std::set<std::string>({"foo", "baz"}) == q.getTermsForBounds());
+
+ auto clone = q.clone();
+ ASSERT_EQUALS(clone->getQuery(), q.getQuery());
+ ASSERT_EQUALS(clone->getLanguage(), q.getLanguage());
+ ASSERT_EQUALS(clone->getCaseSensitive(), q.getCaseSensitive());
+ ASSERT_EQUALS(clone->getDiacriticSensitive(), q.getDiacriticSensitive());
+ FTSQueryImpl* castedClone = static_cast<FTSQueryImpl*>(clone.get());
+ ASSERT(castedClone->getPositiveTerms() == q.getPositiveTerms());
+ ASSERT(castedClone->getNegatedTerms() == q.getNegatedTerms());
+ ASSERT(castedClone->getPositivePhr() == q.getPositivePhr());
+ ASSERT(castedClone->getNegatedPhr() == q.getNegatedPhr());
+ ASSERT(castedClone->getTermsForBounds() == q.getTermsForBounds());
+}
}
}
diff --git a/src/mongo/db/fts/fts_query_noop.cpp b/src/mongo/db/fts/fts_query_noop.cpp
new file mode 100644
index 00000000000..d6da988a3e0
--- /dev/null
+++ b/src/mongo/db/fts/fts_query_noop.cpp
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/fts/fts_query_noop.h"
+
+#include "mongo/stdx/memory.h"
+
+namespace mongo {
+namespace fts {
+
+std::unique_ptr<FTSQuery> FTSQueryNoop::clone() const {
+ auto clonedQuery = stdx::make_unique<FTSQueryNoop>();
+ clonedQuery->setQuery(getQuery());
+ clonedQuery->setLanguage(getLanguage());
+ clonedQuery->setCaseSensitive(getCaseSensitive());
+ clonedQuery->setDiacriticSensitive(getDiacriticSensitive());
+ return std::move(clonedQuery);
+}
+
+} // namespace fts
+} // namespace mongo
diff --git a/src/mongo/db/fts/fts_query_noop.h b/src/mongo/db/fts/fts_query_noop.h
new file mode 100644
index 00000000000..b24a8f572a1
--- /dev/null
+++ b/src/mongo/db/fts/fts_query_noop.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/fts/fts_query.h"
+
+namespace mongo {
+namespace fts {
+
+/**
+ * A no-op implementation of FTSQuery.
+ */
+class FTSQueryNoop final : public FTSQuery {
+public:
+ Status parse(TextIndexVersion textIndexVersion) final {
+ return Status::OK();
+ }
+
+ std::unique_ptr<FTSQuery> clone() const final;
+};
+
+} // namespace fts
+} // namespace mongo
diff --git a/src/mongo/db/fts/fts_query_noop_test.cpp b/src/mongo/db/fts/fts_query_noop_test.cpp
new file mode 100644
index 00000000000..6876893c6e9
--- /dev/null
+++ b/src/mongo/db/fts/fts_query_noop_test.cpp
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/fts/fts_query_noop.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace fts {
+
+TEST(FTSQueryNoop, Parse) {
+ FTSQueryNoop q;
+ ASSERT_OK(q.parse(TEXT_INDEX_VERSION_INVALID));
+}
+
+TEST(FTSQueryNoop, Clone) {
+ FTSQueryNoop q;
+ q.setQuery("foo");
+ q.setLanguage("bar");
+ q.setCaseSensitive(true);
+ q.setDiacriticSensitive(true);
+
+ auto clone = q.clone();
+ ASSERT_EQUALS(clone->getQuery(), q.getQuery());
+ ASSERT_EQUALS(clone->getLanguage(), q.getLanguage());
+ ASSERT_EQUALS(clone->getCaseSensitive(), q.getCaseSensitive());
+ ASSERT_EQUALS(clone->getDiacriticSensitive(), q.getDiacriticSensitive());
+}
+
+} // namespace fts
+} // namespace mongo
diff --git a/src/mongo/db/fts/fts_util.h b/src/mongo/db/fts/fts_util.h
index b9fed70a8e8..7286e6fc7a6 100644
--- a/src/mongo/db/fts/fts_util.h
+++ b/src/mongo/db/fts/fts_util.h
@@ -42,9 +42,10 @@ extern const std::string WILDCARD;
extern const std::string INDEX_NAME;
enum TextIndexVersion {
- TEXT_INDEX_VERSION_1 = 1, // Legacy index format. Deprecated.
- TEXT_INDEX_VERSION_2 = 2, // Index format with ASCII support and murmur hashing.
- TEXT_INDEX_VERSION_3 = 3, // Current index format with basic Unicode support.
+ TEXT_INDEX_VERSION_INVALID = 0, // Invalid value.
+ TEXT_INDEX_VERSION_1 = 1, // Legacy index format. Deprecated.
+ TEXT_INDEX_VERSION_2 = 2, // Index format with ASCII support and murmur hashing.
+ TEXT_INDEX_VERSION_3 = 3, // Current index format with basic Unicode support.
};
}
}
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index af686b9f96b..d8c4fbd8299 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -47,6 +47,7 @@ env.Library(
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/bson/util/bson_extract',
'$BUILD_DIR/mongo/db/common',
+ '$BUILD_DIR/mongo/db/fts/fts_query_noop',
'$BUILD_DIR/third_party/shim_pcrecpp',
'path',
],
@@ -123,26 +124,19 @@ env.CppUnitTest(
)
env.Library(
- target='expressions_text',
- source=[
- 'expression_text.cpp',
- ],
- LIBDEPS=[
- '$BUILD_DIR/mongo/db/fts/base',
- 'expressions',
- ],
-)
-
-env.Library(
target='expressions_mongod_only',
source=[
'extensions_callback_real.cpp',
+ 'expression_text.cpp',
'expression_where.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/db/auth/authorization_manager_global',
'$BUILD_DIR/mongo/scripting/scripting_server',
'expressions',
- 'expressions_text',
+ ],
+ LIBDEPS_TAGS=[
+ # Depends on symbols from serverOnlyFiles.
+ 'incomplete',
],
)
diff --git a/src/mongo/db/matcher/expression_text.cpp b/src/mongo/db/matcher/expression_text.cpp
index 8ee279efc1d..b97c2ce2780 100644
--- a/src/mongo/db/matcher/expression_text.cpp
+++ b/src/mongo/db/matcher/expression_text.cpp
@@ -32,34 +32,76 @@
#include "mongo/db/matcher/expression_text.h"
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/db_raii.h"
#include "mongo/db/fts/fts_language.h"
+#include "mongo/db/fts/fts_spec.h"
+#include "mongo/db/index/fts_access_method.h"
#include "mongo/stdx/memory.h"
namespace mongo {
-TextMatchExpression::TextMatchExpression(TextParams params)
- : TextMatchExpressionBase(std::move(params)) {}
+Status TextMatchExpression::init(OperationContext* txn,
+ const NamespaceString& nss,
+ TextParams params) {
+ _ftsQuery.setQuery(std::move(params.query));
+ _ftsQuery.setLanguage(std::move(params.language));
+ _ftsQuery.setCaseSensitive(params.caseSensitive);
+ _ftsQuery.setDiacriticSensitive(params.diacriticSensitive);
-Status TextMatchExpression::init() {
- // Validate language, but defer construction of FTSQueryImpl (which requires access to the
- // target namespace) until stage building time.
- if (!getLanguage().empty()) {
- if (!fts::FTSLanguage::make(getLanguage(), fts::TEXT_INDEX_VERSION_2).isOK()) {
- return {ErrorCodes::BadValue, "$language specifies unsupported language"};
+ fts::TextIndexVersion version;
+ {
+ // Find text index.
+ ScopedTransaction transaction(txn, MODE_IS);
+ AutoGetDb autoDb(txn, nss.db(), MODE_IS);
+ Lock::CollectionLock collLock(txn->lockState(), nss.ns(), MODE_IS);
+ Database* db = autoDb.getDb();
+ if (!db) {
+ return {ErrorCodes::IndexNotFound,
+ str::stream() << "text index required for $text query (no such collection '"
+ << nss.ns() << "')"};
}
+ Collection* collection = db->getCollection(nss);
+ if (!collection) {
+ return {ErrorCodes::IndexNotFound,
+ str::stream() << "text index required for $text query (no such collection '"
+ << nss.ns() << "')"};
+ }
+ std::vector<IndexDescriptor*> idxMatches;
+ collection->getIndexCatalog()->findIndexByType(txn, IndexNames::TEXT, idxMatches);
+ if (idxMatches.empty()) {
+ return {ErrorCodes::IndexNotFound, "text index required for $text query"};
+ }
+ if (idxMatches.size() > 1) {
+ return {ErrorCodes::IndexNotFound, "more than one text index found for $text query"};
+ }
+ invariant(idxMatches.size() == 1);
+ IndexDescriptor* index = idxMatches[0];
+ const FTSAccessMethod* fam =
+ static_cast<FTSAccessMethod*>(collection->getIndexCatalog()->getIndex(index));
+ invariant(fam);
+
+ // Extract version and default language from text index.
+ version = fam->getSpec().getTextIndexVersion();
+ if (_ftsQuery.getLanguage().empty()) {
+ _ftsQuery.setLanguage(fam->getSpec().defaultLanguage().str());
+ }
+ }
+
+ Status parseStatus = _ftsQuery.parse(version);
+ if (!parseStatus.isOK()) {
+ return parseStatus;
}
return initPath("_fts");
}
std::unique_ptr<MatchExpression> TextMatchExpression::shallowClone() const {
- TextParams params;
- params.query = getQuery();
- params.language = getLanguage();
- params.caseSensitive = getCaseSensitive();
- params.diacriticSensitive = getDiacriticSensitive();
- auto expr = stdx::make_unique<TextMatchExpression>(std::move(params));
- expr->init();
+ auto expr = stdx::make_unique<TextMatchExpression>();
+ // We initialize _ftsQuery here directly rather than calling init(), to avoid needing to examine
+ // the index catalog.
+ expr->_ftsQuery = _ftsQuery;
+ invariantOK(expr->initPath("_fts"));
if (getTag()) {
expr->setTag(getTag()->clone());
}
diff --git a/src/mongo/db/matcher/expression_text.h b/src/mongo/db/matcher/expression_text.h
index dfe4cbd8204..3bd44b29da3 100644
--- a/src/mongo/db/matcher/expression_text.h
+++ b/src/mongo/db/matcher/expression_text.h
@@ -30,17 +30,27 @@
#pragma once
+#include "mongo/db/fts/fts_query_impl.h"
#include "mongo/db/matcher/expression_text_base.h"
+#include "mongo/db/namespace_string.h"
namespace mongo {
+class NamespaceString;
+class OperationContext;
+
class TextMatchExpression : public TextMatchExpressionBase {
public:
- TextMatchExpression(TextParams params);
+ Status init(OperationContext* txn, const NamespaceString& nss, TextParams params);
- Status init();
+ const fts::FTSQuery& getFTSQuery() const final {
+ return _ftsQuery;
+ }
std::unique_ptr<MatchExpression> shallowClone() const final;
+
+private:
+ fts::FTSQueryImpl _ftsQuery;
};
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_text_base.cpp b/src/mongo/db/matcher/expression_text_base.cpp
index 04b62ecd6b1..82fa7f9fd3e 100644
--- a/src/mongo/db/matcher/expression_text_base.cpp
+++ b/src/mongo/db/matcher/expression_text_base.cpp
@@ -30,23 +30,21 @@
#include "mongo/db/matcher/expression_text_base.h"
+#include "mongo/db/fts/fts_query.h"
+
namespace mongo {
const bool TextMatchExpressionBase::kCaseSensitiveDefault = false;
const bool TextMatchExpressionBase::kDiacriticSensitiveDefault = false;
-TextMatchExpressionBase::TextMatchExpressionBase(TextParams params)
- : LeafMatchExpression(TEXT),
- _query(std::move(params.query)),
- _language(std::move(params.language)),
- _caseSensitive(params.caseSensitive),
- _diacriticSensitive(params.diacriticSensitive) {}
+TextMatchExpressionBase::TextMatchExpressionBase() : LeafMatchExpression(TEXT) {}
void TextMatchExpressionBase::debugString(StringBuilder& debug, int level) const {
+ const fts::FTSQuery& ftsQuery = getFTSQuery();
_debugAddSpace(debug, level);
- debug << "TEXT : query=" << _query << ", language=" << _language
- << ", caseSensitive=" << _caseSensitive << ", diacriticSensitive=" << _diacriticSensitive
- << ", tag=";
+ debug << "TEXT : query=" << ftsQuery.getQuery() << ", language=" << ftsQuery.getLanguage()
+ << ", caseSensitive=" << ftsQuery.getCaseSensitive()
+ << ", diacriticSensitive=" << ftsQuery.getDiacriticSensitive() << ", tag=";
MatchExpression::TagData* td = getTag();
if (NULL != td) {
td->debugString(&debug);
@@ -57,9 +55,11 @@ void TextMatchExpressionBase::debugString(StringBuilder& debug, int level) const
}
void TextMatchExpressionBase::toBSON(BSONObjBuilder* out) const {
+ const fts::FTSQuery& ftsQuery = getFTSQuery();
out->append("$text",
- BSON("$search" << _query << "$language" << _language << "$caseSensitive"
- << _caseSensitive << "$diacriticSensitive" << _diacriticSensitive));
+ BSON("$search" << ftsQuery.getQuery() << "$language" << ftsQuery.getLanguage()
+ << "$caseSensitive" << ftsQuery.getCaseSensitive()
+ << "$diacriticSensitive" << ftsQuery.getDiacriticSensitive()));
}
bool TextMatchExpressionBase::equivalent(const MatchExpression* other) const {
@@ -68,19 +68,7 @@ bool TextMatchExpressionBase::equivalent(const MatchExpression* other) const {
}
const TextMatchExpressionBase* realOther = static_cast<const TextMatchExpressionBase*>(other);
- if (realOther->getQuery() != _query) {
- return false;
- }
- if (realOther->getLanguage() != _language) {
- return false;
- }
- if (realOther->getCaseSensitive() != _caseSensitive) {
- return false;
- }
- if (realOther->getDiacriticSensitive() != _diacriticSensitive) {
- return false;
- }
- return true;
+ return getFTSQuery().equivalent(realOther->getFTSQuery());
}
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h
index 8ca25cf60eb..187a91bb2d4 100644
--- a/src/mongo/db/matcher/expression_text_base.h
+++ b/src/mongo/db/matcher/expression_text_base.h
@@ -32,6 +32,10 @@
namespace mongo {
+namespace fts {
+class FTSQuery;
+} // namespace fts
+
/**
* Common base class for $text match expression implementations.
*/
@@ -47,23 +51,12 @@ public:
static const bool kCaseSensitiveDefault;
static const bool kDiacriticSensitiveDefault;
- TextMatchExpressionBase(TextParams params);
+ TextMatchExpressionBase();
- const std::string& getQuery() const {
- return _query;
- }
-
- const std::string& getLanguage() const {
- return _language;
- }
-
- bool getCaseSensitive() const {
- return _caseSensitive;
- }
-
- bool getDiacriticSensitive() const {
- return _diacriticSensitive;
- }
+ /**
+ * Returns a reference to the parsed text query that this TextMatchExpressionBase owns.
+ */
+ virtual const fts::FTSQuery& getFTSQuery() const = 0;
//
// Methods inherited from MatchExpression.
@@ -89,12 +82,6 @@ public:
void toBSON(BSONObjBuilder* out) const final;
bool equivalent(const MatchExpression* other) const final;
-
-private:
- const std::string _query;
- const std::string _language;
- const bool _caseSensitive;
- const bool _diacriticSensitive;
};
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_text_noop.cpp b/src/mongo/db/matcher/expression_text_noop.cpp
index 7d2fb5c84b7..bfd61a54081 100644
--- a/src/mongo/db/matcher/expression_text_noop.cpp
+++ b/src/mongo/db/matcher/expression_text_noop.cpp
@@ -34,21 +34,24 @@
namespace mongo {
-TextNoOpMatchExpression::TextNoOpMatchExpression(TextParams params)
- : TextMatchExpressionBase(std::move(params)) {}
-
-Status TextNoOpMatchExpression::init() {
+Status TextNoOpMatchExpression::init(TextParams params) {
+ _ftsQuery.setQuery(std::move(params.query));
+ _ftsQuery.setLanguage(std::move(params.language));
+ _ftsQuery.setCaseSensitive(params.caseSensitive);
+ _ftsQuery.setDiacriticSensitive(params.diacriticSensitive);
+ invariantOK(_ftsQuery.parse(fts::TEXT_INDEX_VERSION_INVALID));
return initPath("_fts");
}
std::unique_ptr<MatchExpression> TextNoOpMatchExpression::shallowClone() const {
TextParams params;
- params.query = getQuery();
- params.language = getLanguage();
- params.caseSensitive = getCaseSensitive();
- params.diacriticSensitive = getDiacriticSensitive();
- auto expr = stdx::make_unique<TextNoOpMatchExpression>(std::move(params));
- expr->init();
+ params.query = _ftsQuery.getQuery();
+ params.language = _ftsQuery.getLanguage();
+ params.caseSensitive = _ftsQuery.getCaseSensitive();
+ params.diacriticSensitive = _ftsQuery.getDiacriticSensitive();
+
+ auto expr = stdx::make_unique<TextNoOpMatchExpression>();
+ invariantOK(expr->init(std::move(params)));
if (getTag()) {
expr->setTag(getTag()->clone());
}
diff --git a/src/mongo/db/matcher/expression_text_noop.h b/src/mongo/db/matcher/expression_text_noop.h
index de02bcfa3cf..8e01d40e70b 100644
--- a/src/mongo/db/matcher/expression_text_noop.h
+++ b/src/mongo/db/matcher/expression_text_noop.h
@@ -28,17 +28,23 @@
#pragma once
+#include "mongo/db/fts/fts_query_noop.h"
#include "mongo/db/matcher/expression_text_base.h"
namespace mongo {
class TextNoOpMatchExpression : public TextMatchExpressionBase {
public:
- TextNoOpMatchExpression(TextParams params);
+ Status init(TextParams params);
- Status init();
+ const fts::FTSQuery& getFTSQuery() const final {
+ return _ftsQuery;
+ }
std::unique_ptr<MatchExpression> shallowClone() const final;
+
+private:
+ fts::FTSQueryNoop _ftsQuery;
};
} // namespace mongo
diff --git a/src/mongo/db/matcher/extensions_callback_noop.cpp b/src/mongo/db/matcher/extensions_callback_noop.cpp
index e5941f73562..10026370e64 100644
--- a/src/mongo/db/matcher/extensions_callback_noop.cpp
+++ b/src/mongo/db/matcher/extensions_callback_noop.cpp
@@ -41,8 +41,8 @@ StatusWithMatchExpression ExtensionsCallbackNoop::parseText(BSONElement text) co
return textParams.getStatus();
}
- auto expr = stdx::make_unique<TextNoOpMatchExpression>(std::move(textParams.getValue()));
- Status initStatus = expr->init();
+ auto expr = stdx::make_unique<TextNoOpMatchExpression>();
+ Status initStatus = expr->init(std::move(textParams.getValue()));
if (!initStatus.isOK()) {
return initStatus;
}
diff --git a/src/mongo/db/matcher/extensions_callback_real.cpp b/src/mongo/db/matcher/extensions_callback_real.cpp
index 161e0fb2e79..0b7e815ca58 100644
--- a/src/mongo/db/matcher/extensions_callback_real.cpp
+++ b/src/mongo/db/matcher/extensions_callback_real.cpp
@@ -45,8 +45,8 @@ StatusWithMatchExpression ExtensionsCallbackReal::parseText(BSONElement text) co
return textParams.getStatus();
}
- auto exp = stdx::make_unique<TextMatchExpression>(std::move(textParams.getValue()));
- Status status = exp->init();
+ auto exp = stdx::make_unique<TextMatchExpression>();
+ Status status = exp->init(_txn, *_nss, std::move(textParams.getValue()));
if (!status.isOK()) {
return status;
}
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index d25d0a0a909..cc4fcec81c6 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -26,7 +26,6 @@ env.Library(
"$BUILD_DIR/mongo/db/index/expression_params",
"$BUILD_DIR/mongo/db/matcher/expression_algo",
"$BUILD_DIR/mongo/db/matcher/expressions",
- "$BUILD_DIR/mongo/db/matcher/expressions_text",
"$BUILD_DIR/mongo/db/index_names",
"$BUILD_DIR/mongo/db/server_parameters",
"command_request_response",
@@ -199,7 +198,6 @@ env.CppUnitTest(
],
LIBDEPS=[
"query_planner",
- "$BUILD_DIR/mongo/db/matcher/expressions_text",
],
)
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index 72f6fca93ca..54bcc0b2311 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -194,13 +194,10 @@ QuerySolutionNode* QueryPlannerAccess::makeLeafNode(
} else if (MatchExpression::TEXT == expr->matchType()) {
// We must not keep the expression node around.
*tightnessOut = IndexBoundsBuilder::EXACT;
- TextMatchExpression* textExpr = static_cast<TextMatchExpression*>(expr);
+ TextMatchExpressionBase* textExpr = static_cast<TextMatchExpressionBase*>(expr);
TextNode* ret = new TextNode();
ret->indexKeyPattern = index.keyPattern;
- ret->query = textExpr->getQuery();
- ret->language = textExpr->getLanguage();
- ret->caseSensitive = textExpr->getCaseSensitive();
- ret->diacriticSensitive = textExpr->getDiacriticSensitive();
+ ret->ftsQuery = textExpr->getFTSQuery().clone();
return ret;
} else {
// Note that indexKeyPattern.firstElement().fieldName() may not equal expr->path()
diff --git a/src/mongo/db/query/planner_ixselect.h b/src/mongo/db/query/planner_ixselect.h
index b782e6d8560..2928b708677 100644
--- a/src/mongo/db/query/planner_ixselect.h
+++ b/src/mongo/db/query/planner_ixselect.h
@@ -31,6 +31,7 @@
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/index_entry.h"
#include "mongo/db/query/query_solution.h"
+#include "mongo/platform/unordered_set.h"
namespace mongo {
diff --git a/src/mongo/db/query/query_planner_test_lib.cpp b/src/mongo/db/query/query_planner_test_lib.cpp
index 774b00368a9..557bde5f252 100644
--- a/src/mongo/db/query/query_planner_test_lib.cpp
+++ b/src/mongo/db/query/query_planner_test_lib.cpp
@@ -297,28 +297,28 @@ bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln,
BSONElement searchElt = textObj["search"];
if (!searchElt.eoo()) {
- if (searchElt.String() != node->query) {
+ if (searchElt.String() != node->ftsQuery->getQuery()) {
return false;
}
}
BSONElement languageElt = textObj["language"];
if (!languageElt.eoo()) {
- if (languageElt.String() != node->language) {
+ if (languageElt.String() != node->ftsQuery->getLanguage()) {
return false;
}
}
BSONElement caseSensitiveElt = textObj["caseSensitive"];
if (!caseSensitiveElt.eoo()) {
- if (caseSensitiveElt.trueValue() != node->caseSensitive) {
+ if (caseSensitiveElt.trueValue() != node->ftsQuery->getCaseSensitive()) {
return false;
}
}
BSONElement diacriticSensitiveElt = textObj["diacriticSensitive"];
if (!diacriticSensitiveElt.eoo()) {
- if (diacriticSensitiveElt.trueValue() != node->diacriticSensitive) {
+ if (diacriticSensitiveElt.trueValue() != node->ftsQuery->getDiacriticSensitive()) {
return false;
}
}
diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp
index 345a4132b0c..a80f9199b33 100644
--- a/src/mongo/db/query/query_solution.cpp
+++ b/src/mongo/db/query/query_solution.cpp
@@ -73,13 +73,13 @@ void TextNode::appendToString(mongoutils::str::stream* ss, int indent) const {
addIndent(ss, indent + 1);
*ss << "keyPattern = " << indexKeyPattern.toString() << '\n';
addIndent(ss, indent + 1);
- *ss << "query = " << query << '\n';
+ *ss << "query = " << ftsQuery->getQuery() << '\n';
addIndent(ss, indent + 1);
- *ss << "language = " << language << '\n';
+ *ss << "language = " << ftsQuery->getLanguage() << '\n';
addIndent(ss, indent + 1);
- *ss << "caseSensitive= " << caseSensitive << '\n';
+ *ss << "caseSensitive= " << ftsQuery->getCaseSensitive() << '\n';
addIndent(ss, indent + 1);
- *ss << "diacriticSensitive= " << diacriticSensitive << '\n';
+ *ss << "diacriticSensitive= " << ftsQuery->getDiacriticSensitive() << '\n';
addIndent(ss, indent + 1);
*ss << "indexPrefix = " << indexPrefix.toString() << '\n';
if (NULL != filter) {
@@ -95,10 +95,7 @@ QuerySolutionNode* TextNode::clone() const {
copy->_sort = this->_sort;
copy->indexKeyPattern = this->indexKeyPattern;
- copy->query = this->query;
- copy->language = this->language;
- copy->caseSensitive = this->caseSensitive;
- copy->diacriticSensitive = this->diacriticSensitive;
+ copy->ftsQuery = this->ftsQuery->clone();
copy->indexPrefix = this->indexPrefix;
return copy;
diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h
index e3fd9d9dd3c..4df3e3e8665 100644
--- a/src/mongo/db/query/query_solution.h
+++ b/src/mongo/db/query/query_solution.h
@@ -32,15 +32,13 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/expression.h"
-#include "mongo/db/fts/fts_query_impl.h"
+#include "mongo/db/fts/fts_query.h"
#include "mongo/db/query/index_bounds.h"
#include "mongo/db/query/plan_cache.h"
#include "mongo/db/query/stage_types.h"
namespace mongo {
-using mongo::fts::FTSQueryImpl;
-
class GeoNearExpression;
/**
@@ -247,10 +245,7 @@ struct TextNode : public QuerySolutionNode {
BSONObjSet _sort;
BSONObj indexKeyPattern;
- std::string query;
- std::string language;
- bool caseSensitive;
- bool diacriticSensitive;
+ std::unique_ptr<fts::FTSQuery> ftsQuery;
// "Prefix" fields of a text index can handle equality predicates. We group them with the
// text node while creating the text leaf node and convert them into a BSONObj index prefix
diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp
index d23c16ad38b..f3344921e6b 100644
--- a/src/mongo/db/query/stage_builder.cpp
+++ b/src/mongo/db/query/stage_builder.cpp
@@ -256,40 +256,22 @@ PlanStage* buildStages(OperationContext* txn,
return new GeoNear2DSphereStage(params, txn, ws, collection, s2Index);
} else if (STAGE_TEXT == root->getType()) {
const TextNode* node = static_cast<const TextNode*>(root);
-
- if (NULL == collection) {
- warning() << "Null collection for text";
- return NULL;
- }
- vector<IndexDescriptor*> idxMatches;
- collection->getIndexCatalog()->findIndexByType(txn, "text", idxMatches);
- if (1 != idxMatches.size()) {
- warning() << "No text index, or more than one text index";
- return NULL;
- }
- IndexDescriptor* index = idxMatches[0];
+ IndexDescriptor* desc =
+ collection->getIndexCatalog()->findIndexByKeyPattern(txn, node->indexKeyPattern);
+ invariant(desc);
const FTSAccessMethod* fam =
- static_cast<FTSAccessMethod*>(collection->getIndexCatalog()->getIndex(index));
- TextStageParams params(fam->getSpec());
+ static_cast<FTSAccessMethod*>(collection->getIndexCatalog()->getIndex(desc));
+ invariant(fam);
- // params.collection = collection;
- params.index = index;
+ TextStageParams params(fam->getSpec());
+ params.index = desc;
params.spec = fam->getSpec();
params.indexPrefix = node->indexPrefix;
-
- const std::string& language =
- ("" == node->language ? fam->getSpec().defaultLanguage().str() : node->language);
-
- Status parseStatus = params.query.parse(node->query,
- language,
- node->caseSensitive,
- node->diacriticSensitive,
- fam->getSpec().getTextIndexVersion());
- if (!parseStatus.isOK()) {
- warning() << "Can't parse text search query";
- return NULL;
- }
-
+ // We assume here that node->ftsQuery is an FTSQueryImpl, not an FTSQueryNoop. In practice,
+ // this means that it is illegal to use the StageBuilder on a QuerySolution created by
+ // planning a query that contains "no-op" expressions. TODO: make StageBuilder::build()
+ // fail in this case (this improvement is being tracked by SERVER-21510).
+ params.query = static_cast<FTSQueryImpl&>(*node->ftsQuery);
return new TextStage(txn, params, ws, node->filter.get());
} else if (STAGE_SHARDING_FILTER == root->getType()) {
const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(root);
diff --git a/src/mongo/dbtests/extensions_callback_real_test.cpp b/src/mongo/dbtests/extensions_callback_real_test.cpp
index b4c2291810f..56465cb2c7e 100644
--- a/src/mongo/dbtests/extensions_callback_real_test.cpp
+++ b/src/mongo/dbtests/extensions_callback_real_test.cpp
@@ -28,196 +28,280 @@
#include "mongo/platform/basic.h"
+#include "mongo/db/db_raii.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression_text.h"
#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context_impl.h"
+#include "mongo/dbtests/dbtests.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
namespace {
-const NamespaceString nss("unittests.extensions_callback_real_test");
-
//
// $text parsing tests.
//
-TEST(ExtensionsCallbackReal, TextBasic) {
- OperationContextImpl txn;
+class ExtensionsCallbackRealTest : public unittest::Test {
+public:
+ ExtensionsCallbackRealTest() : _nss("unittests.extensions_callback_real_test") {}
+
+ void setUp() final {
+ AutoGetOrCreateDb autoDb(&_txn, _nss.db(), MODE_X);
+ Database* database = autoDb.getDb();
+ {
+ WriteUnitOfWork wunit(&_txn);
+ ASSERT(database->createCollection(&_txn, _nss.ns()));
+ wunit.commit();
+ }
+ }
+
+ void tearDown() final {
+ AutoGetDb autoDb(&_txn, _nss.db(), MODE_X);
+ Database* database = autoDb.getDb();
+ if (!database) {
+ return;
+ }
+ {
+ WriteUnitOfWork wunit(&_txn);
+ static_cast<void>(database->dropCollection(&_txn, _nss.ns()));
+ wunit.commit();
+ }
+ }
+
+protected:
+ OperationContextImpl _txn;
+ const NamespaceString _nss;
+};
+
+TEST_F(ExtensionsCallbackRealTest, TextNoIndex) {
+ BSONObj query = fromjson("{$text: {$search:\"awesome\"}}");
+ StatusWithMatchExpression result =
+ ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement());
+
+ ASSERT_NOT_OK(result.getStatus());
+ ASSERT_EQ(ErrorCodes::IndexNotFound, result.getStatus());
+}
+
+TEST_F(ExtensionsCallbackRealTest, TextBasic) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text: {$search:\"awesome\", $language:\"english\"}}");
auto expr =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement()));
ASSERT_EQUALS(MatchExpression::TEXT, expr->matchType());
std::unique_ptr<TextMatchExpression> textExpr(
static_cast<TextMatchExpression*>(expr.release()));
- ASSERT_EQUALS(textExpr->getQuery(), "awesome");
- ASSERT_EQUALS(textExpr->getLanguage(), "english");
- ASSERT_EQUALS(textExpr->getCaseSensitive(), TextMatchExpressionBase::kCaseSensitiveDefault);
- ASSERT_EQUALS(textExpr->getDiacriticSensitive(),
+ ASSERT_EQUALS(textExpr->getFTSQuery().getQuery(), "awesome");
+ ASSERT_EQUALS(textExpr->getFTSQuery().getLanguage(), "english");
+ ASSERT_EQUALS(textExpr->getFTSQuery().getCaseSensitive(),
+ TextMatchExpressionBase::kCaseSensitiveDefault);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getDiacriticSensitive(),
TextMatchExpressionBase::kDiacriticSensitiveDefault);
}
-TEST(ExtensionsCallbackReal, TextLanguageError) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextLanguageError) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text: {$search:\"awesome\", $language:\"spanglish\"}}");
StatusWithMatchExpression result =
- ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement());
+ ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement());
ASSERT_NOT_OK(result.getStatus());
}
-TEST(ExtensionsCallbackReal, TextCaseSensitiveTrue) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextCaseSensitiveTrue) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text: {$search:\"awesome\", $caseSensitive: true}}");
auto expr =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement()));
ASSERT_EQUALS(MatchExpression::TEXT, expr->matchType());
std::unique_ptr<TextMatchExpression> textExpr(
static_cast<TextMatchExpression*>(expr.release()));
- ASSERT_EQUALS(textExpr->getCaseSensitive(), true);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getCaseSensitive(), true);
}
-TEST(ExtensionsCallbackReal, TextCaseSensitiveFalse) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextCaseSensitiveFalse) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text: {$search:\"awesome\", $caseSensitive: false}}");
auto expr =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement()));
ASSERT_EQUALS(MatchExpression::TEXT, expr->matchType());
std::unique_ptr<TextMatchExpression> textExpr(
static_cast<TextMatchExpression*>(expr.release()));
- ASSERT_EQUALS(textExpr->getCaseSensitive(), false);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getCaseSensitive(), false);
}
-TEST(ExtensionsCallbackReal, TextCaseSensitiveError) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextCaseSensitiveError) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text:{$search:\"awesome\", $caseSensitive: 0}}");
StatusWithMatchExpression result =
- ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement());
+ ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement());
ASSERT_NOT_OK(result.getStatus());
}
-TEST(ExtensionsCallbackReal, TextDiacriticSensitiveTrue) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextDiacriticSensitiveTrue) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text: {$search:\"awesome\", $diacriticSensitive: true}}");
auto expr =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement()));
ASSERT_EQUALS(MatchExpression::TEXT, expr->matchType());
std::unique_ptr<TextMatchExpression> textExpr(
static_cast<TextMatchExpression*>(expr.release()));
- ASSERT_EQUALS(textExpr->getDiacriticSensitive(), true);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getDiacriticSensitive(), true);
}
-TEST(ExtensionsCallbackReal, TextDiacriticSensitiveFalse) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextDiacriticSensitiveFalse) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text: {$search:\"awesome\", $diacriticSensitive: false}}");
auto expr =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement()));
ASSERT_EQUALS(MatchExpression::TEXT, expr->matchType());
std::unique_ptr<TextMatchExpression> textExpr(
static_cast<TextMatchExpression*>(expr.release()));
- ASSERT_EQUALS(textExpr->getDiacriticSensitive(), false);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getDiacriticSensitive(), false);
}
-TEST(ExtensionsCallbackReal, TextDiacriticSensitiveError) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextDiacriticSensitiveError) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query = fromjson("{$text:{$search:\"awesome\", $diacriticSensitive: 0}}");
StatusWithMatchExpression result =
- ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement());
+ ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement());
ASSERT_NOT_OK(result.getStatus());
}
-TEST(ExtensionsCallbackReal, TextDiacriticSensitiveAndCaseSensitiveTrue) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, TextDiacriticSensitiveAndCaseSensitiveTrue) {
+ ASSERT_OK(dbtests::createIndex(&_txn,
+ _nss.ns(),
+ BSON("a"
+ << "text"),
+ false)); // isUnique
+
BSONObj query =
fromjson("{$text: {$search:\"awesome\", $diacriticSensitive: true, $caseSensitive: true}}");
auto expr =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseText(query.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseText(query.firstElement()));
ASSERT_EQUALS(MatchExpression::TEXT, expr->matchType());
std::unique_ptr<TextMatchExpression> textExpr(
static_cast<TextMatchExpression*>(expr.release()));
- ASSERT_EQUALS(textExpr->getDiacriticSensitive(), true);
- ASSERT_EQUALS(textExpr->getCaseSensitive(), true);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getDiacriticSensitive(), true);
+ ASSERT_EQUALS(textExpr->getFTSQuery().getCaseSensitive(), true);
}
//
// $where parsing tests.
//
-TEST(ExtensionsCallbackReal, WhereExpressionsWithSameScopeHaveSameBSONRepresentation) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, WhereExpressionsWithSameScopeHaveSameBSONRepresentation) {
const char code[] = "function(){ return a; }";
BSONObj query1 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true)));
auto expr1 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query1.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query1.firstElement()));
BSONObjBuilder builder1;
expr1->toBSON(&builder1);
BSONObj query2 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true)));
auto expr2 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query2.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query2.firstElement()));
BSONObjBuilder builder2;
expr2->toBSON(&builder2);
ASSERT_EQ(builder1.obj(), builder2.obj());
}
-TEST(ExtensionsCallbackReal, WhereExpressionsWithDifferentScopesHaveDifferentBSONRepresentations) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest,
+ WhereExpressionsWithDifferentScopesHaveDifferentBSONRepresentations) {
const char code[] = "function(){ return a; }";
BSONObj query1 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true)));
auto expr1 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query1.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query1.firstElement()));
BSONObjBuilder builder1;
expr1->toBSON(&builder1);
BSONObj query2 = BSON("$where" << BSONCodeWScope(code, BSON("a" << false)));
auto expr2 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query2.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query2.firstElement()));
BSONObjBuilder builder2;
expr2->toBSON(&builder2);
ASSERT_NE(builder1.obj(), builder2.obj());
}
-TEST(ExtensionsCallbackReal, WhereExpressionsWithSameScopeAreEquivalent) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, WhereExpressionsWithSameScopeAreEquivalent) {
const char code[] = "function(){ return a; }";
BSONObj query1 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true)));
auto expr1 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query1.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query1.firstElement()));
BSONObj query2 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true)));
auto expr2 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query2.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query2.firstElement()));
ASSERT(expr1->equivalent(expr2.get()));
ASSERT(expr2->equivalent(expr1.get()));
}
-TEST(ExtensionsCallbackReal, WhereExpressionsWithDifferentScopesAreNotEquivalent) {
- OperationContextImpl txn;
+TEST_F(ExtensionsCallbackRealTest, WhereExpressionsWithDifferentScopesAreNotEquivalent) {
const char code[] = "function(){ return a; }";
BSONObj query1 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true)));
auto expr1 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query1.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query1.firstElement()));
BSONObj query2 = BSON("$where" << BSONCodeWScope(code, BSON("a" << false)));
auto expr2 =
- unittest::assertGet(ExtensionsCallbackReal(&txn, &nss).parseWhere(query2.firstElement()));
+ unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query2.firstElement()));
ASSERT_FALSE(expr1->equivalent(expr2.get()));
ASSERT_FALSE(expr2->equivalent(expr1.get()));
diff --git a/src/mongo/s/chunk_manager.cpp b/src/mongo/s/chunk_manager.cpp
index 223ad2b2825..2343924383d 100644
--- a/src/mongo/s/chunk_manager.cpp
+++ b/src/mongo/s/chunk_manager.cpp
@@ -560,10 +560,8 @@ void ChunkManager::getAllShardIds(set<ShardId>* all) const {
IndexBounds ChunkManager::getIndexBoundsForQuery(const BSONObj& key,
const CanonicalQuery& canonicalQuery) {
- // $text is not allowed in planning since we don't have text index on mongos.
- //
- // TODO: Treat $text query as a no-op in planning. So with shard key {a: 1},
- // the query { a: 2, $text: { ... } } will only target to {a: 2}.
+ // TODO: special-casing TEXT here is no longer necessary. The work to remove this special case
+ // is being tracked at SERVER-21511.
if (QueryPlannerCommon::hasNode(canonicalQuery.root(), MatchExpression::TEXT)) {
IndexBounds bounds;
IndexBoundsBuilder::allValuesBounds(key, &bounds); // [minKey, maxKey]