diff options
author | Bernard Gorman <bernard.gorman@gmail.com> | 2018-09-06 16:30:44 +0100 |
---|---|---|
committer | Bernard Gorman <bernard.gorman@gmail.com> | 2018-09-14 21:06:17 +0100 |
commit | f8c5f009ac1def37d73b20d45dd9352e36849cb9 (patch) | |
tree | ea087702639c07b14676d5576db8189eed009a95 /src/mongo/db/query | |
parent | 45fccee20b37579662fabc6268a76a52f00661c4 (diff) | |
download | mongo-f8c5f009ac1def37d73b20d45dd9352e36849cb9.tar.gz |
SERVER-36362 Do not consider an 'allPaths' index for a $text query
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r-- | src/mongo/db/query/planner_ixselect.cpp | 29 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect.h | 11 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner_all_paths_index_test.cpp | 40 |
3 files changed, 74 insertions, 6 deletions
diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp index eb9cbfbb55f..70deacf2d5b 100644 --- a/src/mongo/db/query/planner_ixselect.cpp +++ b/src/mongo/db/query/planner_ixselect.cpp @@ -686,6 +686,7 @@ void QueryPlannerIXSelect::_rateIndices(MatchExpression* node, // static void QueryPlannerIXSelect::stripInvalidAssignments(MatchExpression* node, const vector<IndexEntry>& indices) { + stripInvalidAssignmentsToAllPathsIndexes(node, indices); stripInvalidAssignmentsToTextIndexes(node, indices); if (MatchExpression::GEO != node->matchType() && @@ -874,6 +875,34 @@ void QueryPlannerIXSelect::stripInvalidAssignmentsToPartialIndices( } // +// AllPaths index invalid assignments. +// +void QueryPlannerIXSelect::stripInvalidAssignmentsToAllPathsIndexes( + MatchExpression* root, const vector<IndexEntry>& indices) { + for (size_t idx = 0; idx < indices.size(); ++idx) { + // Skip over all indexes except $**. + if (indices[idx].type != IndexType::INDEX_ALLPATHS) { + continue; + } + // If we have a $** index, check whether we have a TEXT node in the MatchExpression tree. + const std::function<MatchExpression*(MatchExpression*)> findTextNode = [&](auto* node) { + if (node->matchType() == MatchExpression::TEXT) { + return node; + } + for (size_t i = 0; i < node->numChildren(); ++i) { + if (auto* foundNode = findTextNode(node->getChild(i))) + return foundNode; + } + return static_cast<MatchExpression*>(nullptr); + }; + // If so, remove the $** index from the node's relevant tags. + if (auto* textNode = findTextNode(root)) { + removeIndexRelevantTag(textNode, idx); + } + } +} + +// // Text index quirks // diff --git a/src/mongo/db/query/planner_ixselect.h b/src/mongo/db/query/planner_ixselect.h index b1834a1610e..b0181fab150 100644 --- a/src/mongo/db/query/planner_ixselect.h +++ b/src/mongo/db/query/planner_ixselect.h @@ -228,6 +228,17 @@ private: const std::vector<IndexEntry>& indices); /** + * This function strips RelevantTag assignments to expanded 'allPaths' indexes, in cases where + * the assignment is incompatible with the query. + * + * Specifically, if the query has a TEXT node with both 'text' and 'allPaths' indexes present, + * then the 'allPaths' index will mark itself as relevant to the '_fts' path reported by the + * TEXT node. We therefore remove any such misassigned 'allPaths' tags here. + */ + static void stripInvalidAssignmentsToAllPathsIndexes(MatchExpression* root, + const std::vector<IndexEntry>& indices); + + /** * This function strips RelevantTag assignments to partial indices, where the assignment is * incompatible with the index's filter expression. * diff --git a/src/mongo/db/query/query_planner_all_paths_index_test.cpp b/src/mongo/db/query/query_planner_all_paths_index_test.cpp index 0e6374252c1..c758a0c48a8 100644 --- a/src/mongo/db/query/query_planner_all_paths_index_test.cpp +++ b/src/mongo/db/query/query_planner_all_paths_index_test.cpp @@ -585,12 +585,6 @@ TEST_F(QueryPlannerAllPathsTest, InBasicOrEquivalent) { "bounds: {'$_path': [['a','a',true,true]], a: [[1,1,true,true],[2,2,true,true]]}}}}}"); } -// TODO SERVER-35335: Add testing for Min/Max. -// TODO SERVER-36517: Add testing for DISTINCT_SCAN. -// TODO SERVER-35336: Add testing for partialFilterExpression. -// TODO SERVER-35331: Add testing for hints. -// TODO SERVER-36145: Add testing for non-blocking sort. - // // Index intersection tests. // @@ -668,4 +662,38 @@ TEST_F(QueryPlannerAllPathsTest, AllPathsIndexDoesNotParticipateInIndexIntersect "{fetch: {filter:{b:{$gt:10}}, node: {ixscan: {filter: null, pattern: {$_path:1, a:1}}}}}"); } +// +// AllPaths and $text index tests. +// + +TEST_F(QueryPlannerAllPathsTest, AllPathsIndexDoesNotSupplyCandidatePlanForTextSearch) { + addAllPathsIndex(BSON("$**" << 1)); + addIndex(BSON("a" << 1 << "_fts" + << "text" + << "_ftsx" + << 1)); + + // Confirm that the allPaths index generates candidate plans for queries which do not include a + // $text predicate. + runQuery(fromjson("{a: 10, b: 10}")); + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists( + "{fetch: {filter: {b: 10}, node: {ixscan: {filter: null, pattern: {'$_path': 1, a: 1}}}}}"); + assertSolutionExists( + "{fetch: {filter: {a: 10}, node: {ixscan: {filter: null, pattern: {'$_path': 1, b: 1}}}}}"); + + // Confirm that the allPaths index does not produce any candidate plans when a query includes a + // $text predicate, even for non-$text predicates which may be present in the query. + runQuery(fromjson("{a: 10, b: 10, $text: {$search: 'banana'}}")); + ASSERT_EQUALS(getNumSolutions(), 1U); + assertSolutionExists( + "{fetch: {filter: {b: 10}, node: {text: {prefix: {a: 10}, search: 'banana'}}}}"); +} + +// TODO SERVER-35335: Add testing for Min/Max. +// TODO SERVER-36517: Add testing for DISTINCT_SCAN. +// TODO SERVER-35336: Add testing for partialFilterExpression. +// TODO SERVER-35331: Add testing for hints. +// TODO SERVER-36145: Add testing for non-blocking sort. + } // namespace mongo |