diff options
author | Benjamin Murphy <benjamin_murphy@me.com> | 2016-03-02 16:51:17 -0500 |
---|---|---|
committer | Benjamin Murphy <benjamin_murphy@me.com> | 2016-03-15 11:27:33 -0400 |
commit | a9d3450f9a40c6ef02b5713c9f0841aa424a327a (patch) | |
tree | ac64a6daf70e1b4078c9dc915e41032b08238ab8 | |
parent | fea8935aa03a68524014e5d0697ca5e55d390a2b (diff) | |
download | mongo-a9d3450f9a40c6ef02b5713c9f0841aa424a327a.tar.gz |
SERVER-13447 Added type expression to aggregation.
-rw-r--r-- | src/mongo/bson/bsontypes.cpp | 46 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.cpp | 43 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 7 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 94 | ||||
-rw-r--r-- | src/mongo/db/repl/freshness_checker_test.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_set_heartbeat_response_test.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/repl/replica_set_config_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/logger/parse_log_component_settings_test.cpp | 4 |
9 files changed, 179 insertions, 67 deletions
diff --git a/src/mongo/bson/bsontypes.cpp b/src/mongo/bson/bsontypes.cpp index 4797c260802..e1348b94f06 100644 --- a/src/mongo/bson/bsontypes.cpp +++ b/src/mongo/bson/bsontypes.cpp @@ -44,52 +44,52 @@ const BSONObj kMinBSONKey(kMinKeyData); const char* typeName(BSONType type) { switch (type) { case MinKey: - return "MinKey"; + return "minKey"; case EOO: - return "EOO"; + return "missing"; case NumberDouble: - return "NumberDouble"; + return "double"; case String: - return "String"; + return "string"; case Object: - return "Object"; + return "object"; case Array: - return "Array"; + return "array"; case BinData: - return "BinaryData"; + return "binData"; case Undefined: - return "Undefined"; + return "undefined"; case jstOID: - return "OID"; + return "objectId"; case Bool: - return "Bool"; + return "bool"; case Date: - return "Date"; + return "date"; case jstNULL: - return "NULL"; + return "null"; case RegEx: - return "RegEx"; + return "regex"; case DBRef: - return "DBRef"; + return "dbPointer"; case Code: - return "Code"; + return "javascript"; case Symbol: - return "Symbol"; + return "symbol"; case CodeWScope: - return "CodeWScope"; + return "javascriptWithScope"; case NumberInt: - return "NumberInt32"; + return "int"; case bsonTimestamp: - return "Timestamp"; + return "timestamp"; case NumberLong: - return "NumberLong64"; + return "long"; case NumberDecimal: - return "NumberDecimal128"; + return "decimal"; // JSTypeMax doesn't make sense to turn into a string; overlaps with highest-valued type case MaxKey: - return "MaxKey"; + return "maxKey"; default: - return "Invalid"; + return "invalid"; } } diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp index 042e4169609..491643ef6ec 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -393,30 +393,29 @@ bool ExistsMatchExpression::equivalent(const MatchExpression* other) const { const std::string TypeMatchExpression::kMatchesAllNumbersAlias = "number"; const std::unordered_map<std::string, BSONType> TypeMatchExpression::typeAliasMap = { - {"double", NumberDouble}, + {typeName(NumberDouble), NumberDouble}, + {typeName(String), String}, + {typeName(Object), Object}, + {typeName(Array), Array}, + {typeName(BinData), BinData}, + {typeName(Undefined), Undefined}, + {typeName(jstOID), jstOID}, + {typeName(Bool), Bool}, + {typeName(Date), Date}, + {typeName(jstNULL), jstNULL}, + {typeName(RegEx), RegEx}, + {typeName(DBRef), DBRef}, + {typeName(Code), Code}, + {typeName(Symbol), Symbol}, + {typeName(CodeWScope), CodeWScope}, + {typeName(NumberInt), NumberInt}, + {typeName(bsonTimestamp), bsonTimestamp}, + {typeName(NumberLong), NumberLong}, #ifdef MONGO_CONFIG_EXPERIMENTAL_DECIMAL_SUPPORT - {"decimal", NumberDecimal}, + {typeName(NumberDecimal), NumberDecimal}, #endif - {"string", String}, - {"object", Object}, - {"array", Array}, - {"binData", BinData}, - {"undefined", Undefined}, - {"objectId", jstOID}, - {"bool", Bool}, - {"date", Date}, - {"null", jstNULL}, - {"regex", RegEx}, - {"dbPointer", DBRef}, - {"javascript", Code}, - {"symbol", Symbol}, - {"javascriptWithScope", CodeWScope}, - {"int", NumberInt}, - {"timestamp", bsonTimestamp}, - {"long", NumberLong}, - {"decimal", NumberDecimal}, - {"maxKey", MaxKey}, - {"minKey", MinKey}}; + {typeName(MaxKey), MaxKey}, + {typeName(MinKey), MinKey}}; Status TypeMatchExpression::initWithBSONType(StringData path, int type) { if (!isValidBSONType(type)) { diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 179feac2b82..b84b529c62e 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -403,7 +403,7 @@ Value ExpressionAdd::evaluateInternal(Variables* vars) const { doubleTotal += val.coerceToDouble(); longTotal += val.coerceToLong(); } else if (val.getType() == Date) { - uassert(16612, "only one Date allowed in an $add expression", !haveDate); + uassert(16612, "only one date allowed in an $add expression", !haveDate); haveDate = true; // We don't manipulate totalType here. @@ -1642,7 +1642,7 @@ Value ExpressionFilter::evaluateInternal(Variables* vars) const { return Value(BSONNULL); uassert(28651, - str::stream() << "input to $filter must be an Array not " + str::stream() << "input to $filter must be an array not " << typeName(inputVal.getType()), inputVal.getType() == Array); @@ -1846,7 +1846,7 @@ Value ExpressionMap::evaluateInternal(Variables* vars) const { return Value(BSONNULL); uassert(16883, - str::stream() << "input to $map must be an Array not " << typeName(inputVal.getType()), + str::stream() << "input to $map must be an array not " << typeName(inputVal.getType()), inputVal.getType() == Array); const vector<Value>& input = inputVal.getArray(); @@ -1879,7 +1879,7 @@ void ExpressionMap::addDependencies(DepsTracker* deps, vector<string>* path) con REGISTER_EXPRESSION(meta, ExpressionMeta::parse); intrusive_ptr<Expression> ExpressionMeta::parse(BSONElement expr, const VariablesParseState& vpsIn) { - uassert(17307, "$meta only supports String arguments", expr.type() == String); + uassert(17307, "$meta only supports string arguments", expr.type() == String); if (expr.valueStringData() == "textScore") { return new ExpressionMeta(MetaType::TEXT_SCORE); } else if (expr.valueStringData() == "randVal") { @@ -2774,7 +2774,7 @@ Value ExpressionSlice::evaluateInternal(Variables* vars) const { } uassert(28724, - str::stream() << "First argument to $slice must be an Array, but is" + str::stream() << "First argument to $slice must be an array, but is" << " of type: " << typeName(arrayVal.getType()), arrayVal.getType() == Array); uassert(28725, @@ -2852,7 +2852,7 @@ Value ExpressionSize::evaluateInternal(Variables* vars) const { Value array = vpOperand[0]->evaluateInternal(vars); uassert(17124, - str::stream() << "The argument to $size must be an Array, but was of type: " + str::stream() << "The argument to $size must be an array, but was of type: " << typeName(array.getType()), array.getType() == Array); return Value::createIntOrLong(array.getArray().size()); @@ -3040,6 +3040,18 @@ const char* ExpressionTrunc::getOpName() const { return "$trunc"; } +/* ------------------------- ExpressionType ----------------------------- */ + +Value ExpressionType::evaluateInternal(Variables* vars) const { + Value val(vpOperand[0]->evaluateInternal(vars)); + return Value(typeName(val.getType())); +} + +REGISTER_EXPRESSION(type, ExpressionType::parse); +const char* ExpressionType::getOpName() const { + return "$type"; +} + /* ------------------------- ExpressionWeek ----------------------------- */ Value ExpressionWeek::evaluateInternal(Variables* vars) const { diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index da8254c8ba4..d0f1370daf7 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -1263,6 +1263,13 @@ public: }; +class ExpressionType final : public ExpressionFixedArity<ExpressionType, 1> { +public: + Value evaluateInternal(Variables* vars) const final; + const char* getOpName() const final; +}; + + class ExpressionWeek final : public ExpressionFixedArity<ExpressionWeek, 1> { public: Value evaluateInternal(Variables* vars) const final; diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index cac721ded66..63a6375e85a 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -4345,6 +4345,100 @@ class DropEndingNull : public ExpectedResultBase { } // namespace Substr +namespace Type { + +TEST(ExpressionTypeTest, WithMinKeyValue) { + assertExpectedResults("$type", {{{Value(MINKEY)}, Value("minKey")}}); +} + +TEST(ExpressionTypeTest, WithDoubleValue) { + assertExpectedResults("$type", {{{Value(1.0)}, Value("double")}}); +} + +TEST(ExpressionTypeTest, WithStringValue) { + assertExpectedResults("$type", {{{Value("stringValue")}, Value("string")}}); +} + +TEST(ExpressionTypeTest, WithObjectValue) { + BSONObj objectVal = fromjson("{a: {$literal: 1}}"); + assertExpectedResults("$type", {{{Value(objectVal)}, Value("object")}}); +} + +TEST(ExpressionTypeTest, WithArrayValue) { + assertExpectedResults("$type", {{{Value(BSON_ARRAY(1 << 2))}, Value("array")}}); +} + +TEST(ExpressionTypeTest, WithBinDataValue) { + BSONBinData binDataVal = BSONBinData("", 0, BinDataGeneral); + assertExpectedResults("$type", {{{Value(binDataVal)}, Value("binData")}}); +} + +TEST(ExpressionTypeTest, WithUndefinedValue) { + assertExpectedResults("$type", {{{Value(BSONUndefined)}, Value("undefined")}}); +} + +TEST(ExpressionTypeTest, WithOIDValue) { + assertExpectedResults("$type", {{{Value(OID())}, Value("objectId")}}); +} + +TEST(ExpressionTypeTest, WithBoolValue) { + assertExpectedResults("$type", {{{Value(true)}, Value("bool")}}); +} + +TEST(ExpressionTypeTest, WithDateValue) { + Date_t dateVal = BSON("" << DATENOW).firstElement().Date(); + assertExpectedResults("$type", {{{Value(dateVal)}, Value("date")}}); +} + +TEST(ExpressionTypeTest, WithNullValue) { + assertExpectedResults("$type", {{{Value(BSONNULL)}, Value("null")}}); +} + +TEST(ExpressionTypeTest, WithRegexValue) { + assertExpectedResults("$type", {{{Value(BSONRegEx("a.b"))}, Value("regex")}}); +} + +TEST(ExpressionTypeTest, WithSymbolValue) { + assertExpectedResults("$type", {{{Value(BSONSymbol("a"))}, Value("symbol")}}); +} + +TEST(ExpressionTypeTest, WithDBRefValue) { + assertExpectedResults("$type", {{{Value(BSONDBRef("", OID()))}, Value("dbPointer")}}); +} + +TEST(ExpressionTypeTest, WithCodeWScopeValue) { + assertExpectedResults( + "$type", {{{Value(BSONCodeWScope("var x = 3", BSONObj()))}, Value("javascriptWithScope")}}); +} + +TEST(ExpressionTypeTest, WithCodeValue) { + assertExpectedResults("$type", {{{Value(BSONCode("var x = 3"))}, Value("javascript")}}); +} + +TEST(ExpressionTypeTest, WithIntValue) { + assertExpectedResults("$type", {{{Value(1)}, Value("int")}}); +} + +#ifdef MONGO_CONFIG_EXPERIMENTAL_DECIMAL_SUPPORT +TEST(ExpressionTypeTest, WithDecimalValue) { + assertExpectedResults("$type", {{{Value(Decimal128(0.3))}, Value("decimal")}}); +} +#endif + +TEST(ExpressionTypeTest, WithLongValue) { + assertExpectedResults("$type", {{{Value(1LL)}, Value("long")}}); +} + +TEST(ExpressionTypeTest, WithTimestampValue) { + assertExpectedResults("$type", {{{Value(Timestamp(0, 0))}, Value("timestamp")}}); +} + +TEST(ExpressionTypeTest, WithMaxKeyValue) { + assertExpectedResults("$type", {{{Value(MAXKEY)}, Value("maxKey")}}); +} + +} // namespace Type + namespace ToLower { class ExpectedResultBase { diff --git a/src/mongo/db/repl/freshness_checker_test.cpp b/src/mongo/db/repl/freshness_checker_test.cpp index a8a269b627b..9f20ec0101c 100644 --- a/src/mongo/db/repl/freshness_checker_test.cpp +++ b/src/mongo/db/repl/freshness_checker_test.cpp @@ -357,7 +357,7 @@ TEST_F(FreshnessCheckerTest, ElectWrongTypeInFreshnessResponse) { ASSERT_EQUALS(1, countLogLinesContaining( "wrong type for opTime argument in replSetFresh " - "response: NumberInt32")); + "response: int")); } TEST_F(FreshnessCheckerTest, ElectVetoed) { @@ -601,7 +601,7 @@ TEST_F(FreshnessCheckerTest, ElectWrongTypeInFreshnessResponseManyNodes) { ASSERT_EQUALS(1, countLogLinesContaining( "wrong type for opTime argument in replSetFresh " - "response: NumberInt32")); + "response: int")); } TEST_F(FreshnessCheckerTest, ElectVetoedManyNodes) { diff --git a/src/mongo/db/repl/repl_set_heartbeat_response_test.cpp b/src/mongo/db/repl/repl_set_heartbeat_response_test.cpp index 3c7adf479ee..87634a155a5 100644 --- a/src/mongo/db/repl/repl_set_heartbeat_response_test.cpp +++ b/src/mongo/db/repl/repl_set_heartbeat_response_test.cpp @@ -606,7 +606,7 @@ TEST(ReplSetHeartbeatResponse, InitializeWrongElectionTimeType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"electionTime\" field in response to replSetHeartbeat command to " - "have type Date or Timestamp, but found type String", + "have type Date or Timestamp, but found type string", result.reason()); } @@ -618,7 +618,7 @@ TEST(ReplSetHeartbeatResponse, InitializeWrongTimeType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"time\" field in response to replSetHeartbeat command to " - "have a numeric type, but found type String", + "have a numeric type, but found type string", result.reason()); } @@ -628,13 +628,13 @@ TEST(ReplSetHeartbeatResponse, InitializeWrongDurableOpTimeType) { << "hello"); Status result = hbResponse.initialize(initializerObj, 0); ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); - ASSERT_EQUALS("\"durableOpTime\" had the wrong type. Expected Object, found String", + ASSERT_EQUALS("\"durableOpTime\" had the wrong type. Expected object, found string", result.reason()); BSONObj initializerObj2 = BSON("ok" << 1.0 << "durableOpTime" << OpTime().getTimestamp()); Status result2 = hbResponse.initialize(initializerObj2, 0); ASSERT_EQUALS(ErrorCodes::TypeMismatch, result2); - ASSERT_EQUALS("\"durableOpTime\" had the wrong type. Expected Object, found Timestamp", + ASSERT_EQUALS("\"durableOpTime\" had the wrong type. Expected object, found timestamp", result2.reason()); } @@ -646,7 +646,7 @@ TEST(ReplSetHeartbeatResponse, InitializeWrongAppliedOpTimeType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"opTime\" field in response to replSetHeartbeat command to " - "have type Date or Timestamp, but found type String", + "have type Date or Timestamp, but found type string", result.reason()); } @@ -658,7 +658,7 @@ TEST(ReplSetHeartbeatResponse, InitializeMemberStateWrongType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"state\" field in response to replSetHeartbeat command to " - "have type NumberInt or NumberLong, but found type String", + "have type NumberInt or NumberLong, but found type string", result.reason()); } @@ -692,7 +692,7 @@ TEST(ReplSetHeartbeatResponse, InitializeVersionWrongType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"v\" field in response to replSetHeartbeat to " - "have type NumberInt, but found String", + "have type NumberInt, but found string", result.reason()); } @@ -705,7 +705,7 @@ TEST(ReplSetHeartbeatResponse, InitializeReplSetNameWrongType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"set\" field in response to replSetHeartbeat to " - "have type String, but found NumberInt32", + "have type String, but found int", result.reason()); } @@ -718,7 +718,7 @@ TEST(ReplSetHeartbeatResponse, InitializeHeartbeatMeessageWrongType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"hbmsg\" field in response to replSetHeartbeat to " - "have type String, but found NumberInt32", + "have type String, but found int", result.reason()); } @@ -731,7 +731,7 @@ TEST(ReplSetHeartbeatResponse, InitializeSyncingToWrongType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"syncingTo\" field in response to replSetHeartbeat to " - "have type String, but found NumberInt32", + "have type String, but found int", result.reason()); } @@ -744,7 +744,7 @@ TEST(ReplSetHeartbeatResponse, InitializeConfigWrongType) { ASSERT_EQUALS(ErrorCodes::TypeMismatch, result); ASSERT_EQUALS( "Expected \"config\" in response to replSetHeartbeat to " - "have type Object, but found NumberInt32", + "have type Object, but found int", result.reason()); } diff --git a/src/mongo/db/repl/replica_set_config_test.cpp b/src/mongo/db/repl/replica_set_config_test.cpp index a140967c562..651225e8ed9 100644 --- a/src/mongo/db/repl/replica_set_config_test.cpp +++ b/src/mongo/db/repl/replica_set_config_test.cpp @@ -1402,7 +1402,7 @@ TEST(ReplicaSetConfig, ReplSetId) { << BSON("replicaSetId" << 12345))); ASSERT_EQUALS(ErrorCodes::TypeMismatch, status); ASSERT_STRING_CONTAINS(status.reason(), - "\"replicaSetId\" had the wrong type. Expected OID, found NumberInt32"); + "\"replicaSetId\" had the wrong type. Expected objectId, found int"); } } // namespace diff --git a/src/mongo/logger/parse_log_component_settings_test.cpp b/src/mongo/logger/parse_log_component_settings_test.cpp index 96f0864a692..4f32825361a 100644 --- a/src/mongo/logger/parse_log_component_settings_test.cpp +++ b/src/mongo/logger/parse_log_component_settings_test.cpp @@ -69,7 +69,7 @@ TEST(Flat, FailNonNumeric) { ASSERT_NOT_OK(result.getStatus()); ASSERT_EQUALS(ErrorCodes::BadValue, result.getStatus().code()); ASSERT_EQUALS(result.getStatus().reason(), - "Expected default.verbosity to be a number, but found String"); + "Expected default.verbosity to be a number, but found string"); } TEST(Flat, FailBadComponent) { @@ -102,7 +102,7 @@ TEST(Nested, FailNonNumeric) { ASSERT_NOT_OK(result.getStatus()); ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); ASSERT_EQUALS(result.getStatus().reason(), - "Expected accessControl.verbosity to be a number, but found String"); + "Expected accessControl.verbosity to be a number, but found string"); } TEST(Nested, FailBadComponent) { |