diff options
author | Jason Rassi <rassi@10gen.com> | 2013-10-11 16:53:46 -0700 |
---|---|---|
committer | Jason Rassi <rassi@10gen.com> | 2013-10-11 17:32:19 -0700 |
commit | 34b88976c91fdeec5ff0b8816d35deef72f3767e (patch) | |
tree | b9d1c4f49da0778a7c028c2b82a1838afab3a4a5 /src/mongo | |
parent | a1d5f24d910d1407b8c166d99f0df99640845a2e (diff) | |
download | mongo-34b88976c91fdeec5ff0b8816d35deef72f3767e.tar.gz |
SERVER-9063 Add new query operator $text
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/fts/fts_command.h | 3 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_search.h | 3 | ||||
-rw-r--r-- | src/mongo/db/query/indexability.h | 3 | ||||
-rw-r--r-- | src/mongo/db/query/new_find.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/query/plan_enumerator.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner.cpp | 33 | ||||
-rw-r--r-- | src/mongo/db/query/query_solution.cpp | 23 | ||||
-rw-r--r-- | src/mongo/db/query/query_solution.h | 23 | ||||
-rw-r--r-- | src/mongo/db/query/stage_builder.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/query/stage_types.h | 1 |
10 files changed, 124 insertions, 8 deletions
diff --git a/src/mongo/db/fts/fts_command.h b/src/mongo/db/fts/fts_command.h index 990a9a64998..81a4a3d6043 100644 --- a/src/mongo/db/fts/fts_command.h +++ b/src/mongo/db/fts/fts_command.h @@ -35,6 +35,9 @@ #include "mongo/db/commands.h" +// mongo::fts::FTSCommand is deprecated: the "text" command is deprecated in favor of the $text +// query operator. + namespace mongo { namespace fts { diff --git a/src/mongo/db/fts/fts_search.h b/src/mongo/db/fts/fts_search.h index 46264e20d43..17070cec903 100644 --- a/src/mongo/db/fts/fts_search.h +++ b/src/mongo/db/fts/fts_search.h @@ -42,6 +42,9 @@ #include "mongo/db/index/index_descriptor.h" #include "mongo/db/matcher.h" +// mongo::fts::FTSSearch is deprecated: the "text" command is deprecated in favor of the $text +// query operator. + namespace mongo { class BtreeCursor; diff --git a/src/mongo/db/query/indexability.h b/src/mongo/db/query/indexability.h index 626a4cdd1a0..b72eb2a6573 100644 --- a/src/mongo/db/query/indexability.h +++ b/src/mongo/db/query/indexability.h @@ -48,7 +48,8 @@ namespace mongo { || me->matchType() == MatchExpression::MATCH_IN || me->matchType() == MatchExpression::TYPE_OPERATOR || me->matchType() == MatchExpression::GEO - || me->matchType() == MatchExpression::GEO_NEAR; + || me->matchType() == MatchExpression::GEO_NEAR + || me->matchType() == MatchExpression::TEXT; } /** diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp index 5854a196216..dfac2b653fc 100644 --- a/src/mongo/db/query/new_find.cpp +++ b/src/mongo/db/query/new_find.cpp @@ -531,6 +531,10 @@ namespace mongo { // We use this a lot below. const LiteParsedQuery& pq = cq->getParsed(); + // Need to call cq->toString() now, since upon error getRunner doesn't guarantee + // cq is in a consistent state. + string cqStr = cq->toString(); + Status status = Status::OK(); if (pq.hasOption(QueryOption_OplogReplay)) { status = getOplogStartHack(cq, &rawRunner); @@ -541,8 +545,7 @@ namespace mongo { } if (!status.isOK()) { - uasserted(17007, "Couldn't process query " + cq->toString() - + " why: " + status.reason()); + uasserted(17007, "Couldn't process query " + cqStr + " why: " + status.reason()); } verify(NULL != rawRunner); diff --git a/src/mongo/db/query/plan_enumerator.cpp b/src/mongo/db/query/plan_enumerator.cpp index 6c806cfa624..1af135c4bdc 100644 --- a/src/mongo/db/query/plan_enumerator.cpp +++ b/src/mongo/db/query/plan_enumerator.cpp @@ -304,11 +304,14 @@ namespace mongo { for (size_t j = 0; j < oie.preds.size(); ++j) { MatchExpression* expr = oie.preds[j]; - // TODO: Text goes here. if (MatchExpression::GEO_NEAR == expr->matchType()) { hasPredThatRequiresIndex = true; break; } + if (MatchExpression::TEXT == expr->matchType()) { + hasPredThatRequiresIndex = true; + break; + } } if (hasPredThatRequiresIndex) { diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index f8c3a77ad3d..d67a29d666f 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -37,6 +37,7 @@ #include "mongo/client/dbclientinterface.h" #include "mongo/db/matcher/expression_array.h" #include "mongo/db/matcher/expression_geo.h" +#include "mongo/db/matcher/expression_text.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/query/index_bounds_builder.h" @@ -153,7 +154,10 @@ namespace mongo { } return false; } - else if ("text" == ixtype || "_fts" == ixtype || "geoHaystack" == ixtype) { + else if ("text" == ixtype || "fts" == ixtype) { + return (exprtype == MatchExpression::TEXT); + } + else if ("geoHaystack" == ixtype) { return false; } else { @@ -286,6 +290,16 @@ namespace mongo { ret->seek = nearme->getRawObj(); return ret; } + else if (MatchExpression::TEXT == expr->matchType()) { + // We must not keep the expression node around. + *exact = true; + TextMatchExpression* textExpr = static_cast<TextMatchExpression*>(expr); + TextNode* ret = new TextNode(); + ret->_indexKeyPattern = index.keyPattern; + ret->_query = textExpr->getQuery(); + ret->_language = textExpr->getLanguage(); + return ret; + } else { // QLOG() << "making ixscan for " << expr->toString() << endl; @@ -398,7 +412,7 @@ namespace mongo { void QueryPlanner::finishLeafNode(QuerySolutionNode* node, const IndexEntry& index) { const StageType type = node->getType(); - if (STAGE_GEO_2D == type || STAGE_GEO_NEAR_2D == type) { + if (STAGE_GEO_2D == type || STAGE_GEO_NEAR_2D == type || STAGE_TEXT == type) { // XXX: do we do anything here? return; } @@ -1153,6 +1167,15 @@ namespace mongo { } } + // Likewise, if there is a TEXT it must have an index it can use directly. + MatchExpression* textNode; + if (QueryPlannerCommon::hasNode(query.root(), MatchExpression::TEXT, &textNode)) { + RelevantTag* tag = static_cast<RelevantTag*>(textNode->getTag()); + if (0 == tag->first.size() && 0 == tag->notFirst.size()) { + return; + } + } + // If we have any relevant indices, we try to create indexed plans. if (0 < relevantIndices.size()) { for (size_t i = 0; i < relevantIndices.size(); ++i) { @@ -1279,8 +1302,10 @@ namespace mongo { // TODO: Do we always want to offer a collscan solution? // XXX: currently disabling the always-use-a-collscan in order to find more planner bugs. - if (!QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO_NEAR) - && ((options & QueryPlanner::INCLUDE_COLLSCAN) || (0 == out->size() && canTableScan))) { + if ( !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO_NEAR) + && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::TEXT) + && ((options & QueryPlanner::INCLUDE_COLLSCAN) || (0 == out->size() && canTableScan))) + { QuerySolution* collscan = makeCollectionScan(query, false); out->push_back(collscan); QLOG() << "Planner: outputting a collscan\n"; diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp index 66f4c756228..4647eb592de 100644 --- a/src/mongo/db/query/query_solution.cpp +++ b/src/mongo/db/query/query_solution.cpp @@ -31,6 +31,29 @@ namespace mongo { // + // TextNode + // + + void TextNode::appendToString(stringstream* ss, int indent) const { + addIndent(ss, indent); + *ss << "TEXT\n"; + addIndent(ss, indent + 1); + *ss << "numWanted = " << _numWanted << endl; + addIndent(ss, indent + 1); + *ss << "keyPattern = " << _indexKeyPattern.toString() << endl; + addIndent(ss, indent + 1); + *ss << "fetched = " << fetched() << endl; + addIndent(ss, indent + 1); + *ss << "sortedByDiskLoc = " << sortedByDiskLoc() << endl; + addIndent(ss, indent + 1); + *ss << "getSort = " << getSort().toString() << endl; + addIndent(ss, indent + 1); + *ss << "query = " << _query << endl; + addIndent(ss, indent + 1); + *ss << "language = " << _language << endl; + } + + // // CollectionScanNode // diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h index c8a0e933247..2ccbcfa4ffe 100644 --- a/src/mongo/db/query/query_solution.h +++ b/src/mongo/db/query/query_solution.h @@ -30,12 +30,15 @@ #include "mongo/db/matcher/expression.h" #include "mongo/db/geo/geoquery.h" +#include "mongo/db/fts/fts_query.h" #include "mongo/db/query/index_bounds.h" #include "mongo/db/query/projection_parser.h" #include "mongo/db/query/stage_types.h" namespace mongo { + using mongo::fts::FTSQuery; + /** * This is an abstract representation of a query plan. It can be transcribed into a tree of * PlanStages, which can then be handed to a PlanRunner for execution. @@ -155,6 +158,26 @@ namespace mongo { MONGO_DISALLOW_COPYING(QuerySolution); }; + struct TextNode : public QuerySolutionNode { + TextNode() : _numWanted(100) { } + virtual ~TextNode() { } + + virtual StageType getType() const { return STAGE_TEXT; } + + virtual void appendToString(stringstream* ss, int indent) const; + + bool fetched() const { return false; } + bool hasField(const string& field) const { return false; } + bool sortedByDiskLoc() const { return false; } + BSONObj getSort() const { return _indexKeyPattern; } + + uint32_t _numWanted; + BSONObj _indexKeyPattern; + std::string _query; + std::string _language; + scoped_ptr<MatchExpression> _filter; + }; + struct CollectionScanNode : public QuerySolutionNode { CollectionScanNode(); virtual ~CollectionScanNode() { } diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp index 1859329f26b..8de28f45464 100644 --- a/src/mongo/db/query/stage_builder.cpp +++ b/src/mongo/db/query/stage_builder.cpp @@ -40,6 +40,7 @@ #include "mongo/db/exec/s2near.h" #include "mongo/db/exec/sort.h" #include "mongo/db/exec/skip.h" +#include "mongo/db/exec/text.h" #include "mongo/db/index/catalog_hack.h" #include "mongo/db/namespace_details.h" @@ -189,6 +190,36 @@ namespace mongo { return new S2NearStage(ns, node->indexKeyPattern, node->nq, node->baseBounds, node->filter.get(), ws); } + else if (STAGE_TEXT == root->getType()) { + const TextNode* node = static_cast<const TextNode*>(root); + + NamespaceDetails* nsd = nsdetails(ns.c_str()); + if (NULL == nsd) { return NULL; } + vector<int> idxMatches; + nsd->findIndexByType("text", idxMatches); + if (0 == idxMatches.size()) { return NULL; } + IndexDescriptor* index = CatalogHack::getDescriptor(nsd, idxMatches[0]); + auto_ptr<FTSAccessMethod> fam(new FTSAccessMethod(index)); + TextStageParams params(fam->getSpec()); + + params.ns = ns; + params.index = index; + params.spec = fam->getSpec(); + params.limit = node->_numWanted; + Status s = fam->getSpec().getIndexPrefix(BSONObj(), ¶ms.indexPrefix); + if (!s.isOK()) { return NULL; } + + string language = ("" == node->_language + ? fam->getSpec().defaultLanguage() + : node->_language); + + FTSQuery ftsq; + Status parseStatus = ftsq.parse(node->_query, language); + if (!parseStatus.isOK()) { return NULL; } + params.query = ftsq; + + return new TextStage(params, ws, node->_filter.get()); + } else { stringstream ss; root->appendToString(&ss, 0); diff --git a/src/mongo/db/query/stage_types.h b/src/mongo/db/query/stage_types.h index 5db655f8e81..c59c20c2cf1 100644 --- a/src/mongo/db/query/stage_types.h +++ b/src/mongo/db/query/stage_types.h @@ -53,6 +53,7 @@ namespace mongo { STAGE_SKIP, STAGE_SORT, STAGE_SORT_MERGE, + STAGE_TEXT, STAGE_UNKNOWN, }; |