summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Swanson <charlie.swanson@mongodb.com>2015-07-01 16:47:20 -0400
committerCharlie Swanson <charlie.swanson@mongodb.com>2015-07-13 17:41:39 -0400
commit1527a67262baf2d80776f86d4af0e42d53aa3eec (patch)
tree802170077582f8d30a12e548b2e6ce6578275443
parent908313432e597623361df39339cee452176fd7b2 (diff)
downloadmongo-1527a67262baf2d80776f86d4af0e42d53aa3eec.tar.gz
SERVER-19105 Add macro to register Accumulators, move REGISTER_EXPRESSION to header
-rw-r--r--src/mongo/db/pipeline/SConscript1
-rw-r--r--src/mongo/db/pipeline/accumulator.cpp62
-rw-r--r--src/mongo/db/pipeline/accumulator.h34
-rw-r--r--src/mongo/db/pipeline/accumulator_add_to_set.cpp10
-rw-r--r--src/mongo/db/pipeline/accumulator_avg.cpp10
-rw-r--r--src/mongo/db/pipeline/accumulator_first.cpp10
-rw-r--r--src/mongo/db/pipeline/accumulator_last.cpp10
-rw-r--r--src/mongo/db/pipeline/accumulator_min_max.cpp15
-rw-r--r--src/mongo/db/pipeline/accumulator_push.cpp10
-rw-r--r--src/mongo/db/pipeline/accumulator_std_dev.cpp11
-rw-r--r--src/mongo/db/pipeline/accumulator_sum.cpp10
-rw-r--r--src/mongo/db/pipeline/document_source.h11
-rw-r--r--src/mongo/db/pipeline/document_source_group.cpp48
-rw-r--r--src/mongo/db/pipeline/expression.cpp146
-rw-r--r--src/mongo/db/pipeline/expression.h25
15 files changed, 251 insertions, 162 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index ed3fc5021ac..c95676f9d2b 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -66,6 +66,7 @@ env.Library(
env.Library(
target='accumulator',
source=[
+ 'accumulator.cpp',
'accumulator_add_to_set.cpp',
'accumulator_avg.cpp',
'accumulator_first.cpp',
diff --git a/src/mongo/db/pipeline/accumulator.cpp b/src/mongo/db/pipeline/accumulator.cpp
new file mode 100644
index 00000000000..f5b21798f5a
--- /dev/null
+++ b/src/mongo/db/pipeline/accumulator.cpp
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2011 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <string>
+
+#include "mongo/db/pipeline/accumulator.h"
+#include "mongo/db/pipeline/value.h"
+#include "mongo/util/string_map.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+using Factory = Accumulator::Factory;
+
+namespace {
+// Used to keep track of which Accumulators are registered under which name.
+static StringMap<Factory> factoryMap;
+} // namespace
+
+void Accumulator::registerAccumulator(std::string name, Factory factory) {
+ auto it = factoryMap.find(name);
+ massert(28722,
+ str::stream() << "Duplicate accumulator (" << name << ") registered.",
+ it == factoryMap.end());
+ factoryMap[name] = factory;
+}
+
+Factory Accumulator::getFactory(StringData name) {
+ auto it = factoryMap.find(name);
+ uassert(
+ 15952, str::stream() << "unknown group operator '" << name << "'", it != factoryMap.end());
+ return it->second;
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/pipeline/accumulator.h b/src/mongo/db/pipeline/accumulator.h
index 125544c5ef4..64e8aed036c 100644
--- a/src/mongo/db/pipeline/accumulator.h
+++ b/src/mongo/db/pipeline/accumulator.h
@@ -34,12 +34,30 @@
#include <unordered_set>
#include <vector>
+#include "mongo/base/init.h"
#include "mongo/bson/bsontypes.h"
#include "mongo/db/pipeline/value.h"
+#include "mongo/stdx/functional.h"
namespace mongo {
+/**
+ * Registers an Accumulator to have the name 'key'. When an accumulator with name '$key' is found
+ * during parsing of a $group stage, 'factory' will be called to construct the Accumulator.
+ *
+ * As an example, if your accumulator looks like {"$foo": <args>}, with a factory method 'create',
+ * you would add this line:
+ * REGISTER_EXPRESSION(foo, AccumulatorFoo::create);
+ */
+#define REGISTER_ACCUMULATOR(key, factory) \
+ MONGO_INITIALIZER(addToAccumulatorFactoryMap_##key)(InitializerContext*) { \
+ Accumulator::registerAccumulator("$" #key, (factory)); \
+ return Status::OK(); \
+ }
+
class Accumulator : public RefCountable {
public:
+ using Factory = boost::intrusive_ptr<Accumulator>(*)();
+
Accumulator() = default;
/** Process input and update internal state.
@@ -65,6 +83,22 @@ public:
/// Reset this accumulator to a fresh state ready to receive input.
virtual void reset() = 0;
+ /**
+ * Registers an Accumulator with a parsing function, so that when an accumulator with the given
+ * name is encountered during parsing of the $group stage, it will call 'factory' to construct
+ * that Accumulator.
+ *
+ * DO NOT call this method directly. Instead, use the REGISTER_ACCUMULATOR macro defined in this
+ * file.
+ */
+ static void registerAccumulator(std::string name, Factory factory);
+
+ /**
+ * Retrieves the Factory for the accumulator specified by the given name, and raises an error if
+ * there is no such Accumulator registered.
+ */
+ static Factory getFactory(StringData name);
+
protected:
/// Update subclass's internal state based on input
virtual void processInternal(const Value& input, bool merging) = 0;
diff --git a/src/mongo/db/pipeline/accumulator_add_to_set.cpp b/src/mongo/db/pipeline/accumulator_add_to_set.cpp
index bf2bc183afd..313624bfdf0 100644
--- a/src/mongo/db/pipeline/accumulator_add_to_set.cpp
+++ b/src/mongo/db/pipeline/accumulator_add_to_set.cpp
@@ -37,6 +37,12 @@ namespace mongo {
using boost::intrusive_ptr;
using std::vector;
+REGISTER_ACCUMULATOR(addToSet, AccumulatorAddToSet::create);
+
+const char* AccumulatorAddToSet::getOpName() const {
+ return "$addToSet";
+}
+
void AccumulatorAddToSet::processInternal(const Value& input, bool merging) {
if (!merging) {
if (!input.missing()) {
@@ -78,8 +84,4 @@ void AccumulatorAddToSet::reset() {
intrusive_ptr<Accumulator> AccumulatorAddToSet::create() {
return new AccumulatorAddToSet();
}
-
-const char* AccumulatorAddToSet::getOpName() const {
- return "$addToSet";
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_avg.cpp b/src/mongo/db/pipeline/accumulator_avg.cpp
index ad027e7709d..cf2d5c9b677 100644
--- a/src/mongo/db/pipeline/accumulator_avg.cpp
+++ b/src/mongo/db/pipeline/accumulator_avg.cpp
@@ -37,6 +37,12 @@ namespace mongo {
using boost::intrusive_ptr;
+REGISTER_ACCUMULATOR(avg, AccumulatorAvg::create);
+
+const char* AccumulatorAvg::getOpName() const {
+ return "$avg";
+}
+
namespace {
const char subTotalName[] = "subTotal";
const char countName[] = "count";
@@ -83,8 +89,4 @@ void AccumulatorAvg::reset() {
_total = 0;
_count = 0;
}
-
-const char* AccumulatorAvg::getOpName() const {
- return "$avg";
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_first.cpp b/src/mongo/db/pipeline/accumulator_first.cpp
index 9425199793f..5c929443b69 100644
--- a/src/mongo/db/pipeline/accumulator_first.cpp
+++ b/src/mongo/db/pipeline/accumulator_first.cpp
@@ -35,6 +35,12 @@ namespace mongo {
using boost::intrusive_ptr;
+REGISTER_ACCUMULATOR(first, AccumulatorFirst::create);
+
+const char* AccumulatorFirst::getOpName() const {
+ return "$first";
+}
+
void AccumulatorFirst::processInternal(const Value& input, bool merging) {
/* only remember the first value seen */
if (!_haveFirst) {
@@ -63,8 +69,4 @@ void AccumulatorFirst::reset() {
intrusive_ptr<Accumulator> AccumulatorFirst::create() {
return new AccumulatorFirst();
}
-
-const char* AccumulatorFirst::getOpName() const {
- return "$first";
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_last.cpp b/src/mongo/db/pipeline/accumulator_last.cpp
index c9f4b487d1c..5dee2ec581c 100644
--- a/src/mongo/db/pipeline/accumulator_last.cpp
+++ b/src/mongo/db/pipeline/accumulator_last.cpp
@@ -35,6 +35,12 @@ namespace mongo {
using boost::intrusive_ptr;
+REGISTER_ACCUMULATOR(last, AccumulatorLast::create);
+
+const char* AccumulatorLast::getOpName() const {
+ return "$last";
+}
+
void AccumulatorLast::processInternal(const Value& input, bool merging) {
/* always remember the last value seen */
_last = input;
@@ -57,8 +63,4 @@ void AccumulatorLast::reset() {
intrusive_ptr<Accumulator> AccumulatorLast::create() {
return new AccumulatorLast();
}
-
-const char* AccumulatorLast::getOpName() const {
- return "$last";
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_min_max.cpp b/src/mongo/db/pipeline/accumulator_min_max.cpp
index da4f280f797..9bfbf7b380b 100644
--- a/src/mongo/db/pipeline/accumulator_min_max.cpp
+++ b/src/mongo/db/pipeline/accumulator_min_max.cpp
@@ -35,6 +35,15 @@ namespace mongo {
using boost::intrusive_ptr;
+REGISTER_ACCUMULATOR(max, AccumulatorMinMax::createMax);
+REGISTER_ACCUMULATOR(min, AccumulatorMinMax::createMin);
+
+const char* AccumulatorMinMax::getOpName() const {
+ if (_sense == 1)
+ return "$min";
+ return "$max";
+}
+
void AccumulatorMinMax::processInternal(const Value& input, bool merging) {
// nullish values should have no impact on result
if (!input.nullish()) {
@@ -67,10 +76,4 @@ intrusive_ptr<Accumulator> AccumulatorMinMax::createMin() {
intrusive_ptr<Accumulator> AccumulatorMinMax::createMax() {
return new AccumulatorMinMax(Sense::MAX);
}
-
-const char* AccumulatorMinMax::getOpName() const {
- if (_sense == 1)
- return "$min";
- return "$max";
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_push.cpp b/src/mongo/db/pipeline/accumulator_push.cpp
index b19ec08f71d..5d1da33730c 100644
--- a/src/mongo/db/pipeline/accumulator_push.cpp
+++ b/src/mongo/db/pipeline/accumulator_push.cpp
@@ -37,6 +37,12 @@ namespace mongo {
using boost::intrusive_ptr;
using std::vector;
+REGISTER_ACCUMULATOR(push, AccumulatorPush::create);
+
+const char* AccumulatorPush::getOpName() const {
+ return "$push";
+}
+
void AccumulatorPush::processInternal(const Value& input, bool merging) {
if (!merging) {
if (!input.missing()) {
@@ -75,8 +81,4 @@ void AccumulatorPush::reset() {
intrusive_ptr<Accumulator> AccumulatorPush::create() {
return new AccumulatorPush();
}
-
-const char* AccumulatorPush::getOpName() const {
- return "$push";
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_std_dev.cpp b/src/mongo/db/pipeline/accumulator_std_dev.cpp
index b51a21bbe6f..00922345ed9 100644
--- a/src/mongo/db/pipeline/accumulator_std_dev.cpp
+++ b/src/mongo/db/pipeline/accumulator_std_dev.cpp
@@ -36,6 +36,13 @@
namespace mongo {
using boost::intrusive_ptr;
+REGISTER_ACCUMULATOR(stdDevPop, AccumulatorStdDev::createPop);
+REGISTER_ACCUMULATOR(stdDevSamp, AccumulatorStdDev::createSamp);
+
+const char* AccumulatorStdDev::getOpName() const {
+ return (_isSamp ? "$stdDevSamp" : "$stdDevPop");
+}
+
void AccumulatorStdDev::processInternal(const Value& input, bool merging) {
if (!merging) {
// non numeric types have no impact on standard deviation
@@ -101,8 +108,4 @@ void AccumulatorStdDev::reset() {
_mean = 0;
_m2 = 0;
}
-
-const char* AccumulatorStdDev::getOpName() const {
- return (_isSamp ? "$stdDevSamp" : "$stdDevPop");
-}
}
diff --git a/src/mongo/db/pipeline/accumulator_sum.cpp b/src/mongo/db/pipeline/accumulator_sum.cpp
index 4da24904078..5b105753625 100644
--- a/src/mongo/db/pipeline/accumulator_sum.cpp
+++ b/src/mongo/db/pipeline/accumulator_sum.cpp
@@ -35,6 +35,12 @@ namespace mongo {
using boost::intrusive_ptr;
+REGISTER_ACCUMULATOR(sum, AccumulatorSum::create);
+
+const char* AccumulatorSum::getOpName() const {
+ return "$sum";
+}
+
void AccumulatorSum::processInternal(const Value& input, bool merging) {
// do nothing with non numeric types
if (!input.numeric())
@@ -82,8 +88,4 @@ void AccumulatorSum::reset() {
longTotal = 0;
doubleTotal = 0;
}
-
-const char* AccumulatorSum::getOpName() const {
- return "$sum";
-}
}
diff --git a/src/mongo/db/pipeline/document_source.h b/src/mongo/db/pipeline/document_source.h
index 8095426a3c3..3c5d96e259c 100644
--- a/src/mongo/db/pipeline/document_source.h
+++ b/src/mongo/db/pipeline/document_source.h
@@ -44,6 +44,7 @@
#include "mongo/db/clientcursor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/matcher.h"
+#include "mongo/db/pipeline/accumulator.h"
#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/dependencies.h"
#include "mongo/db/pipeline/expression_context.h"
@@ -57,7 +58,6 @@
namespace mongo {
-class Accumulator;
class Document;
class Expression;
class ExpressionFieldPath;
@@ -68,10 +68,13 @@ class PlanExecutor;
/**
* Registers a DocumentSource to have the name 'key'. When a stage with name '$key' is found,
* 'parser' will be called to construct a DocumentSource.
+ *
+ * As an example, if your document source looks like {"$foo": <args>}, with a parsing function
+ * 'createFromBson', you would add this line:
+ * REGISTER_EXPRESSION(foo, DocumentSourceFoo::createFromBson);
*/
#define REGISTER_DOCUMENT_SOURCE(key, parser) \
MONGO_INITIALIZER(addToDocSourceParserMap_##key)(InitializerContext*) { \
- /* Prevent duplicate document sources with the same name. */ \
DocumentSource::registerParser("$" #key, (parser)); \
return Status::OK(); \
}
@@ -499,7 +502,7 @@ public:
group field
*/
void addAccumulator(const std::string& fieldName,
- boost::intrusive_ptr<Accumulator>(*pAccumulatorFactory)(),
+ Accumulator::Factory accumulatorFactory,
const boost::intrusive_ptr<Expression>& pExpression);
/// Tell this source if it is doing a merge from shards. Defaults to false.
@@ -577,7 +580,7 @@ private:
These three vectors parallel each other.
*/
std::vector<std::string> vFieldName;
- std::vector<boost::intrusive_ptr<Accumulator>(*)()> vpAccumulatorFactory;
+ std::vector<Accumulator::Factory> vpAccumulatorFactory;
std::vector<boost::intrusive_ptr<Expression>> vpExpression;
diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp
index 53f8dc3f8e3..e605d1403a1 100644
--- a/src/mongo/db/pipeline/document_source_group.cpp
+++ b/src/mongo/db/pipeline/document_source_group.cpp
@@ -206,42 +206,13 @@ DocumentSourceGroup::DocumentSourceGroup(const intrusive_ptr<ExpressionContext>&
_maxMemoryUsageBytes(100 * 1024 * 1024) {}
void DocumentSourceGroup::addAccumulator(const std::string& fieldName,
- intrusive_ptr<Accumulator>(*pAccumulatorFactory)(),
+ Accumulator::Factory accumulatorFactory,
const intrusive_ptr<Expression>& pExpression) {
vFieldName.push_back(fieldName);
- vpAccumulatorFactory.push_back(pAccumulatorFactory);
+ vpAccumulatorFactory.push_back(accumulatorFactory);
vpExpression.push_back(pExpression);
}
-
-struct GroupOpDesc {
- const char* name;
- intrusive_ptr<Accumulator>(*factory)();
-};
-
-static int GroupOpDescCmp(const void* pL, const void* pR) {
- return strcmp(((const GroupOpDesc*)pL)->name, ((const GroupOpDesc*)pR)->name);
-}
-
-/*
- Keep these sorted alphabetically so we can bsearch() them using
- GroupOpDescCmp() above.
-*/
-static const GroupOpDesc GroupOpTable[] = {
- {"$addToSet", AccumulatorAddToSet::create},
- {"$avg", AccumulatorAvg::create},
- {"$first", AccumulatorFirst::create},
- {"$last", AccumulatorLast::create},
- {"$max", AccumulatorMinMax::createMax},
- {"$min", AccumulatorMinMax::createMin},
- {"$push", AccumulatorPush::create},
- {"$stdDevPop", AccumulatorStdDev::createPop},
- {"$stdDevSamp", AccumulatorStdDev::createSamp},
- {"$sum", AccumulatorSum::create},
-};
-
-static const size_t NGroupOp = sizeof(GroupOpTable) / sizeof(GroupOpTable[0]);
-
intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson(
BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) {
uassert(15947, "a group's fields must be specified in an object", elem.type() == Object);
@@ -292,29 +263,22 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson(
for (; subIterator.more(); ++subCount) {
BSONElement subElement(subIterator.next());
- /* look for the specified operator */
- GroupOpDesc key;
- key.name = subElement.fieldName();
- const GroupOpDesc* pOp = (const GroupOpDesc*)bsearch(
- &key, GroupOpTable, NGroupOp, sizeof(GroupOpDesc), GroupOpDescCmp);
-
- uassert(15952, str::stream() << "unknown group operator '" << key.name << "'", pOp);
-
+ auto name = subElement.fieldNameStringData();
+ Accumulator::Factory factory = Accumulator::getFactory(name);
intrusive_ptr<Expression> pGroupExpr;
-
BSONType elementType = subElement.type();
if (elementType == Object) {
Expression::ObjectCtx oCtx(Expression::ObjectCtx::DOCUMENT_OK);
pGroupExpr = Expression::parseObject(subElement.Obj(), &oCtx, vps);
} else if (elementType == Array) {
uasserted(15953,
- str::stream() << "aggregating group operators are unary (" << key.name
+ str::stream() << "aggregating group operators are unary (" << name
<< ")");
} else { /* assume its an atomic single operand */
pGroupExpr = Expression::parseOperand(subElement, vps);
}
- pGroup->addAccumulator(pFieldName, pOp->factory, pGroupExpr);
+ pGroup->addAccumulator(pFieldName, factory, pGroupExpr);
}
uassert(15954,
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 2ce8b92cbbb..ca1ad24a5e5 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -31,19 +31,18 @@
#include "mongo/db/pipeline/expression.h"
#include <boost/algorithm/string.hpp>
-#include <boost/preprocessor/cat.hpp> // like the ## operator but works with __LINE__
#include <cstdio>
-#include "mongo/base/init.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/expression_context.h"
#include "mongo/db/pipeline/value.h"
-#include "mongo/stdx/functional.h"
#include "mongo/util/string_map.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
+using Parser = Expression::Parser;
+
using namespace mongoutils;
using boost::intrusive_ptr;
@@ -301,39 +300,22 @@ intrusive_ptr<Expression> Expression::parseObject(BSONObj obj,
}
namespace {
-typedef stdx::function<intrusive_ptr<Expression>(BSONElement, const VariablesParseState&)>
- ExpressionParser;
-StringMap<ExpressionParser> expressionParserMap;
+StringMap<Parser> parserMap;
}
-/** Registers an ExpressionParser so it can be called from parseExpression and friends.
- *
- * As an example, if your expression looks like {"$foo": [1,2,3]} you would add this line:
- * REGISTER_EXPRESSION("$foo", ExpressionFoo::parse);
- */
-#define REGISTER_EXPRESSION(key, parserFunc) \
- MONGO_INITIALIZER(BOOST_PP_CAT(addToExpressionParserMap, __LINE__))(InitializerContext*) { \
- /* prevent duplicate expressions */ \
- StringMap<ExpressionParser>::const_iterator op = expressionParserMap.find(key); \
- massert(17064, \
- str::stream() << "Duplicate expression (" << key << ") detected at " << __FILE__ \
- << ":" << __LINE__, \
- op == expressionParserMap.end()); \
- /* register expression */ \
- expressionParserMap[key] = (parserFunc); \
- return Status::OK(); \
- }
+void Expression::registerExpression(string key, Parser parser) {
+ auto op = parserMap.find(key);
+ massert(17064,
+ str::stream() << "Duplicate expression (" << key << ") registered.",
+ op == parserMap.end());
+ parserMap[key] = parser;
+}
intrusive_ptr<Expression> Expression::parseExpression(BSONElement exprElement,
const VariablesParseState& vps) {
- /* look for the specified operator */
const char* opName = exprElement.fieldName();
- StringMap<ExpressionParser>::const_iterator op = expressionParserMap.find(opName);
- uassert(15999,
- str::stream() << "invalid operator '" << opName << "'",
- op != expressionParserMap.end());
-
- /* make the expression node */
+ auto op = parserMap.find(opName);
+ uassert(15999, str::stream() << "invalid operator '" << opName << "'", op != parserMap.end());
return op->second(exprElement, vps);
}
@@ -393,7 +375,7 @@ Value ExpressionAbs::evaluateInternal(Variables* vars) const {
}
}
-REGISTER_EXPRESSION("$abs", ExpressionAbs::parse);
+REGISTER_EXPRESSION(abs, ExpressionAbs::parse);
const char* ExpressionAbs::getOpName() const {
return "$abs";
}
@@ -453,7 +435,7 @@ Value ExpressionAdd::evaluateInternal(Variables* vars) const {
}
}
-REGISTER_EXPRESSION("$add", ExpressionAdd::parse);
+REGISTER_EXPRESSION(add, ExpressionAdd::parse);
const char* ExpressionAdd::getOpName() const {
return "$add";
}
@@ -475,7 +457,7 @@ Value ExpressionAllElementsTrue::evaluateInternal(Variables* vars) const {
return Value(true);
}
-REGISTER_EXPRESSION("$allElementsTrue", ExpressionAllElementsTrue::parse);
+REGISTER_EXPRESSION(allElementsTrue, ExpressionAllElementsTrue::parse);
const char* ExpressionAllElementsTrue::getOpName() const {
return "$allElementsTrue";
}
@@ -547,7 +529,7 @@ Value ExpressionAnd::evaluateInternal(Variables* vars) const {
return Value(true);
}
-REGISTER_EXPRESSION("$and", ExpressionAnd::parse);
+REGISTER_EXPRESSION(and, ExpressionAnd::parse);
const char* ExpressionAnd::getOpName() const {
return "$and";
}
@@ -569,7 +551,7 @@ Value ExpressionAnyElementTrue::evaluateInternal(Variables* vars) const {
return Value(false);
}
-REGISTER_EXPRESSION("$anyElementTrue", ExpressionAnyElementTrue::parse);
+REGISTER_EXPRESSION(anyElementTrue, ExpressionAnyElementTrue::parse);
const char* ExpressionAnyElementTrue::getOpName() const {
return "$anyElementTrue";
}
@@ -609,7 +591,7 @@ Value ExpressionArrayElemAt::evaluateInternal(Variables* vars) const {
return array[index];
}
-REGISTER_EXPRESSION("$arrayElemAt", ExpressionArrayElemAt::parse);
+REGISTER_EXPRESSION(arrayElemAt, ExpressionArrayElemAt::parse);
const char* ExpressionArrayElemAt::getOpName() const {
return "$arrayElemAt";
}
@@ -660,37 +642,37 @@ Value ExpressionCoerceToBool::serialize(bool explain) const {
/* ----------------------- ExpressionCompare --------------------------- */
-REGISTER_EXPRESSION("$cmp",
+REGISTER_EXPRESSION(cmp,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
ExpressionCompare::CMP));
-REGISTER_EXPRESSION("$eq",
+REGISTER_EXPRESSION(eq,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
ExpressionCompare::EQ));
-REGISTER_EXPRESSION("$gt",
+REGISTER_EXPRESSION(gt,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
ExpressionCompare::GT));
-REGISTER_EXPRESSION("$gte",
+REGISTER_EXPRESSION(gte,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
ExpressionCompare::GTE));
-REGISTER_EXPRESSION("$lt",
+REGISTER_EXPRESSION(lt,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
ExpressionCompare::LT));
-REGISTER_EXPRESSION("$lte",
+REGISTER_EXPRESSION(lte,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
ExpressionCompare::LTE));
-REGISTER_EXPRESSION("$ne",
+REGISTER_EXPRESSION(ne,
stdx::bind(ExpressionCompare::parse,
stdx::placeholders::_1,
stdx::placeholders::_2,
@@ -775,7 +757,7 @@ Value ExpressionConcat::evaluateInternal(Variables* vars) const {
return Value(result.str());
}
-REGISTER_EXPRESSION("$concat", ExpressionConcat::parse);
+REGISTER_EXPRESSION(concat, ExpressionConcat::parse);
const char* ExpressionConcat::getOpName() const {
return "$concat";
}
@@ -803,7 +785,7 @@ Value ExpressionConcatArrays::evaluateInternal(Variables* vars) const {
return Value(std::move(values));
}
-REGISTER_EXPRESSION("$concatArrays", ExpressionConcatArrays::parse);
+REGISTER_EXPRESSION(concatArrays, ExpressionConcatArrays::parse);
const char* ExpressionConcatArrays::getOpName() const {
return "$concatArrays";
}
@@ -846,7 +828,7 @@ intrusive_ptr<Expression> ExpressionCond::parse(BSONElement expr, const Variable
return ret;
}
-REGISTER_EXPRESSION("$cond", ExpressionCond::parse);
+REGISTER_EXPRESSION(cond, ExpressionCond::parse);
const char* ExpressionCond::getOpName() const {
return "$cond";
}
@@ -884,15 +866,15 @@ Value ExpressionConstant::serialize(bool explain) const {
return serializeConstant(pValue);
}
-REGISTER_EXPRESSION("$const", ExpressionConstant::parse);
-REGISTER_EXPRESSION("$literal", ExpressionConstant::parse); // alias
+REGISTER_EXPRESSION(const, ExpressionConstant::parse);
+REGISTER_EXPRESSION(literal, ExpressionConstant::parse); // alias
const char* ExpressionConstant::getOpName() const {
return "$const";
}
/* ---------------------- ExpressionDateToString ----------------------- */
-REGISTER_EXPRESSION("$dateToString", ExpressionDateToString::parse);
+REGISTER_EXPRESSION(dateToString, ExpressionDateToString::parse);
intrusive_ptr<Expression> ExpressionDateToString::parse(BSONElement expr,
const VariablesParseState& vps) {
verify(str::equals(expr.fieldName(), "$dateToString"));
@@ -1083,7 +1065,7 @@ Value ExpressionDayOfMonth::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$dayOfMonth", ExpressionDayOfMonth::parse);
+REGISTER_EXPRESSION(dayOfMonth, ExpressionDayOfMonth::parse);
const char* ExpressionDayOfMonth::getOpName() const {
return "$dayOfMonth";
}
@@ -1095,7 +1077,7 @@ Value ExpressionDayOfWeek::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$dayOfWeek", ExpressionDayOfWeek::parse);
+REGISTER_EXPRESSION(dayOfWeek, ExpressionDayOfWeek::parse);
const char* ExpressionDayOfWeek::getOpName() const {
return "$dayOfWeek";
}
@@ -1107,7 +1089,7 @@ Value ExpressionDayOfYear::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$dayOfYear", ExpressionDayOfYear::parse);
+REGISTER_EXPRESSION(dayOfYear, ExpressionDayOfYear::parse);
const char* ExpressionDayOfYear::getOpName() const {
return "$dayOfYear";
}
@@ -1133,7 +1115,7 @@ Value ExpressionDivide::evaluateInternal(Variables* vars) const {
}
}
-REGISTER_EXPRESSION("$divide", ExpressionDivide::parse);
+REGISTER_EXPRESSION(divide, ExpressionDivide::parse);
const char* ExpressionDivide::getOpName() const {
return "$divide";
}
@@ -1539,7 +1521,7 @@ Value ExpressionFieldPath::serialize(bool explain) const {
/* ------------------------- ExpressionFilter ----------------------------- */
-REGISTER_EXPRESSION("$filter", ExpressionFilter::parse);
+REGISTER_EXPRESSION(filter, ExpressionFilter::parse);
intrusive_ptr<Expression> ExpressionFilter::parse(BSONElement expr,
const VariablesParseState& vpsIn) {
verify(str::equals(expr.fieldName(), "$filter"));
@@ -1638,7 +1620,7 @@ void ExpressionFilter::addDependencies(DepsTracker* deps, vector<string>* path)
/* ------------------------- ExpressionLet ----------------------------- */
-REGISTER_EXPRESSION("$let", ExpressionLet::parse);
+REGISTER_EXPRESSION(let, ExpressionLet::parse);
intrusive_ptr<Expression> ExpressionLet::parse(BSONElement expr, const VariablesParseState& vpsIn) {
verify(str::equals(expr.fieldName(), "$let"));
@@ -1733,7 +1715,7 @@ void ExpressionLet::addDependencies(DepsTracker* deps, vector<string>* path) con
/* ------------------------- ExpressionMap ----------------------------- */
-REGISTER_EXPRESSION("$map", ExpressionMap::parse);
+REGISTER_EXPRESSION(map, ExpressionMap::parse);
intrusive_ptr<Expression> ExpressionMap::parse(BSONElement expr, const VariablesParseState& vpsIn) {
verify(str::equals(expr.fieldName(), "$map"));
@@ -1831,7 +1813,7 @@ void ExpressionMap::addDependencies(DepsTracker* deps, vector<string>* path) con
/* ------------------------- ExpressionMeta ----------------------------- */
-REGISTER_EXPRESSION("$meta", ExpressionMeta::parse);
+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);
@@ -1867,7 +1849,7 @@ int ExpressionMillisecond::extract(const long long date) {
return ms >= 0 ? ms : 1000 + ms;
}
-REGISTER_EXPRESSION("$millisecond", ExpressionMillisecond::parse);
+REGISTER_EXPRESSION(millisecond, ExpressionMillisecond::parse);
const char* ExpressionMillisecond::getOpName() const {
return "$millisecond";
}
@@ -1879,7 +1861,7 @@ Value ExpressionMinute::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$minute", ExpressionMinute::parse);
+REGISTER_EXPRESSION(minute, ExpressionMinute::parse);
const char* ExpressionMinute::getOpName() const {
return "$minute";
}
@@ -1924,7 +1906,7 @@ Value ExpressionMod::evaluateInternal(Variables* vars) const {
}
}
-REGISTER_EXPRESSION("$mod", ExpressionMod::parse);
+REGISTER_EXPRESSION(mod, ExpressionMod::parse);
const char* ExpressionMod::getOpName() const {
return "$mod";
}
@@ -1936,7 +1918,7 @@ Value ExpressionMonth::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$month", ExpressionMonth::parse);
+REGISTER_EXPRESSION(month, ExpressionMonth::parse);
const char* ExpressionMonth::getOpName() const {
return "$month";
}
@@ -1982,7 +1964,7 @@ Value ExpressionMultiply::evaluateInternal(Variables* vars) const {
massert(16418, "$multiply resulted in a non-numeric type", false);
}
-REGISTER_EXPRESSION("$multiply", ExpressionMultiply::parse);
+REGISTER_EXPRESSION(multiply, ExpressionMultiply::parse);
const char* ExpressionMultiply::getOpName() const {
return "$multiply";
}
@@ -1994,7 +1976,7 @@ Value ExpressionHour::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$hour", ExpressionHour::parse);
+REGISTER_EXPRESSION(hour, ExpressionHour::parse);
const char* ExpressionHour::getOpName() const {
return "$hour";
}
@@ -2010,7 +1992,7 @@ Value ExpressionIfNull::evaluateInternal(Variables* vars) const {
return pRight;
}
-REGISTER_EXPRESSION("$ifNull", ExpressionIfNull::parse);
+REGISTER_EXPRESSION(ifNull, ExpressionIfNull::parse);
const char* ExpressionIfNull::getOpName() const {
return "$ifNull";
}
@@ -2117,7 +2099,7 @@ Value ExpressionNot::evaluateInternal(Variables* vars) const {
return Value(!b);
}
-REGISTER_EXPRESSION("$not", ExpressionNot::parse);
+REGISTER_EXPRESSION(not, ExpressionNot::parse);
const char* ExpressionNot::getOpName() const {
return "$not";
}
@@ -2185,7 +2167,7 @@ intrusive_ptr<Expression> ExpressionOr::optimize() {
return pE;
}
-REGISTER_EXPRESSION("$or", ExpressionOr::parse);
+REGISTER_EXPRESSION(or, ExpressionOr::parse);
const char* ExpressionOr::getOpName() const {
return "$or";
}
@@ -2197,7 +2179,7 @@ Value ExpressionSecond::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$second", ExpressionSecond::parse);
+REGISTER_EXPRESSION(second, ExpressionSecond::parse);
const char* ExpressionSecond::getOpName() const {
return "$second";
}
@@ -2242,7 +2224,7 @@ Value ExpressionSetDifference::evaluateInternal(Variables* vars) const {
return Value(std::move(returnVec));
}
-REGISTER_EXPRESSION("$setDifference", ExpressionSetDifference::parse);
+REGISTER_EXPRESSION(setDifference, ExpressionSetDifference::parse);
const char* ExpressionSetDifference::getOpName() const {
return "$setDifference";
}
@@ -2278,7 +2260,7 @@ Value ExpressionSetEquals::evaluateInternal(Variables* vars) const {
return Value(true);
}
-REGISTER_EXPRESSION("$setEquals", ExpressionSetEquals::parse);
+REGISTER_EXPRESSION(setEquals, ExpressionSetEquals::parse);
const char* ExpressionSetEquals::getOpName() const {
return "$setEquals";
}
@@ -2324,7 +2306,7 @@ Value ExpressionSetIntersection::evaluateInternal(Variables* vars) const {
return Value(vector<Value>(currentIntersection.begin(), currentIntersection.end()));
}
-REGISTER_EXPRESSION("$setIntersection", ExpressionSetIntersection::parse);
+REGISTER_EXPRESSION(setIntersection, ExpressionSetIntersection::parse);
const char* ExpressionSetIntersection::getOpName() const {
return "$setIntersection";
}
@@ -2409,7 +2391,7 @@ intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() {
return optimized;
}
-REGISTER_EXPRESSION("$setIsSubset", ExpressionSetIsSubset::parse);
+REGISTER_EXPRESSION(setIsSubset, ExpressionSetIsSubset::parse);
const char* ExpressionSetIsSubset::getOpName() const {
return "$setIsSubset";
}
@@ -2434,7 +2416,7 @@ Value ExpressionSetUnion::evaluateInternal(Variables* vars) const {
return Value(vector<Value>(unionedSet.begin(), unionedSet.end()));
}
-REGISTER_EXPRESSION("$setUnion", ExpressionSetUnion::parse);
+REGISTER_EXPRESSION(setUnion, ExpressionSetUnion::parse);
const char* ExpressionSetUnion::getOpName() const {
return "$setUnion";
}
@@ -2446,7 +2428,7 @@ Value ExpressionIsArray::evaluateInternal(Variables* vars) const {
return Value(argument.getType() == Array);
}
-REGISTER_EXPRESSION("$isArray", ExpressionIsArray::parse);
+REGISTER_EXPRESSION(isArray, ExpressionIsArray::parse);
const char* ExpressionIsArray::getOpName() const {
return "$isArray";
}
@@ -2463,7 +2445,7 @@ Value ExpressionSize::evaluateInternal(Variables* vars) const {
return Value::createIntOrLong(array.getArray().size());
}
-REGISTER_EXPRESSION("$size", ExpressionSize::parse);
+REGISTER_EXPRESSION(size, ExpressionSize::parse);
const char* ExpressionSize::getOpName() const {
return "$size";
}
@@ -2487,7 +2469,7 @@ Value ExpressionSqrt::evaluateInternal(Variables* vars) const {
return Value(sqrt(argDouble));
}
-REGISTER_EXPRESSION("$sqrt", ExpressionSqrt::parse);
+REGISTER_EXPRESSION(sqrt, ExpressionSqrt::parse);
const char* ExpressionSqrt::getOpName() const {
return "$sqrt";
}
@@ -2511,7 +2493,7 @@ Value ExpressionStrcasecmp::evaluateInternal(Variables* vars) const {
return Value(-1);
}
-REGISTER_EXPRESSION("$strcasecmp", ExpressionStrcasecmp::parse);
+REGISTER_EXPRESSION(strcasecmp, ExpressionStrcasecmp::parse);
const char* ExpressionStrcasecmp::getOpName() const {
return "$strcasecmp";
}
@@ -2562,7 +2544,7 @@ Value ExpressionSubstr::evaluateInternal(Variables* vars) const {
return Value(str.substr(lower, length));
}
-REGISTER_EXPRESSION("$substr", ExpressionSubstr::parse);
+REGISTER_EXPRESSION(substr, ExpressionSubstr::parse);
const char* ExpressionSubstr::getOpName() const {
return "$substr";
}
@@ -2608,7 +2590,7 @@ Value ExpressionSubtract::evaluateInternal(Variables* vars) const {
}
}
-REGISTER_EXPRESSION("$subtract", ExpressionSubtract::parse);
+REGISTER_EXPRESSION(subtract, ExpressionSubtract::parse);
const char* ExpressionSubtract::getOpName() const {
return "$subtract";
}
@@ -2622,7 +2604,7 @@ Value ExpressionToLower::evaluateInternal(Variables* vars) const {
return Value(str);
}
-REGISTER_EXPRESSION("$toLower", ExpressionToLower::parse);
+REGISTER_EXPRESSION(toLower, ExpressionToLower::parse);
const char* ExpressionToLower::getOpName() const {
return "$toLower";
}
@@ -2636,7 +2618,7 @@ Value ExpressionToUpper::evaluateInternal(Variables* vars) const {
return Value(str);
}
-REGISTER_EXPRESSION("$toUpper", ExpressionToUpper::parse);
+REGISTER_EXPRESSION(toUpper, ExpressionToUpper::parse);
const char* ExpressionToUpper::getOpName() const {
return "$toUpper";
}
@@ -2668,7 +2650,7 @@ int ExpressionWeek::extract(const tm& tm) {
return nextSundayWeek;
}
-REGISTER_EXPRESSION("$week", ExpressionWeek::parse);
+REGISTER_EXPRESSION(week, ExpressionWeek::parse);
const char* ExpressionWeek::getOpName() const {
return "$week";
}
@@ -2680,7 +2662,7 @@ Value ExpressionYear::evaluateInternal(Variables* vars) const {
return Value(extract(pDate.coerceToTm()));
}
-REGISTER_EXPRESSION("$year", ExpressionYear::parse);
+REGISTER_EXPRESSION(year, ExpressionYear::parse);
const char* ExpressionYear::getOpName() const {
return "$year";
}
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index 330487b4342..4bcbe74abf4 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -35,10 +35,12 @@
#include <string>
#include <vector>
+#include "mongo/base/init.h"
#include "mongo/db/pipeline/dependencies.h"
#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/field_path.h"
#include "mongo/db/pipeline/value.h"
+#include "mongo/stdx/functional.h"
#include "mongo/util/intrusive_counter.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/string_map.h"
@@ -50,6 +52,18 @@ class BSONElement;
class BSONObjBuilder;
class DocumentSource;
+/**
+ * Registers an Parser so it can be called from parseExpression and friends.
+ *
+ * As an example, if your expression looks like {"$foo": [1,2,3]} you would add this line:
+ * REGISTER_EXPRESSION(foo, ExpressionFoo::parse);
+ */
+#define REGISTER_EXPRESSION(key, parser) \
+ MONGO_INITIALIZER(addToExpressionParserMap_##key)(InitializerContext*) { \
+ Expression::registerExpression("$" #key, (parser)); \
+ return Status::OK(); \
+ }
+
// TODO: Look into merging with ExpressionContext and possibly ObjectCtx.
/// The state used as input and working space for Expressions.
class Variables {
@@ -157,6 +171,9 @@ private:
class Expression : public IntrusiveCounterUnsigned {
public:
+ using Parser =
+ stdx::function<boost::intrusive_ptr<Expression>(BSONElement, const VariablesParseState&)>;
+
virtual ~Expression(){};
/*
@@ -295,6 +312,14 @@ public:
*/
virtual Value evaluateInternal(Variables* vars) const = 0;
+ /**
+ * Registers an Parser so it can be called from parseExpression and friends.
+ *
+ * DO NOT call this method directly. Instead, use the REGISTER_EXPRESSION macro defined in this
+ * file.
+ */
+ static void registerExpression(std::string key, Parser parser);
+
protected:
typedef std::vector<boost::intrusive_ptr<Expression>> ExpressionVector;
};