summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/pipeline/expression.cpp66
-rw-r--r--src/mongo/db/pipeline/expression.h7
2 files changed, 73 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 465c98aa450..f462052d856 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -3069,6 +3069,72 @@ const char* ExpressionSize::getOpName() const {
return "$size";
}
+/* ----------------------- ExpressionSplit --------------------------- */
+
+namespace {
+
+bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::string& token) {
+ if (token.size() + index > input.size()) {
+ return false;
+ }
+
+ return input.compare(index, token.size(), token) == 0;
+}
+
+} // namespace
+
+Value ExpressionSplit::evaluateInternal(Variables* vars) const {
+ Value inputArg = vpOperand[0]->evaluateInternal(vars);
+ Value separatorArg = vpOperand[1]->evaluateInternal(vars);
+
+ if (inputArg.nullish() || separatorArg.nullish()) {
+ return Value(BSONNULL);
+ }
+
+ uassert(40085,
+ str::stream() << "$split requires an expression that evaluates to a string as a first "
+ "argument, found: " << typeName(inputArg.getType()),
+ inputArg.getType() == BSONType::String);
+ uassert(40086,
+ str::stream() << "$split requires an expression that evaluates to a string as a second "
+ "argument, found: " << typeName(separatorArg.getType()),
+ separatorArg.getType() == BSONType::String);
+
+ std::string input = inputArg.getString();
+ std::string separator = separatorArg.getString();
+
+ uassert(40087, "$split requires a non-empty separator", !separator.empty());
+
+ std::vector<Value> output;
+
+ // Keep track of the index at which the current output string began.
+ size_t splitStartIndex = 0;
+
+ // Iterate through 'input' and check to see if 'separator' matches at any point.
+ for (size_t i = 0; i < input.size();) {
+ if (stringHasTokenAtIndex(i, input, separator)) {
+ // We matched; add the current string to our output and jump ahead.
+ StringData splitString(input.c_str() + splitStartIndex, i - splitStartIndex);
+ output.push_back(Value(splitString));
+ i += separator.size();
+ splitStartIndex = i;
+ } else {
+ // We did not match, continue to the next character.
+ ++i;
+ }
+ }
+
+ StringData splitString(input.c_str() + splitStartIndex, input.size() - splitStartIndex);
+ output.push_back(Value(splitString));
+
+ return Value(output);
+}
+
+REGISTER_EXPRESSION(split, ExpressionSplit::parse);
+const char* ExpressionSplit::getOpName() const {
+ return "$split";
+}
+
/* ----------------------- ExpressionSqrt ---------------------------- */
Value ExpressionSqrt::evaluateNumericArg(const Value& numericArg) const {
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index f17fd5a93d6..777fbd4dc8c 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -1258,6 +1258,13 @@ public:
};
+class ExpressionSplit final : public ExpressionFixedArity<ExpressionSplit, 2> {
+public:
+ Value evaluateInternal(Variables* vars) const final;
+ const char* getOpName() const final;
+};
+
+
class ExpressionSqrt final : public ExpressionSingleNumericArg<ExpressionSqrt> {
Value evaluateNumericArg(const Value& numericArg) const final;
const char* getOpName() const final;