diff options
author | Charlie Swanson <charlie.swanson@mongodb.com> | 2015-07-01 16:47:20 -0400 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2015-07-13 17:41:39 -0400 |
commit | 1527a67262baf2d80776f86d4af0e42d53aa3eec (patch) | |
tree | 802170077582f8d30a12e548b2e6ce6578275443 | |
parent | 908313432e597623361df39339cee452176fd7b2 (diff) | |
download | mongo-1527a67262baf2d80776f86d4af0e42d53aa3eec.tar.gz |
SERVER-19105 Add macro to register Accumulators, move REGISTER_EXPRESSION to header
-rw-r--r-- | src/mongo/db/pipeline/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator.cpp | 62 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator.h | 34 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_add_to_set.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_avg.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_first.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_last.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_min_max.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_push.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_std_dev.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_sum.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source.h | 11 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_group.cpp | 48 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 146 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 25 |
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; }; |