summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/pipeline/SConscript1
-rw-r--r--src/mongo/db/pipeline/expression.cpp32
-rw-r--r--src/mongo/db/pipeline/expression.h23
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp90
-rw-r--r--src/mongo/db/pipeline/expression_visitor.h2
5 files changed, 148 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index f034f2f87fb..db5c5a9f439 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -148,6 +148,7 @@ env.Library(
'expression_context',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/mongohasher',
'$BUILD_DIR/mongo/db/query/query_knobs',
]
)
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index ea28f3e13cc..437fe7d9d45 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -41,6 +41,7 @@
#include <vector>
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
+#include "mongo/db/hasher.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/expression_context.h"
@@ -6233,4 +6234,35 @@ Value ExpressionRegexMatch::evaluate(const Document& root, Variables* variables)
return executionState.nullish() ? Value(false) : Value(execute(&executionState) > 0);
}
+/* ------------------------- ExpressionToHashedIndexKey -------------------------- */
+REGISTER_EXPRESSION_WITH_MIN_VERSION(
+ toHashedIndexKey,
+ ExpressionToHashedIndexKey::parse,
+ ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42);
+
+boost::intrusive_ptr<Expression> ExpressionToHashedIndexKey::parse(
+ const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ BSONElement expr,
+ const VariablesParseState& vps) {
+ return new ExpressionToHashedIndexKey(expCtx, parseOperand(expCtx, expr, vps));
+}
+
+Value ExpressionToHashedIndexKey::evaluate(const Document& root, Variables* variables) const {
+ Value inpVal(_children[0]->evaluate(root, variables));
+ if (inpVal.missing()) {
+ inpVal = Value(BSONNULL);
+ }
+
+ return Value(BSONElementHasher::hash64(BSON("" << inpVal).firstElement(),
+ BSONElementHasher::DEFAULT_HASH_SEED));
+}
+
+Value ExpressionToHashedIndexKey::serialize(bool explain) const {
+ return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(explain)));
+}
+
+void ExpressionToHashedIndexKey::_doAddDependencies(DepsTracker* deps) const {
+ _children[0]->addDependencies(deps);
+}
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index f1daa29afe9..e73f4d82166 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -2713,4 +2713,27 @@ public:
using ExpressionRegex::ExpressionRegex;
};
+
+class ExpressionToHashedIndexKey : public Expression {
+public:
+ ExpressionToHashedIndexKey(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> inputExpression)
+ : Expression(expCtx, {inputExpression}){};
+
+ static boost::intrusive_ptr<Expression> parse(
+ const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ BSONElement expr,
+ const VariablesParseState& vps);
+
+ void acceptVisitor(ExpressionVisitor* visitor) final {
+ return visitor->visit(this);
+ }
+
+ Value evaluate(const Document& root, Variables* variables) const;
+ Value serialize(bool explain) const final;
+
+protected:
+ void _doAddDependencies(DepsTracker* deps) const final;
+};
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index 54a959e0d0f..dd74505263b 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -31,6 +31,7 @@
#include "mongo/bson/bsonmisc.h"
#include "mongo/config.h"
+#include "mongo/db/hasher.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/pipeline/accumulator.h"
@@ -6450,4 +6451,93 @@ TEST(ExpressionSubtractTest, OverflowLong) {
ASSERT_EQ(result.getType(), BSONType::NumberDouble);
ASSERT_EQ(result.getDouble(), static_cast<double>(minLong) * -1);
}
+
+namespace ExpressionToHashedIndexKeyTest {
+
+TEST(ExpressionToHashedIndexKeyTest, StringInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey"
+ << "hashThisStringLiteral"_sd);
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(-5776344739422278694));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, IntInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << 123);
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(-6548868637522515075));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, TimestampInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << Timestamp(0, 0));
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(-7867208682377458672));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, ObjectIdInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << OID("47cc67093475061e3d95369d"));
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(1576265281381834298));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, DateInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << Date_t());
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(-1178696894582842035));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, MissingInputValueSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey"
+ << "$missingField");
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(2338878944348059895));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, NullInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << BSONNULL);
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(2338878944348059895));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, ExpressionInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << BSON("$pow" << BSON_ARRAY(2 << 4)));
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(2598032665634823220));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, UndefinedInputSucceeds) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey" << BSONUndefined);
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+ Value result = expression->evaluate({}, &(expCtx->variables));
+ ASSERT_VALUE_EQ(result, Value::createIntOrLong(40158834000849533LL));
+}
+
+TEST(ExpressionToHashedIndexKeyTest, DoesAddInputDependencies) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ const BSONObj obj = BSON("$toHashedIndexKey"
+ << "$someValue");
+ auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState);
+
+ DepsTracker deps;
+ expression->addDependencies(&deps);
+ ASSERT_EQ(deps.fields.count("someValue"), 1u);
+ ASSERT_EQ(deps.fields.size(), 1u);
+}
+} // namespace ExpressionToHashedIndexKeyTest
} // namespace ExpressionTests
diff --git a/src/mongo/db/pipeline/expression_visitor.h b/src/mongo/db/pipeline/expression_visitor.h
index 7141547ad54..6b77982df28 100644
--- a/src/mongo/db/pipeline/expression_visitor.h
+++ b/src/mongo/db/pipeline/expression_visitor.h
@@ -65,6 +65,7 @@ class ExpressionExp;
class ExpressionFieldPath;
class ExpressionFilter;
class ExpressionFloor;
+class ExpressionToHashedIndexKey;
class ExpressionHour;
class ExpressionIfNull;
class ExpressionIn;
@@ -273,6 +274,7 @@ public:
virtual void visit(ExpressionFromAccumulator<AccumulatorSum>*) = 0;
virtual void visit(ExpressionFromAccumulator<AccumulatorMergeObjects>*) = 0;
virtual void visit(ExpressionTests::Testable*) = 0;
+ virtual void visit(ExpressionToHashedIndexKey*) = 0;
};
} // namespace mongo