summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKshitij Gupta <kshitij.gupta@mongodb.com>2020-07-15 15:06:01 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-03 16:33:05 +0000
commitd57e17494ceade881013e2e99606cc92d4a38605 (patch)
tree0aede55cbb5c2751374cef684cc0c21c7117741f
parent9cfe13115e92a43d1b9273ee1d5817d548264ba7 (diff)
downloadmongo-d57e17494ceade881013e2e99606cc92d4a38605.tar.gz
SERVER-49214: Add $toHashedIndexKey expression.
-rw-r--r--src/mongo/db/pipeline/SConscript2
-rw-r--r--src/mongo/db/pipeline/expression.cpp29
-rw-r--r--src/mongo/db/pipeline/expression.h22
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp79
-rw-r--r--src/mongo/db/pipeline/expression_visitor.h2
-rw-r--r--src/mongo/db/query/sbe_stage_builder_expression.cpp6
6 files changed, 140 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 71f7524be9c..1929babc330 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -83,6 +83,7 @@ env.Library(
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/vector_clock',
+ '$BUILD_DIR/mongo/db/mongohasher',
],
)
@@ -392,6 +393,7 @@ env.CppUnitTest(
'$BUILD_DIR/mongo/db/cst/cst',
'$BUILD_DIR/mongo/db/exec/document_value/document_value',
'$BUILD_DIR/mongo/db/exec/document_value/document_value_test_util',
+ '$BUILD_DIR/mongo/db/mongohasher',
'$BUILD_DIR/mongo/db/query/collation/collator_interface_mock',
'$BUILD_DIR/mongo/db/query/query_test_service_context',
'$BUILD_DIR/mongo/db/repl/oplog_entry',
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 8c5ef56f5f2..71d24d13528 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
#include "mongo/db/exec/document_value/document.h"
#include "mongo/db/exec/document_value/value.h"
+#include "mongo/db/hasher.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/pipeline/expression_context.h"
#include "mongo/db/query/datetime/date_time_support.h"
@@ -6481,4 +6482,32 @@ void ExpressionRandom::_doAddDependencies(DepsTracker* deps) const {
Value ExpressionRandom::serialize(const bool explain) const {
return Value(DOC(getOpName() << Document()));
}
+
+/* ------------------------- ExpressionToHashedIndexKey -------------------------- */
+REGISTER_EXPRESSION(toHashedIndexKey, ExpressionToHashedIndexKey::parse);
+
+boost::intrusive_ptr<Expression> ExpressionToHashedIndexKey::parse(ExpressionContext* const expCtx,
+ BSONElement expr,
+ const VariablesParseState& vps) {
+ return make_intrusive<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 {
+ // Nothing to do
+}
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index 862a6a31da6..32b47f4562a 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -2908,4 +2908,26 @@ private:
double getRandomValue() const;
};
+
+class ExpressionToHashedIndexKey : public Expression {
+public:
+ ExpressionToHashedIndexKey(ExpressionContext* const expCtx,
+ boost::intrusive_ptr<Expression> inputExpression)
+ : Expression(expCtx, {inputExpression}){};
+
+ static boost::intrusive_ptr<Expression> parse(ExpressionContext* const 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 cf3929bf7aa..5288fe96c00 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/exec/document_value/document.h"
#include "mongo/db/exec/document_value/document_value_test_util.h"
#include "mongo/db/exec/document_value/value_comparator.h"
+#include "mongo/db/hasher.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/pipeline/accumulator.h"
@@ -2983,4 +2984,82 @@ TEST(ExpressionRandom, Basic) {
};
assertRandomProperties(randFn);
}
+
+namespace ExpressionToHashedIndexKeyTest {
+
+TEST(ExpressionToHashedIndexKeyTest, StringInputSucceeds) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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) {
+ auto expCtx = 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));
+}
+
+} // namespace ExpressionToHashedIndexKeyTest
} // namespace ExpressionTests
diff --git a/src/mongo/db/pipeline/expression_visitor.h b/src/mongo/db/pipeline/expression_visitor.h
index d6b2b9d5042..a8b3d08e6c7 100644
--- a/src/mongo/db/pipeline/expression_visitor.h
+++ b/src/mongo/db/pipeline/expression_visitor.h
@@ -68,6 +68,7 @@ class ExpressionExp;
class ExpressionFieldPath;
class ExpressionFilter;
class ExpressionFloor;
+class ExpressionToHashedIndexKey;
class ExpressionHour;
class ExpressionIfNull;
class ExpressionIn;
@@ -299,6 +300,7 @@ public:
virtual void visit(ExpressionInternalFindSlice*) = 0;
virtual void visit(ExpressionInternalFindPositional*) = 0;
virtual void visit(ExpressionInternalFindElemMatch*) = 0;
+ virtual void visit(ExpressionToHashedIndexKey*) = 0;
};
} // namespace mongo
diff --git a/src/mongo/db/query/sbe_stage_builder_expression.cpp b/src/mongo/db/query/sbe_stage_builder_expression.cpp
index 7250ba476df..b4fe30cf997 100644
--- a/src/mongo/db/query/sbe_stage_builder_expression.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_expression.cpp
@@ -467,6 +467,7 @@ public:
void visit(ExpressionInternalFindElemMatch* expr) final {}
void visit(ExpressionFunction* expr) final {}
void visit(ExpressionRandom* expr) final {}
+ void visit(ExpressionToHashedIndexKey* expr) final {}
private:
void visitMultiBranchLogicExpression(Expression* expr, sbe::EPrimBinary::Op logicOp) {
@@ -652,6 +653,7 @@ public:
void visit(ExpressionInternalFindElemMatch* expr) final {}
void visit(ExpressionFunction* expr) final {}
void visit(ExpressionRandom* expr) final {}
+ void visit(ExpressionToHashedIndexKey* expr) final {}
private:
void visitMultiBranchLogicExpression(Expression* expr, sbe::EPrimBinary::Op logicOp) {
@@ -1509,6 +1511,10 @@ public:
unsupportedExpression(expr->getOpName());
}
+ void visit(ExpressionToHashedIndexKey* expr) final {
+ unsupportedExpression("$toHashedIndexKey");
+ }
+
private:
/**
* Shared logic for $and, $or. Converts each child into an EExpression that evaluates to Boolean