diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 88 | ||||
-rw-r--r-- | src/mongo/db/pipeline/variables.h | 4 |
3 files changed, 90 insertions, 7 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 2ceb6302392..c2c96cf6b35 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -1821,10 +1821,7 @@ intrusive_ptr<Expression> ExpressionFieldPath::optimize() { if (Variables::isUserDefinedVariable(_variable) && getExpressionContext()->variables.hasUserDefinedValue(_variable)) { - const auto val = getExpressionContext()->variables.getUserDefinedValue(_variable); - if (!val.missing()) { - return ExpressionConstant::create(getExpressionContext(), val); - } + return ExpressionConstant::create(getExpressionContext(), evaluate(Document())); } return intrusive_ptr<Expression>(this); diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index 41a9da08bd9..6ada15ea651 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -2190,14 +2190,42 @@ public: } }; +TEST(FieldPath, NoOptimizationForRootFieldPathWithDottedPath) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<ExpressionFieldPath> expression = + ExpressionFieldPath::parse(expCtx, "$$ROOT.x.y", expCtx->variablesParseState); + + // An attempt to optimize returns the Expression itself. + ASSERT_EQUALS(expression, expression->optimize()); +} + +TEST(FieldPath, NoOptimizationForCurrentFieldPathWithDottedPath) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<ExpressionFieldPath> expression = + ExpressionFieldPath::parse(expCtx, "$$CURRENT.x.y", expCtx->variablesParseState); + + // An attempt to optimize returns the Expression itself. + ASSERT_EQUALS(expression, expression->optimize()); +} + +TEST(FieldPath, RemoveOptimizesToMissingValue) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<ExpressionFieldPath> expression = + ExpressionFieldPath::parse(expCtx, "$$REMOVE", expCtx->variablesParseState); + + auto optimizedExpr = expression->optimize(); + + ASSERT_VALUE_EQ(Value(), optimizedExpr->evaluate(Document(BSON("x" << BSON("y" << 123))))); +} + TEST(FieldPath, NoOptimizationOnNormalPath) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a"); // An attempt to optimize returns the Expression itself. ASSERT_EQUALS(expression, expression->optimize()); -}; +} -TEST(FieldPath, OptimizeOnVariableWithConstantValue) { +TEST(FieldPath, OptimizeOnVariableWithConstantScalarValue) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); auto varId = expCtx->variablesParseState.defineVariable("userVar"); expCtx->variables.setValue(varId, Value(123)); @@ -2209,6 +2237,48 @@ TEST(FieldPath, OptimizeOnVariableWithConstantValue) { ASSERT_TRUE(dynamic_cast<ExpressionConstant*>(optimizedExpr.get())); } +TEST(FieldPath, OptimizeOnVariableWithConstantArrayValue) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto varId = expCtx->variablesParseState.defineVariable("userVar"); + expCtx->variables.setValue(varId, Value(BSON_ARRAY(1 << 2 << 3))); + + auto expr = ExpressionFieldPath::parse(expCtx, "$$userVar", expCtx->variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + auto constantExpr = dynamic_cast<ExpressionConstant*>(optimizedExpr.get()); + ASSERT_TRUE(constantExpr); + ASSERT_VALUE_EQ(Value(BSON_ARRAY(1 << 2 << 3)), constantExpr->getValue()); +} + +TEST(FieldPath, OptimizeToEmptyArrayOnNumericalPathComponentAndConstantArrayValue) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto varId = expCtx->variablesParseState.defineVariable("userVar"); + expCtx->variables.setValue(varId, Value(BSON_ARRAY(1 << 2 << 3))); + + auto expr = ExpressionFieldPath::parse(expCtx, "$$userVar.1", expCtx->variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + auto constantExpr = dynamic_cast<ExpressionConstant*>(optimizedExpr.get()); + ASSERT_TRUE(constantExpr); + ASSERT_VALUE_EQ(Value(BSONArray()), constantExpr->getValue()); +} + +TEST(FieldPath, OptimizeOnVariableWithConstantValueAndDottedPath) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto varId = expCtx->variablesParseState.defineVariable("userVar"); + expCtx->variables.setValue(varId, Value(Document{{"x", Document{{"y", 123}}}})); + + auto expr = ExpressionFieldPath::parse(expCtx, "$$userVar.x.y", expCtx->variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + auto constantExpr = dynamic_cast<ExpressionConstant*>(optimizedExpr.get()); + ASSERT_TRUE(constantExpr); + ASSERT_VALUE_EQ(Value(123), constantExpr->getValue()); +} + TEST(FieldPath, NoOptimizationOnVariableWithNoValue) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); expCtx->variablesParseState.defineVariable("userVar"); @@ -2232,6 +2302,20 @@ TEST(FieldPath, NoOptimizationOnVariableWithMissingValue) { ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(optimizedExpr.get())); } +TEST(FieldPath, ScalarVariableWithDottedFieldPathOptimizesToConstantMissingValue) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto varId = expCtx->variablesParseState.defineVariable("userVar"); + expCtx->variables.setValue(varId, Value(123)); + + auto expr = ExpressionFieldPath::parse(expCtx, "$$userVar.x.y", expCtx->variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + auto constantExpr = dynamic_cast<ExpressionConstant*>(optimizedExpr.get()); + ASSERT_TRUE(constantExpr); + ASSERT_VALUE_EQ(Value(), constantExpr->getValue()); +} + /** The field path itself is a dependency. */ class Dependencies { public: diff --git a/src/mongo/db/pipeline/variables.h b/src/mongo/db/pipeline/variables.h index 0d8dd403c36..d3fcc298d1c 100644 --- a/src/mongo/db/pipeline/variables.h +++ b/src/mongo/db/pipeline/variables.h @@ -96,10 +96,12 @@ public: /** * Returns whether a value for 'id' has been stored in this Variables instance. + * TODO: This method does not distinguish between missing entries in _valueList and entries that + * have been explicitly set to missing. */ bool hasUserDefinedValue(Variables::Id id) const { invariant(isUserDefinedVariable(id)); - return _valueList.size() > static_cast<size_t>(id); + return _valueList.size() > static_cast<size_t>(id) && !getUserDefinedValue(id).missing(); } /** |