summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJason Rassi <rassi@10gen.com>2013-10-11 16:53:46 -0700
committerJason Rassi <rassi@10gen.com>2013-10-11 17:32:19 -0700
commit34b88976c91fdeec5ff0b8816d35deef72f3767e (patch)
treeb9d1c4f49da0778a7c028c2b82a1838afab3a4a5 /src/mongo
parenta1d5f24d910d1407b8c166d99f0df99640845a2e (diff)
downloadmongo-34b88976c91fdeec5ff0b8816d35deef72f3767e.tar.gz
SERVER-9063 Add new query operator $text
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/fts/fts_command.h3
-rw-r--r--src/mongo/db/fts/fts_search.h3
-rw-r--r--src/mongo/db/query/indexability.h3
-rw-r--r--src/mongo/db/query/new_find.cpp7
-rw-r--r--src/mongo/db/query/plan_enumerator.cpp5
-rw-r--r--src/mongo/db/query/query_planner.cpp33
-rw-r--r--src/mongo/db/query/query_solution.cpp23
-rw-r--r--src/mongo/db/query/query_solution.h23
-rw-r--r--src/mongo/db/query/stage_builder.cpp31
-rw-r--r--src/mongo/db/query/stage_types.h1
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(), &params.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,
};