summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Murphy <benjamin_murphy@me.com>2016-03-02 16:51:17 -0500
committerBenjamin Murphy <benjamin_murphy@me.com>2016-03-15 11:27:33 -0400
commita9d3450f9a40c6ef02b5713c9f0841aa424a327a (patch)
treeac64a6daf70e1b4078c9dc915e41032b08238ab8
parentfea8935aa03a68524014e5d0697ca5e55d390a2b (diff)
downloadmongo-a9d3450f9a40c6ef02b5713c9f0841aa424a327a.tar.gz
SERVER-13447 Added type expression to aggregation.
-rw-r--r--src/mongo/bson/bsontypes.cpp46
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp43
-rw-r--r--src/mongo/db/pipeline/expression.cpp24
-rw-r--r--src/mongo/db/pipeline/expression.h7
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp94
-rw-r--r--src/mongo/db/repl/freshness_checker_test.cpp4
-rw-r--r--src/mongo/db/repl/repl_set_heartbeat_response_test.cpp22
-rw-r--r--src/mongo/db/repl/replica_set_config_test.cpp2
-rw-r--r--src/mongo/logger/parse_log_component_settings_test.cpp4
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) {