diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 66 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 7 |
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; |