summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorBenjamin Murphy <benjamin_murphy@me.com>2016-04-12 18:52:11 -0400
committerBenjamin Murphy <benjamin_murphy@me.com>2016-04-21 11:23:15 -0400
commit0dfb47dd678b872a425f26c073e72652bbcde90e (patch)
tree367f1793231b850e42660ddbc90b306eeea32203 /src/mongo
parent1333f35d0f6060d5b053c6868cf0b701850ac155 (diff)
downloadmongo-0dfb47dd678b872a425f26c073e72652bbcde90e.tar.gz
SERVER-17258 Aggregation now supports the reduce expression.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/pipeline/expression.cpp82
-rw-r--r--src/mongo/db/pipeline/expression.h19
2 files changed, 101 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 4f1f050478b..9eb2497cd27 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -2572,6 +2572,88 @@ const char* ExpressionRange::getOpName() const {
return "$range";
}
+/* ------------------------ ExpressionReduce ------------------------------ */
+
+REGISTER_EXPRESSION(reduce, ExpressionReduce::parse);
+intrusive_ptr<Expression> ExpressionReduce::parse(BSONElement expr,
+ const VariablesParseState& vps) {
+ uassert(40075,
+ str::stream() << "$reduce requires an object as an argument, found: "
+ << typeName(expr.type()),
+ expr.type() == Object);
+
+ intrusive_ptr<ExpressionReduce> reduce(new ExpressionReduce());
+
+ // vpsSub is used only to parse 'in', which must have access to $$this and $$value.
+ VariablesParseState vpsSub(vps);
+ reduce->_thisVar = vpsSub.defineVariable("this");
+ reduce->_valueVar = vpsSub.defineVariable("value");
+
+ for (auto&& elem : expr.Obj()) {
+ auto field = elem.fieldNameStringData();
+
+ if (field == "input") {
+ reduce->_input = parseOperand(elem, vps);
+ } else if (field == "initialValue") {
+ reduce->_initial = parseOperand(elem, vps);
+ } else if (field == "in") {
+ reduce->_in = parseOperand(elem, vpsSub);
+ } else {
+ uasserted(40076, str::stream() << "$reduce found an unknown argument: " << field);
+ }
+ }
+
+ uassert(40077, "$reduce requires 'input' to be specified", reduce->_input);
+ uassert(40078, "$reduce requires 'initialValue' to be specified", reduce->_initial);
+ uassert(40079, "$reduce requires 'in' to be specified", reduce->_in);
+
+ return reduce;
+}
+
+Value ExpressionReduce::evaluateInternal(Variables* vars) const {
+ Value inputVal = _input->evaluateInternal(vars);
+
+ if (inputVal.nullish()) {
+ return Value(BSONNULL);
+ }
+
+ uassert(40080,
+ str::stream() << "$reduce requires that 'input' be an array, found: "
+ << inputVal.toString(),
+ inputVal.isArray());
+
+ Value accumulatedValue = _initial->evaluateInternal(vars);
+
+ for (auto&& elem : inputVal.getArray()) {
+ vars->setValue(_thisVar, elem);
+ vars->setValue(_valueVar, accumulatedValue);
+
+ accumulatedValue = _in->evaluateInternal(vars);
+ }
+
+ return accumulatedValue;
+}
+
+intrusive_ptr<Expression> ExpressionReduce::optimize() {
+ _input = _input->optimize();
+ _initial = _initial->optimize();
+ _in = _in->optimize();
+ return this;
+}
+
+void ExpressionReduce::addDependencies(DepsTracker* deps, vector<string>* path) const {
+ _input->addDependencies(deps);
+ _initial->addDependencies(deps);
+ _in->addDependencies(deps);
+}
+
+Value ExpressionReduce::serialize(bool explain) const {
+ return Value(Document{{"$reduce",
+ Document{{"input", _input->serialize(explain)},
+ {"initialValue", _initial->serialize(explain)},
+ {"in", _in->serialize(explain)}}}});
+}
+
/* ------------------------ ExpressionReverseArray ------------------------ */
Value ExpressionReverseArray::evaluateInternal(Variables* vars) const {
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index a5c78babce1..1642ee8cb89 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -1135,6 +1135,25 @@ class ExpressionRange final : public ExpressionRangedArity<ExpressionRange, 2, 3
};
+class ExpressionReduce final : public Expression {
+public:
+ void addDependencies(DepsTracker* deps, std::vector<std::string>* path = nullptr) const final;
+ Value evaluateInternal(Variables* vars) const final;
+ boost::intrusive_ptr<Expression> optimize() final;
+ static boost::intrusive_ptr<Expression> parse(BSONElement expr,
+ const VariablesParseState& vpsIn);
+ Value serialize(bool explain) const final;
+
+private:
+ boost::intrusive_ptr<Expression> _input;
+ boost::intrusive_ptr<Expression> _initial;
+ boost::intrusive_ptr<Expression> _in;
+
+ Variables::Id _valueVar;
+ Variables::Id _thisVar;
+};
+
+
class ExpressionSecond final : public ExpressionFixedArity<ExpressionSecond, 1> {
public:
Value evaluateInternal(Variables* vars) const final;