summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/pipeline/expression.cpp5
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp88
-rw-r--r--src/mongo/db/pipeline/variables.h4
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();
}
/**