diff options
author | Bernard Gorman <bernard.gorman@gmail.com> | 2018-09-12 15:30:56 +0100 |
---|---|---|
committer | Bernard Gorman <bernard.gorman@gmail.com> | 2018-10-11 01:23:07 +0100 |
commit | 8dab2485c1badc2fd09b84667b4dcfc8b53fef63 (patch) | |
tree | 3479da286c74a91a75b1703f422aa886e59bd33d /src/mongo/db/query/planner_access.cpp | |
parent | e01cdb673978f6d3a9d53bb3a6ba35b34c281b0e (diff) | |
download | mongo-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.cpp | 77 |
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. |