summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/planner_access.cpp
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@gmail.com>2018-09-12 15:30:56 +0100
committerBernard Gorman <bernard.gorman@gmail.com>2018-10-11 01:23:07 +0100
commit8dab2485c1badc2fd09b84667b4dcfc8b53fef63 (patch)
tree3479da286c74a91a75b1703f422aa886e59bd33d /src/mongo/db/query/planner_access.cpp
parente01cdb673978f6d3a9d53bb3a6ba35b34c281b0e (diff)
downloadmongo-8dab2485c1badc2fd09b84667b4dcfc8b53fef63.tar.gz
SERVER-36517 Allow wildcard indexes to provide DISTINCT_SCAN
Diffstat (limited to 'src/mongo/db/query/planner_access.cpp')
-rw-r--r--src/mongo/db/query/planner_access.cpp77
1 files changed, 2 insertions, 75 deletions
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index 6cc93126dad..7bcb08f0832 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -57,7 +57,7 @@ namespace {
using namespace mongo;
-namespace app = ::mongo::wildcard_planning;
+namespace wcp = ::mongo::wildcard_planning;
namespace dps = ::mongo::dotted_path_support;
/**
@@ -530,79 +530,6 @@ void QueryPlannerAccess::finishTextNode(QuerySolutionNode* node, const IndexEntr
tn->indexPrefix = prefixBob.obj();
}
-void QueryPlannerAccess::finishWildcardIndexScanNode(QuerySolutionNode* node,
- const IndexEntry& plannerIndex) {
- // We should only ever reach this point if we are an IndexScanNode for a $** index.
- invariant(plannerIndex.type == IndexType::INDEX_WILDCARD);
- invariant(node && node->getType() == STAGE_IXSCAN);
-
- // Sanity check the QuerySolutionNode's copy of the IndexEntry.
- IndexScanNode* scan = static_cast<IndexScanNode*>(node);
- invariant(scan->index.multikeyPaths.size() == 1);
- invariant(scan->index == plannerIndex);
-
- // Obtain some references to make the remainder of this function more legible.
- auto& bounds = scan->bounds;
- auto& index = scan->index;
-
- // For $** indexes, the IndexEntry key pattern is {'path.to.field': ±1} but the actual keys in
- // the index are of the form {'$_path': ±1, 'path.to.field': ±1}, where the value of the first
- // field in each key is 'path.to.field'. We push a new entry into the bounds vector for the
- // leading '$_path' bound here. We also push corresponding fields into the IndexScanNode's
- // keyPattern and its multikeyPaths vector.
- invariant(plannerIndex.keyPattern.nFields() == 1);
- invariant(bounds.fields.size() == 1);
- invariant(!bounds.fields.front().name.empty());
- index.multikeyPaths.insert(index.multikeyPaths.begin(), std::set<std::size_t>{});
- bounds.fields.insert(bounds.fields.begin(), {"$_path"});
- index.keyPattern =
- BSON("$_path" << index.keyPattern.firstElement() << index.keyPattern.firstElement());
-
- // Create a FieldRef to perform any necessary manipulations on the query path string.
- FieldRef queryPath{plannerIndex.keyPattern.firstElementFieldName()};
- auto& multikeyPaths = index.multikeyPaths.back();
-
- // Helper function to check whether the final path component in 'queryPath' is an array index.
- const auto lastFieldIsArrayIndex = [&multikeyPaths](const auto& queryPath) {
- return (queryPath.numParts() > 1u && multikeyPaths.count(queryPath.numParts() - 2u) &&
- queryPath.isNumericPathComponentStrict(queryPath.numParts() - 1u));
- };
-
- // If the bounds intersect with any objects, we must add bounds that encompass all its
- // subpaths, specifically the interval ["path.","path/") on "$_path".
- const bool requiresSubpathBounds = app::requiresSubpathBounds(bounds.fields.back());
-
- // For bounds which contain objects, we build a range interval on all subpaths of the query
- // path(s). We must therefore trim any trailing array indices from the query path before
- // generating the fieldname-or-array power set, in order to avoid overlapping the final set of
- // bounds. For instance, the untrimmed query path 'a.0' will produce paths 'a' and 'a.0' if 'a'
- // is multikey, and so we would end up with bounds [['a','a'], ['a.','a/'], ['a.0','a.0'],
- // ['a.0.','a.0/']]. The latter two are subsets of the ['a.', 'a/'] interval.
- while (requiresSubpathBounds && lastFieldIsArrayIndex(queryPath)) {
- queryPath.removeLastPart();
- }
-
- // Account for fieldname-or-array-index semantics. $** indexes do not explicitly encode array
- // indices in their keys, so if this query traverses one or more multikey fields via an array
- // index (e.g. query 'a.0.b' where 'a' is an array), then we must generate bounds on all array-
- // and non-array permutations of the path in order to produce INEXACT_FETCH bounds.
- auto paths = app::generateFieldNameOrArrayIndexPathSet(multikeyPaths, queryPath);
-
- // Add a $_path point-interval for each path that needs to be traversed in the index. If the
- // bounds on these paths include objects, then for each applicable path we must add a range
- // interval on all its subpaths, i.e. ["path.","path/").
- static const char subPathStart = '.', subPathEnd = static_cast<char>('.' + 1);
- auto& pathIntervals = bounds.fields.front().intervals;
- for (const auto& fieldPath : paths) {
- auto path = fieldPath.dottedField().toString();
- pathIntervals.push_back(IndexBoundsBuilder::makePointInterval(path));
- if (requiresSubpathBounds) {
- pathIntervals.push_back(IndexBoundsBuilder::makeRangeInterval(
- path + subPathStart, path + subPathEnd, BoundInclusion::kIncludeStartKeyOnly));
- }
- }
-}
-
bool QueryPlannerAccess::orNeedsFetch(const ScanBuildingState* scanState) {
if (scanState->loosestBounds == IndexBoundsBuilder::EXACT) {
return false;
@@ -675,7 +602,7 @@ void QueryPlannerAccess::finishLeafNode(QuerySolutionNode* node, const IndexEntr
// If this is a $** index, update and populate the keyPattern, bounds, and multikeyPaths.
if (index.type == IndexType::INDEX_WILDCARD) {
- finishWildcardIndexScanNode(node, index);
+ wcp::finalizeWildcardIndexScanConfiguration(nodeIndex, bounds);
}
// Find the first field in the scan's bounds that was not filled out.