summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/pipeline/expression.cpp52
-rw-r--r--src/mongo/db/pipeline/expression.h12
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp53
3 files changed, 117 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 7dc91bdd4fe..6b4fd5ac6ee 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -3166,6 +3166,58 @@ const char* ExpressionSubstrCP::getOpName() const {
return "$substrCP";
}
+/* ----------------------- ExpressionStrLenBytes ------------------------- */
+
+Value ExpressionStrLenBytes::evaluateInternal(Variables* vars) const {
+ Value str(vpOperand[0]->evaluateInternal(vars));
+
+ uassert(34473,
+ str::stream() << "$strLenBytes requires a string argument, found: "
+ << typeName(str.getType()),
+ str.getType() == String);
+
+ size_t strLen = str.getString().size();
+
+ uassert(34470,
+ "string length could not be represented as an int.",
+ strLen <= std::numeric_limits<int>::max());
+ return Value(static_cast<int>(strLen));
+}
+
+REGISTER_EXPRESSION(strLenBytes, ExpressionStrLenBytes::parse);
+const char* ExpressionStrLenBytes::getOpName() const {
+ return "$strLenBytes";
+}
+
+/* ----------------------- ExpressionStrLenCP ------------------------- */
+
+Value ExpressionStrLenCP::evaluateInternal(Variables* vars) const {
+ Value val(vpOperand[0]->evaluateInternal(vars));
+
+ uassert(
+ 34471,
+ str::stream() << "$strLenCP requires a string argument, found: " << typeName(val.getType()),
+ val.getType() == String);
+
+ std::string stringVal = val.getString();
+
+ size_t strLen = 0;
+ for (char byte : stringVal) {
+ strLen += !isContinuationByte(byte);
+ }
+
+ uassert(34472,
+ "string length could not be represented as an int.",
+ strLen <= std::numeric_limits<int>::max());
+
+ return Value(static_cast<int>(strLen));
+}
+
+REGISTER_EXPRESSION(strLenCP, ExpressionStrLenCP::parse);
+const char* ExpressionStrLenCP::getOpName() const {
+ return "$strLenCP";
+}
+
/* ----------------------- ExpressionSubtract ---------------------------- */
Value ExpressionSubtract::evaluateInternal(Variables* vars) const {
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index fe3e57b0d4a..0939bcbcdc7 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -1259,6 +1259,18 @@ public:
};
+class ExpressionStrLenBytes final : public ExpressionFixedArity<ExpressionStrLenBytes, 1> {
+ Value evaluateInternal(Variables* vars) const final;
+ const char* getOpName() const final;
+};
+
+
+class ExpressionStrLenCP final : public ExpressionFixedArity<ExpressionStrLenCP, 1> {
+ Value evaluateInternal(Variables* vars) const final;
+ const char* getOpName() const final;
+};
+
+
class ExpressionSubtract final : public ExpressionFixedArity<ExpressionSubtract, 2> {
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 ed6625cc594..457cc1e4d8c 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -4294,6 +4294,59 @@ class NullMiddleGt : public ExpectedResultBase {
} // namespace Strcasecmp
+namespace StrLenBytes {
+
+TEST(ExpressionStrLenBytes, ComputesLengthOfString) {
+ assertExpectedResults("$strLenBytes", {{{Value("abc")}, Value(3)}});
+}
+
+TEST(ExpressionStrLenBytes, ComputesLengthOfEmptyString) {
+ assertExpectedResults("$strLenBytes", {{{Value("")}, Value(0)}});
+}
+
+TEST(ExpressionStrLenBytes, ComputesLengthOfStringWithNull) {
+ assertExpectedResults("$strLenBytes",
+ {{{Value(StringData("ab\0c", StringData::LiteralTag()))}, Value(4)}});
+}
+
+TEST(ExpressionStrLenCP, ComputesLengthOfStringWithNullAtEnd) {
+ assertExpectedResults("$strLenBytes",
+ {{{Value(StringData("abc\0", StringData::LiteralTag()))}, Value(4)}});
+}
+
+} // namespace StrLenBytes
+
+namespace StrLenCP {
+
+TEST(ExpressionStrLenCP, ComputesLengthOfASCIIString) {
+ assertExpectedResults("$strLenCP", {{{Value("abc")}, Value(3)}});
+}
+
+TEST(ExpressionStrLenCP, ComputesLengthOfEmptyString) {
+ assertExpectedResults("$strLenCP", {{{Value("")}, Value(0)}});
+}
+
+TEST(ExpressionStrLenCP, ComputesLengthOfStringWithNull) {
+ assertExpectedResults("$strLenCP",
+ {{{Value(StringData("ab\0c", StringData::LiteralTag()))}, Value(4)}});
+}
+
+TEST(ExpressionStrLenCP, ComputesLengthOfStringWithNullAtEnd) {
+ assertExpectedResults("$strLenCP",
+ {{{Value(StringData("abc\0", StringData::LiteralTag()))}, Value(4)}});
+}
+
+TEST(ExpressionStrLenCP, ComputesLengthOfStringWithAccent) {
+ assertExpectedResults("$strLenCP",
+ {{{Value(StringData("a\0bâ", StringData::LiteralTag()))}, Value(4)}});
+}
+
+TEST(ExpressionStrLenCP, ComputesLengthOfStringWithSpecialCharacters) {
+ assertExpectedResults("$strLenCP", {{{Value("ºabøåß")}, Value(6)}});
+}
+
+} // namespace StrLenCP
+
namespace SubstrBytes {
class ExpectedResultBase {