summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSvilen Mihaylov <svilen.mihaylov@mongodb.com>2022-06-22 20:27:30 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-06-22 22:37:28 +0000
commit3fa2961b6463ffa5a1ef1211ed2162965fa9cb19 (patch)
tree663ed41af0087cd2e21e7c970797ed93b76daf22
parent41994ed8c080afb448b20c631a66bf782a9834c0 (diff)
downloadmongo-3fa2961b6463ffa5a1ef1211ed2162965fa9cb19.tar.gz
SERVER-67380 Improve handling of partial schema requirements
-rw-r--r--src/mongo/db/commands/cqf/cqf_aggregate.cpp10
-rw-r--r--src/mongo/db/pipeline/abt/pipeline_test.cpp36
-rw-r--r--src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp39
-rw-r--r--src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp54
-rw-r--r--src/mongo/db/query/optimizer/rewrites/const_eval.cpp2
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.cpp183
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.h10
7 files changed, 173 insertions, 161 deletions
diff --git a/src/mongo/db/commands/cqf/cqf_aggregate.cpp b/src/mongo/db/commands/cqf/cqf_aggregate.cpp
index 26f6bcf5cff..516a3f9ca2e 100644
--- a/src/mongo/db/commands/cqf/cqf_aggregate.cpp
+++ b/src/mongo/db/commands/cqf/cqf_aggregate.cpp
@@ -184,12 +184,16 @@ static opt::unordered_map<std::string, optimizer::IndexDefinition> buildIndexSpe
// TODO: simplify expression.
- PartialSchemaReqConversion conversion = convertExprToPartialSchemaReq(exprABT);
- if (!conversion._success || conversion._hasEmptyInterval) {
+ auto conversion = convertExprToPartialSchemaReq(exprABT, true /*isFilterContext*/);
+ if (!conversion || conversion->_hasEmptyInterval) {
// Unsatisfiable partial index filter?
continue;
}
- partialIndexReqMap = std::move(conversion._reqMap);
+ tassert(6624257,
+ "Should not be seeing a partial index filter where we need to over-approximate",
+ !conversion->_retainPredicate);
+
+ partialIndexReqMap = std::move(conversion->_reqMap);
}
// For now we assume distribution is Centralized.
diff --git a/src/mongo/db/pipeline/abt/pipeline_test.cpp b/src/mongo/db/pipeline/abt/pipeline_test.cpp
index 34f9d486d32..694047d6683 100644
--- a/src/mongo/db/pipeline/abt/pipeline_test.cpp
+++ b/src/mongo/db/pipeline/abt/pipeline_test.cpp
@@ -2326,13 +2326,15 @@ TEST(ABTTranslate, PartialIndex) {
// The expression matches the pipeline.
// By default the constant is translated as "int32".
- auto conversionResult = convertExprToPartialSchemaReq(make<EvalFilter>(
- make<PathGet>("b",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)))),
- make<Variable>(scanProjName)));
- ASSERT_TRUE(conversionResult._success);
- ASSERT_FALSE(conversionResult._hasEmptyInterval);
- ASSERT_FALSE(conversionResult._retainPredicate);
+ auto conversionResult = convertExprToPartialSchemaReq(
+ make<EvalFilter>(
+ make<PathGet>(
+ "b", make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)))),
+ make<Variable>(scanProjName)),
+ true /*isFilterContext*/);
+ ASSERT_TRUE(conversionResult.has_value());
+ ASSERT_FALSE(conversionResult->_hasEmptyInterval);
+ ASSERT_FALSE(conversionResult->_retainPredicate);
Metadata metadata = {
{{scanDefName,
@@ -2341,7 +2343,7 @@ TEST(ABTTranslate, PartialIndex) {
IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
true /*multiKey*/,
{DistributionType::Centralized},
- std::move(conversionResult._reqMap)}}}}}}};
+ std::move(conversionResult->_reqMap)}}}}}}};
ABT translated = translatePipeline(
metadata, "[{$match: {'a': 3, 'b': 2}}]", scanProjName, scanDefName, prefixId);
@@ -2394,13 +2396,15 @@ TEST(ABTTranslate, PartialIndexNegative) {
ProjectionName scanProjName = prefixId.getNextId("scan");
// The expression does not match the pipeline.
- auto conversionResult = convertExprToPartialSchemaReq(make<EvalFilter>(
- make<PathGet>("b",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)))),
- make<Variable>(scanProjName)));
- ASSERT_TRUE(conversionResult._success);
- ASSERT_FALSE(conversionResult._hasEmptyInterval);
- ASSERT_FALSE(conversionResult._retainPredicate);
+ auto conversionResult = convertExprToPartialSchemaReq(
+ make<EvalFilter>(
+ make<PathGet>(
+ "b", make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)))),
+ make<Variable>(scanProjName)),
+ true /*isFilterContext*/);
+ ASSERT_TRUE(conversionResult.has_value());
+ ASSERT_FALSE(conversionResult->_hasEmptyInterval);
+ ASSERT_FALSE(conversionResult->_retainPredicate);
Metadata metadata = {
{{scanDefName,
@@ -2409,7 +2413,7 @@ TEST(ABTTranslate, PartialIndexNegative) {
IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
true /*multiKey*/,
{DistributionType::Centralized},
- std::move(conversionResult._reqMap)}}}}}}};
+ std::move(conversionResult->_reqMap)}}}}}}};
ABT translated = translatePipeline(
metadata, "[{$match: {'a': 3, 'b': 3}}]", scanProjName, scanDefName, prefixId);
diff --git a/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp b/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp
index fd6bf9e1e40..4ecaf2c0795 100644
--- a/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp
+++ b/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp
@@ -624,16 +624,17 @@ static void convertFilterToSargableNode(ABT::reference_type node,
return;
}
- PartialSchemaReqConversion conversion = convertExprToPartialSchemaReq(filterNode.getFilter());
- if (!conversion._success) {
+ auto conversion =
+ convertExprToPartialSchemaReq(filterNode.getFilter(), true /*isFilterContext*/);
+ if (!conversion) {
return;
}
- if (conversion._hasEmptyInterval) {
+ if (conversion->_hasEmptyInterval) {
addEmptyValueScanNode(ctx);
return;
}
- for (const auto& entry : conversion._reqMap) {
+ for (const auto& entry : conversion->_reqMap) {
uassert(6624111,
"Filter partial schema requirement must contain a variable name.",
!entry.first._projectionName.empty());
@@ -648,29 +649,29 @@ static void convertFilterToSargableNode(ABT::reference_type node,
// If in substitution mode, disallow retaining original predicate. If in exploration mode, only
// allow retaining the original predicate and if we have at least one index available.
if constexpr (isSubstitution) {
- if (conversion._retainPredicate) {
+ if (conversion->_retainPredicate) {
return;
}
- } else if (!conversion._retainPredicate || scanDef.getIndexDefs().empty()) {
+ } else if (!conversion->_retainPredicate || scanDef.getIndexDefs().empty()) {
return;
}
bool hasEmptyInterval = false;
auto candidateIndexMap = computeCandidateIndexMap(ctx.getPrefixId(),
indexingAvailability.getScanProjection(),
- conversion._reqMap,
+ conversion->_reqMap,
scanDef,
hasEmptyInterval);
if (hasEmptyInterval) {
addEmptyValueScanNode(ctx);
} else {
- ABT sargableNode = make<SargableNode>(std::move(conversion._reqMap),
+ ABT sargableNode = make<SargableNode>(std::move(conversion->_reqMap),
std::move(candidateIndexMap),
IndexReqTarget::Complete,
filterNode.getChild());
- if (conversion._retainPredicate) {
+ if (conversion->_retainPredicate) {
const GroupIdType childGroupId =
filterNode.getChild().cast<MemoLogicalDelegatorNode>()->getGroupId();
if (childGroupId == indexingAvailability.getScanGroupId()) {
@@ -813,22 +814,24 @@ struct SubstituteConvert<EvaluationNode> {
}
// We still want to extract sargable nodes from EvalNode to use for PhysicalScans.
- PartialSchemaReqConversion conversion =
- convertExprToPartialSchemaReq(evalNode.getProjection());
+ auto conversion =
+ convertExprToPartialSchemaReq(evalNode.getProjection(), false /*isFilterContext*/);
+ if (!conversion) {
+ return;
+ }
uassert(6624165,
"Should not be getting retainPredicate set for EvalNodes",
- !conversion._retainPredicate);
-
- if (!conversion._success || conversion._reqMap.size() != 1) {
+ !conversion->_retainPredicate);
+ if (conversion->_reqMap.size() != 1) {
// For evaluation nodes we expect to create a single entry.
return;
}
- if (conversion._hasEmptyInterval) {
+ if (conversion->_hasEmptyInterval) {
addEmptyValueScanNode(ctx);
return;
}
- for (auto& entry : conversion._reqMap) {
+ for (auto& entry : conversion->_reqMap) {
PartialSchemaRequirement& req = entry.second;
req.setBoundProjectionName(evalNode.getProjectionName());
@@ -842,12 +845,12 @@ struct SubstituteConvert<EvaluationNode> {
bool hasEmptyInterval = false;
auto candidateIndexMap = computeCandidateIndexMap(
- ctx.getPrefixId(), scanProjName, conversion._reqMap, scanDef, hasEmptyInterval);
+ ctx.getPrefixId(), scanProjName, conversion->_reqMap, scanDef, hasEmptyInterval);
if (hasEmptyInterval) {
addEmptyValueScanNode(ctx);
} else {
- ABT newNode = make<SargableNode>(std::move(conversion._reqMap),
+ ABT newNode = make<SargableNode>(std::move(conversion->_reqMap),
std::move(candidateIndexMap),
IndexReqTarget::Complete,
evalNode.getChild());
diff --git a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
index 6f6f6c743ed..58cbf9dee2b 100644
--- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
@@ -4310,13 +4310,15 @@ TEST(PhysRewriter, PartialIndex1) {
// TODO: Test cases where partial filter bound is a range which subsumes the query
// requirement
// TODO: (e.g. half open interval)
- auto conversionResult = convertExprToPartialSchemaReq(make<EvalFilter>(
- make<PathGet>("b",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int64(2)))),
- make<Variable>("root")));
- ASSERT_TRUE(conversionResult._success);
- ASSERT_FALSE(conversionResult._hasEmptyInterval);
- ASSERT_FALSE(conversionResult._retainPredicate);
+ auto conversionResult = convertExprToPartialSchemaReq(
+ make<EvalFilter>(
+ make<PathGet>(
+ "b", make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int64(2)))),
+ make<Variable>("root")),
+ true /*isFilterContext*/);
+ ASSERT_TRUE(conversionResult.has_value());
+ ASSERT_FALSE(conversionResult->_hasEmptyInterval);
+ ASSERT_FALSE(conversionResult->_retainPredicate);
OptPhaseManager phaseManager(
{OptPhaseManager::OptPhase::MemoSubstitutionPhase,
@@ -4329,7 +4331,7 @@ TEST(PhysRewriter, PartialIndex1) {
IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
true /*isMultiKey*/,
{DistributionType::Centralized},
- std::move(conversionResult._reqMap)}}}}}}},
+ std::move(conversionResult->_reqMap)}}}}}}},
{true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests});
ABT optimized = rootNode;
@@ -4387,13 +4389,15 @@ TEST(PhysRewriter, PartialIndex2) {
ABT rootNode =
make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterANode));
- auto conversionResult = convertExprToPartialSchemaReq(make<EvalFilter>(
- make<PathGet>("a",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int64(3)))),
- make<Variable>("root")));
- ASSERT_TRUE(conversionResult._success);
- ASSERT_FALSE(conversionResult._hasEmptyInterval);
- ASSERT_FALSE(conversionResult._retainPredicate);
+ auto conversionResult = convertExprToPartialSchemaReq(
+ make<EvalFilter>(
+ make<PathGet>(
+ "a", make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int64(3)))),
+ make<Variable>("root")),
+ true /*isFilterContext*/);
+ ASSERT_TRUE(conversionResult.has_value());
+ ASSERT_FALSE(conversionResult->_hasEmptyInterval);
+ ASSERT_FALSE(conversionResult->_retainPredicate);
OptPhaseManager phaseManager(
{OptPhaseManager::OptPhase::MemoSubstitutionPhase,
@@ -4406,7 +4410,7 @@ TEST(PhysRewriter, PartialIndex2) {
IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
true /*isMultiKey*/,
{DistributionType::Centralized},
- std::move(conversionResult._reqMap)}}}}}}},
+ std::move(conversionResult->_reqMap)}}}}}}},
{true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests});
ABT optimized = rootNode;
@@ -4462,13 +4466,15 @@ TEST(PhysRewriter, PartialIndexReject) {
ABT rootNode =
make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterBNode));
- auto conversionResult = convertExprToPartialSchemaReq(make<EvalFilter>(
- make<PathGet>("b",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int64(4)))),
- make<Variable>("root")));
- ASSERT_TRUE(conversionResult._success);
- ASSERT_FALSE(conversionResult._hasEmptyInterval);
- ASSERT_FALSE(conversionResult._retainPredicate);
+ auto conversionResult = convertExprToPartialSchemaReq(
+ make<EvalFilter>(
+ make<PathGet>(
+ "b", make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int64(4)))),
+ make<Variable>("root")),
+ true /*isFilterContext*/);
+ ASSERT_TRUE(conversionResult.has_value());
+ ASSERT_FALSE(conversionResult->_hasEmptyInterval);
+ ASSERT_FALSE(conversionResult->_retainPredicate);
OptPhaseManager phaseManager(
{OptPhaseManager::OptPhase::MemoSubstitutionPhase,
@@ -4481,7 +4487,7 @@ TEST(PhysRewriter, PartialIndexReject) {
IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
true /*isMultiKey*/,
{DistributionType::Centralized},
- std::move(conversionResult._reqMap)}}}}}}},
+ std::move(conversionResult->_reqMap)}}}}}}},
{true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests});
ABT optimized = rootNode;
diff --git a/src/mongo/db/query/optimizer/rewrites/const_eval.cpp b/src/mongo/db/query/optimizer/rewrites/const_eval.cpp
index 89bfe74551f..0278e20700e 100644
--- a/src/mongo/db/query/optimizer/rewrites/const_eval.cpp
+++ b/src/mongo/db/query/optimizer/rewrites/const_eval.cpp
@@ -86,7 +86,7 @@ void ConstEval::removeUnusedEvalNodes() {
// TODO: consider caching.
// TODO: consider deriving IndexingAvailability.
if (!_disableSargableInlining ||
- !convertExprToPartialSchemaReq(k->getProjection())._success) {
+ !convertExprToPartialSchemaReq(k->getProjection(), false /*isFilterContext*/)) {
// Schedule node inlining as there is exactly one reference.
_singleRef.emplace(v.front());
_changed = true;
diff --git a/src/mongo/db/query/optimizer/utils/utils.cpp b/src/mongo/db/query/optimizer/utils/utils.cpp
index 42476964493..da4be863228 100644
--- a/src/mongo/db/query/optimizer/utils/utils.cpp
+++ b/src/mongo/db/query/optimizer/utils/utils.cpp
@@ -340,18 +340,8 @@ VariableNameSetType collectVariableReferences(const ABT& n) {
return NodeVariableTracker::collect(n);
}
-PartialSchemaReqConversion::PartialSchemaReqConversion()
- : _success(false),
- _bound(),
- _reqMap(),
- _hasIntersected(false),
- _hasTraversed(false),
- _hasEmptyInterval(false),
- _retainPredicate(false) {}
-
PartialSchemaReqConversion::PartialSchemaReqConversion(PartialSchemaRequirements reqMap)
- : _success(true),
- _bound(),
+ : _bound(),
_reqMap(std::move(reqMap)),
_hasIntersected(false),
_hasTraversed(false),
@@ -359,8 +349,7 @@ PartialSchemaReqConversion::PartialSchemaReqConversion(PartialSchemaRequirements
_retainPredicate(false) {}
PartialSchemaReqConversion::PartialSchemaReqConversion(ABT bound)
- : _success(true),
- _bound(std::move(bound)),
+ : _bound(std::move(bound)),
_reqMap(),
_hasIntersected(false),
_hasTraversed(false),
@@ -372,23 +361,24 @@ PartialSchemaReqConversion::PartialSchemaReqConversion(ABT bound)
*/
class PartialSchemaReqConverter {
public:
- PartialSchemaReqConverter() = default;
+ using ResultType = boost::optional<PartialSchemaReqConversion>;
- PartialSchemaReqConversion handleEvalPathAndEvalFilter(PartialSchemaReqConversion pathResult,
- PartialSchemaReqConversion inputResult) {
- if (!pathResult._success || !inputResult._success) {
+ PartialSchemaReqConverter(const bool isFilterContext) : _isFilterContext(isFilterContext) {}
+
+ ResultType handleEvalPathAndEvalFilter(ResultType pathResult, ResultType inputResult) {
+ if (!pathResult || !inputResult) {
return {};
}
- if (pathResult._bound.has_value() || !inputResult._bound.has_value() ||
- !inputResult._reqMap.empty()) {
+ if (pathResult->_bound.has_value() || !inputResult->_bound.has_value() ||
+ !inputResult->_reqMap.empty()) {
return {};
}
- if (auto boundPtr = inputResult._bound->cast<Variable>(); boundPtr != nullptr) {
+ if (auto boundPtr = inputResult->_bound->cast<Variable>(); boundPtr != nullptr) {
const ProjectionName& boundVarName = boundPtr->name();
PartialSchemaRequirements newMap;
- for (auto& [key, req] : pathResult._reqMap) {
+ for (auto& [key, req] : pathResult->_reqMap) {
if (!key._projectionName.empty()) {
return {};
}
@@ -396,40 +386,40 @@ public:
}
PartialSchemaReqConversion result{std::move(newMap)};
- result._hasEmptyInterval = pathResult._hasEmptyInterval;
- result._retainPredicate = pathResult._retainPredicate;
+ result._hasEmptyInterval = pathResult->_hasEmptyInterval;
+ result._retainPredicate = pathResult->_retainPredicate;
return result;
}
return {};
}
- PartialSchemaReqConversion transport(const ABT& n,
- const EvalPath& evalPath,
- PartialSchemaReqConversion pathResult,
- PartialSchemaReqConversion inputResult) {
+ ResultType transport(const ABT& n,
+ const EvalPath& evalPath,
+ ResultType pathResult,
+ ResultType inputResult) {
return handleEvalPathAndEvalFilter(std::move(pathResult), std::move(inputResult));
}
- PartialSchemaReqConversion transport(const ABT& n,
- const EvalFilter& evalFilter,
- PartialSchemaReqConversion pathResult,
- PartialSchemaReqConversion inputResult) {
+ ResultType transport(const ABT& n,
+ const EvalFilter& evalFilter,
+ ResultType pathResult,
+ ResultType inputResult) {
return handleEvalPathAndEvalFilter(std::move(pathResult), std::move(inputResult));
}
- static PartialSchemaReqConversion handleComposition(const bool isMultiplicative,
- PartialSchemaReqConversion leftResult,
- PartialSchemaReqConversion rightResult) {
- if (!leftResult._success || !rightResult._success) {
+ static ResultType handleComposition(const bool isMultiplicative,
+ ResultType leftResult,
+ ResultType rightResult) {
+ if (!leftResult || !rightResult) {
return {};
}
- if (leftResult._bound.has_value() || rightResult._bound.has_value()) {
+ if (leftResult->_bound.has_value() || rightResult->_bound.has_value()) {
return {};
}
- auto& leftReqMap = leftResult._reqMap;
- auto& rightReqMap = rightResult._reqMap;
+ auto& leftReqMap = leftResult->_reqMap;
+ auto& rightReqMap = rightResult->_reqMap;
if (isMultiplicative) {
{
ProjectionRenames projectionRenames;
@@ -441,7 +431,7 @@ public:
}
}
- if (!leftResult._hasTraversed && !rightResult._hasTraversed) {
+ if (!leftResult->_hasTraversed && !rightResult->_hasTraversed) {
// Intersect intervals only if we have not traversed. E.g. (-inf, 90) ^ (70, +inf)
// becomes (70, 90).
for (auto& [key, req] : leftReqMap) {
@@ -449,7 +439,7 @@ public:
if (newIntervals) {
req.getIntervals() = std::move(newIntervals.get());
} else {
- leftResult._hasEmptyInterval = true;
+ leftResult->_hasEmptyInterval = true;
break;
}
}
@@ -458,7 +448,7 @@ public:
return {};
}
- leftResult._hasIntersected = true;
+ leftResult->_hasIntersected = true;
return leftResult;
}
@@ -537,32 +527,40 @@ public:
rightPath.is<PathIdentity>()) {
// leftPath = Id, rightPath = Traverse Id.
combineIntervalsDNF(false /*intersect*/, leftIntervals, newInterval);
- leftResult._retainPredicate = true;
+ leftResult->_retainPredicate = true;
return leftResult;
} else if (const auto rightTraversePtr = rightPath.cast<PathTraverse>();
rightTraversePtr != nullptr && rightTraversePtr->getPath().is<PathIdentity>() &&
leftPath.is<PathIdentity>()) {
// leftPath = Traverse Id, rightPath = Id.
combineIntervalsDNF(false /*intersect*/, rightIntervals, newInterval);
- rightResult._retainPredicate = true;
+ rightResult->_retainPredicate = true;
return rightResult;
}
return {};
}
- PartialSchemaReqConversion transport(const ABT& n,
- const PathComposeM& pathComposeM,
- PartialSchemaReqConversion leftResult,
- PartialSchemaReqConversion rightResult) {
+ ResultType transport(const ABT& n,
+ const PathComposeM& pathComposeM,
+ ResultType leftResult,
+ ResultType rightResult) {
+ if (!_isFilterContext) {
+ return {};
+ }
+
return handleComposition(
true /*isMultiplicative*/, std::move(leftResult), std::move(rightResult));
}
- PartialSchemaReqConversion transport(const ABT& n,
- const PathComposeA& pathComposeA,
- PartialSchemaReqConversion leftResult,
- PartialSchemaReqConversion rightResult) {
+ ResultType transport(const ABT& n,
+ const PathComposeA& pathComposeA,
+ ResultType leftResult,
+ ResultType rightResult) {
+ if (!_isFilterContext) {
+ return {};
+ }
+
const auto& path1 = pathComposeA.getPath1();
const auto& path2 = pathComposeA.getPath2();
const auto& eqNull = make<PathCompare>(Operations::Eq, Constant::null());
@@ -574,9 +572,9 @@ public:
auto intervalExpr = IntervalReqExpr::makeSingularDNF(IntervalRequirement{
{true /*inclusive*/, Constant::null()}, {true /*inclusive*/, Constant::null()}});
- return {PartialSchemaRequirements{
+ return {{PartialSchemaRequirements{
{PartialSchemaKey{},
- PartialSchemaRequirement{"" /*boundProjectionName*/, std::move(intervalExpr)}}}};
+ PartialSchemaRequirement{"" /*boundProjectionName*/, std::move(intervalExpr)}}}}};
}
return handleComposition(
@@ -584,19 +582,18 @@ public:
}
template <class T>
- static PartialSchemaReqConversion handleGetAndTraverse(const ABT& n,
- PartialSchemaReqConversion inputResult) {
- if (!inputResult._success) {
+ static ResultType handleGetAndTraverse(const ABT& n, ResultType inputResult) {
+ if (!inputResult) {
return {};
}
- if (inputResult._bound.has_value()) {
+ if (inputResult->_bound.has_value()) {
return {};
}
// New map has keys with appended paths.
PartialSchemaRequirements newMap;
- for (auto& entry : inputResult._reqMap) {
+ for (auto& entry : inputResult->_reqMap) {
if (!entry.first._projectionName.empty()) {
return {};
}
@@ -611,41 +608,39 @@ public:
newMap.emplace(PartialSchemaKey{"", std::move(path)}, std::move(entry.second));
}
- inputResult._reqMap = std::move(newMap);
+ inputResult->_reqMap = std::move(newMap);
return inputResult;
}
- PartialSchemaReqConversion transport(const ABT& n,
- const PathGet& pathGet,
- PartialSchemaReqConversion inputResult) {
+ ResultType transport(const ABT& n, const PathGet& pathGet, ResultType inputResult) {
return handleGetAndTraverse<PathGet>(n, std::move(inputResult));
}
- PartialSchemaReqConversion transport(const ABT& n,
- const PathTraverse& pathTraverse,
- PartialSchemaReqConversion inputResult) {
- if (inputResult._reqMap.size() > 1) {
+ ResultType transport(const ABT& n, const PathTraverse& pathTraverse, ResultType inputResult) {
+ if (!inputResult) {
+ return {};
+ }
+ if (inputResult->_reqMap.size() > 1) {
// Cannot append traverse if we have more than one requirement.
return {};
}
- PartialSchemaReqConversion result =
- handleGetAndTraverse<PathTraverse>(n, std::move(inputResult));
- result._hasTraversed = true;
+ auto result = handleGetAndTraverse<PathTraverse>(n, std::move(inputResult));
+ if (result) {
+ result->_hasTraversed = true;
+ }
return result;
}
- PartialSchemaReqConversion transport(const ABT& n,
- const PathCompare& pathCompare,
- PartialSchemaReqConversion inputResult) {
- if (!inputResult._success) {
+ ResultType transport(const ABT& n, const PathCompare& pathCompare, ResultType inputResult) {
+ if (!inputResult) {
return {};
}
- if (!inputResult._bound.has_value() || !inputResult._reqMap.empty()) {
+ if (!inputResult->_bound.has_value() || !inputResult->_reqMap.empty()) {
return {};
}
- const auto& bound = inputResult._bound;
+ const auto& bound = inputResult->_bound;
bool lowBoundInclusive = false;
boost::optional<ABT> lowBound;
bool highBoundInclusive = false;
@@ -681,51 +676,53 @@ public:
auto intervalExpr = IntervalReqExpr::makeSingularDNF(IntervalRequirement{
{lowBoundInclusive, std::move(lowBound)}, {highBoundInclusive, std::move(highBound)}});
- return {PartialSchemaRequirements{
+ return {{PartialSchemaRequirements{
{PartialSchemaKey{},
- PartialSchemaRequirement{"" /*boundProjectionName*/, std::move(intervalExpr)}}}};
+ PartialSchemaRequirement{"" /*boundProjectionName*/, std::move(intervalExpr)}}}}};
}
- PartialSchemaReqConversion transport(const ABT& n, const PathIdentity& pathIdentity) {
- return {PartialSchemaRequirements{{{}, {}}}};
+ ResultType transport(const ABT& n, const PathIdentity& pathIdentity) {
+ return {{PartialSchemaRequirements{{{}, {}}}}};
}
- PartialSchemaReqConversion transport(const ABT& n, const Constant& c) {
+ ResultType transport(const ABT& n, const Constant& c) {
if (c.isNull()) {
// Cannot create bounds with just NULL.
return {};
}
- return {n};
+ return {{n}};
}
template <typename T, typename... Ts>
- PartialSchemaReqConversion transport(const ABT& n, const T& node, Ts&&...) {
+ ResultType transport(const ABT& n, const T& node, Ts&&...) {
if constexpr (std::is_base_of_v<ExpressionSyntaxSort, T>) {
// We allow expressions to participate in bounds.
- return {n};
+ return {{n}};
}
// General case. Reject conversion.
return {};
}
- PartialSchemaReqConversion convert(const ABT& input) {
+ ResultType convert(const ABT& input) {
return algebra::transport<true>(input, *this);
}
+
+private:
+ const bool _isFilterContext;
};
-PartialSchemaReqConversion convertExprToPartialSchemaReq(const ABT& expr) {
- PartialSchemaReqConverter converter;
- PartialSchemaReqConversion result = converter.convert(expr);
- if (result._reqMap.empty()) {
- result._success = false;
- return result;
+boost::optional<PartialSchemaReqConversion> convertExprToPartialSchemaReq(
+ const ABT& expr, const bool isFilterContext) {
+ PartialSchemaReqConverter converter(isFilterContext);
+ auto result = converter.convert(expr);
+ if (!result || result->_reqMap.empty()) {
+ return {};
}
- for (const auto& entry : result._reqMap) {
+ for (const auto& entry : result->_reqMap) {
if (entry.first.emptyPath() && isIntervalReqFullyOpenDNF(entry.second.getIntervals())) {
// We need to determine either path or interval (or both).
- result._success = false;
- return result;
+ return {};
}
}
return result;
diff --git a/src/mongo/db/query/optimizer/utils/utils.h b/src/mongo/db/query/optimizer/utils/utils.h
index 473e2bbbd70..d3164d10db6 100644
--- a/src/mongo/db/query/optimizer/utils/utils.h
+++ b/src/mongo/db/query/optimizer/utils/utils.h
@@ -154,13 +154,9 @@ private:
};
struct PartialSchemaReqConversion {
- PartialSchemaReqConversion();
PartialSchemaReqConversion(PartialSchemaRequirements reqMap);
PartialSchemaReqConversion(ABT bound);
- // Is our current bottom-up conversion successful. If not shortcut to top.
- bool _success;
-
// If set, contains a Constant or Variable bound of an (yet unknown) interval.
boost::optional<ABT> _bound;
@@ -185,9 +181,11 @@ struct PartialSchemaReqConversion {
/**
* Takes an expression that comes from an Filter or Evaluation node, and attempt to convert
* to a PartialSchemaReqConversion. This is done independent of the availability of indexes.
- * Essentially this means to extract intervals over paths whenever possible.
+ * Essentially this means to extract intervals over paths whenever possible. If the conversion is
+ * not possible, return empty result.
*/
-PartialSchemaReqConversion convertExprToPartialSchemaReq(const ABT& expr);
+boost::optional<PartialSchemaReqConversion> convertExprToPartialSchemaReq(const ABT& expr,
+ bool isFilterContext);
bool intersectPartialSchemaReq(PartialSchemaRequirements& target,
const PartialSchemaRequirements& source,