summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJames Wahlin <james.wahlin@10gen.com>2017-04-05 09:59:54 -0400
committerJames Wahlin <james.wahlin@10gen.com>2017-04-29 09:21:00 -0400
commit5273c2bad7df58c44afdd677609b8656f399a906 (patch)
tree0257684f5555293d72e07179c6b058081c247019 /src/mongo
parent2e8e60bfef83ac9c7bf494b0f80977686cb1b772 (diff)
downloadmongo-5273c2bad7df58c44afdd677609b8656f399a906.tar.gz
SERVER-28651 Move agg var ownership to ExpressionContext
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/pipeline/document_source_bucket.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_bucket_auto.cpp23
-rw-r--r--src/mongo/db/pipeline/document_source_bucket_auto.h3
-rw-r--r--src/mongo/db/pipeline/document_source_bucket_auto_test.cpp50
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup.cpp13
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup.h3
-rw-r--r--src/mongo/db/pipeline/document_source_group.cpp45
-rw-r--r--src/mongo/db/pipeline/document_source_group.h4
-rw-r--r--src/mongo/db/pipeline/document_source_group_test.cpp17
-rw-r--r--src/mongo/db/pipeline/document_source_redact.cpp27
-rw-r--r--src/mongo/db/pipeline/document_source_redact.h3
-rw-r--r--src/mongo/db/pipeline/document_source_replace_root.cpp15
-rw-r--r--src/mongo/db/pipeline/document_source_sort.cpp12
-rw-r--r--src/mongo/db/pipeline/expression.cpp386
-rw-r--r--src/mongo/db/pipeline/expression.h167
-rw-r--r--src/mongo/db/pipeline/expression_context.cpp1
-rw-r--r--src/mongo/db/pipeline/expression_context.h9
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp276
-rw-r--r--src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp6
-rw-r--r--src/mongo/db/pipeline/parsed_add_fields.cpp32
-rw-r--r--src/mongo/db/pipeline/parsed_add_fields.h32
-rw-r--r--src/mongo/db/pipeline/parsed_add_fields_test.cpp102
-rw-r--r--src/mongo/db/pipeline/parsed_aggregation_projection.cpp6
-rw-r--r--src/mongo/db/pipeline/parsed_aggregation_projection.h8
-rw-r--r--src/mongo/db/pipeline/parsed_exclusion_projection.cpp7
-rw-r--r--src/mongo/db/pipeline/parsed_exclusion_projection.h12
-rw-r--r--src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp77
-rw-r--r--src/mongo/db/pipeline/parsed_inclusion_projection.cpp45
-rw-r--r--src/mongo/db/pipeline/parsed_inclusion_projection.h40
-rw-r--r--src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp168
-rw-r--r--src/mongo/db/pipeline/variables.cpp19
-rw-r--r--src/mongo/db/pipeline/variables.h73
32 files changed, 780 insertions, 906 deletions
diff --git a/src/mongo/db/pipeline/document_source_bucket.cpp b/src/mongo/db/pipeline/document_source_bucket.cpp
index 7c671ea2998..853ef0aaa42 100644
--- a/src/mongo/db/pipeline/document_source_bucket.cpp
+++ b/src/mongo/db/pipeline/document_source_bucket.cpp
@@ -46,7 +46,7 @@ namespace {
intrusive_ptr<ExpressionConstant> getExpressionConstant(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
BSONElement expressionElem,
- VariablesParseState vps) {
+ const VariablesParseState& vps) {
auto expr = Expression::parseOperand(expCtx, expressionElem, vps)->optimize();
return dynamic_cast<ExpressionConstant*>(expr.get());
}
@@ -64,8 +64,7 @@ vector<intrusive_ptr<DocumentSource>> DocumentSourceBucket::createFromBson(
BSONObjBuilder groupObjBuilder;
BSONObjBuilder switchObjBuilder;
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = pExpCtx->variablesParseState;
vector<Value> boundaryValues;
BSONElement groupByField;
diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.cpp b/src/mongo/db/pipeline/document_source_bucket_auto.cpp
index b00feee2b68..a31e91ff845 100644
--- a/src/mongo/db/pipeline/document_source_bucket_auto.cpp
+++ b/src/mongo/db/pipeline/document_source_bucket_auto.cpp
@@ -118,8 +118,8 @@ Value DocumentSourceBucketAuto::extractKey(const Document& doc) {
return Value(BSONNULL);
}
- _variables->setRoot(doc);
- Value key = _groupByExpression->evaluate(_variables.get());
+ pExpCtx->variables.setRoot(doc);
+ Value key = _groupByExpression->evaluate();
if (_granularityRounder) {
uassert(40258,
@@ -151,9 +151,9 @@ void DocumentSourceBucketAuto::addDocumentToBucket(const pair<Value, Document>&
bucket._max = entry.first;
const size_t numAccumulators = _accumulatorFactories.size();
- _variables->setRoot(entry.second);
+ pExpCtx->variables.setRoot(entry.second);
for (size_t k = 0; k < numAccumulators; k++) {
- bucket._accums[k]->process(_expressions[k]->evaluate(_variables.get()), false);
+ bucket._accums[k]->process(_expressions[k]->evaluate(), false);
}
}
@@ -358,7 +358,6 @@ Value DocumentSourceBucketAuto::serialize(
intrusive_ptr<DocumentSourceBucketAuto> DocumentSourceBucketAuto::create(
const intrusive_ptr<ExpressionContext>& pExpCtx,
const boost::intrusive_ptr<Expression>& groupByExpression,
- Variables::Id numVariables,
int numBuckets,
std::vector<AccumulationStatement> accumulationStatements,
const boost::intrusive_ptr<GranularityRounder>& granularityRounder,
@@ -375,7 +374,6 @@ intrusive_ptr<DocumentSourceBucketAuto> DocumentSourceBucketAuto::create(
}
return new DocumentSourceBucketAuto(pExpCtx,
groupByExpression,
- numVariables,
numBuckets,
accumulationStatements,
granularityRounder,
@@ -385,7 +383,6 @@ intrusive_ptr<DocumentSourceBucketAuto> DocumentSourceBucketAuto::create(
DocumentSourceBucketAuto::DocumentSourceBucketAuto(
const intrusive_ptr<ExpressionContext>& pExpCtx,
const boost::intrusive_ptr<Expression>& groupByExpression,
- Variables::Id numVariables,
int numBuckets,
std::vector<AccumulationStatement> accumulationStatements,
const boost::intrusive_ptr<GranularityRounder>& granularityRounder,
@@ -393,7 +390,6 @@ DocumentSourceBucketAuto::DocumentSourceBucketAuto(
: DocumentSource(pExpCtx),
_nBuckets(numBuckets),
_maxMemoryUsageBytes(maxMemoryUsageBytes),
- _variables(stdx::make_unique<Variables>(numVariables)),
_groupByExpression(groupByExpression),
_granularityRounder(granularityRounder) {
@@ -434,8 +430,7 @@ intrusive_ptr<DocumentSource> DocumentSourceBucketAuto::createFromBson(
<< typeName(elem.type()),
elem.type() == BSONType::Object);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = pExpCtx->variablesParseState;
vector<AccumulationStatement> accumulationStatements;
boost::intrusive_ptr<Expression> groupByExpression;
boost::optional<int> numBuckets;
@@ -489,12 +484,8 @@ intrusive_ptr<DocumentSource> DocumentSourceBucketAuto::createFromBson(
"$bucketAuto requires 'groupBy' and 'buckets' to be specified",
groupByExpression && numBuckets);
- return DocumentSourceBucketAuto::create(pExpCtx,
- groupByExpression,
- idGenerator.getIdCount(),
- numBuckets.get(),
- accumulationStatements,
- granularityRounder);
+ return DocumentSourceBucketAuto::create(
+ pExpCtx, groupByExpression, numBuckets.get(), accumulationStatements, granularityRounder);
}
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.h b/src/mongo/db/pipeline/document_source_bucket_auto.h
index 59fec253134..a60bf5cb752 100644
--- a/src/mongo/db/pipeline/document_source_bucket_auto.h
+++ b/src/mongo/db/pipeline/document_source_bucket_auto.h
@@ -69,7 +69,6 @@ public:
static boost::intrusive_ptr<DocumentSourceBucketAuto> create(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
const boost::intrusive_ptr<Expression>& groupByExpression,
- Variables::Id numVariables,
int numBuckets,
std::vector<AccumulationStatement> accumulationStatements = {},
const boost::intrusive_ptr<GranularityRounder>& granularityRounder = nullptr,
@@ -87,7 +86,6 @@ protected:
private:
DocumentSourceBucketAuto(const boost::intrusive_ptr<ExpressionContext>& pExpCtx,
const boost::intrusive_ptr<Expression>& groupByExpression,
- Variables::Id numVariables,
int numBuckets,
std::vector<AccumulationStatement> accumulationStatements,
const boost::intrusive_ptr<GranularityRounder>& granularityRounder,
@@ -154,7 +152,6 @@ private:
bool _populated = false;
std::vector<Bucket> _buckets;
std::vector<Bucket>::iterator _bucketsIterator;
- std::unique_ptr<Variables> _variables;
boost::intrusive_ptr<Expression> _groupByExpression;
boost::intrusive_ptr<GranularityRounder> _granularityRounder;
long long _nDocuments = 0;
diff --git a/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp b/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp
index debd2eede82..7afa30e1c8b 100644
--- a/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp
+++ b/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp
@@ -353,18 +353,12 @@ TEST_F(BucketAutoTests, ShouldBeAbleToCorrectlySpillToDisk) {
expCtx->extSortAllowed = true;
const size_t maxMemoryUsageBytes = 1000;
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps);
const int numBuckets = 2;
- auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx,
- groupByExpression,
- idGen.getIdCount(),
- numBuckets,
- {},
- nullptr,
- maxMemoryUsageBytes);
+ auto bucketAutoStage = DocumentSourceBucketAuto::create(
+ expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes, 'x');
auto mock = DocumentSourceMock::create({Document{{"a", 0}, {"largeStr", largeStr}},
@@ -395,18 +389,12 @@ TEST_F(BucketAutoTests, ShouldBeAbleToPauseLoadingWhileSpilled) {
expCtx->extSortAllowed = true;
const size_t maxMemoryUsageBytes = 1000;
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps);
const int numBuckets = 2;
- auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx,
- groupByExpression,
- idGen.getIdCount(),
- numBuckets,
- {},
- nullptr,
- maxMemoryUsageBytes);
+ auto bucketAutoStage = DocumentSourceBucketAuto::create(
+ expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes);
auto sort = DocumentSourceSort::create(expCtx, BSON("_id" << -1), -1, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes, 'x');
@@ -557,7 +545,7 @@ TEST_F(BucketAutoTests, FailsWithInvalidNumberOfBuckets) {
const int numBuckets = 0;
ASSERT_THROWS_CODE(
DocumentSourceBucketAuto::create(
- getExpCtx(), ExpressionConstant::create(getExpCtx(), Value(0)), 0, numBuckets),
+ getExpCtx(), ExpressionConstant::create(getExpCtx(), Value(0)), numBuckets),
UserException,
40243);
}
@@ -641,18 +629,12 @@ TEST_F(BucketAutoTests, FailsWithInvalidOutputFieldName) {
void assertCannotSpillToDisk(const boost::intrusive_ptr<ExpressionContext>& expCtx) {
const size_t maxMemoryUsageBytes = 1000;
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps);
const int numBuckets = 2;
- auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx,
- groupByExpression,
- idGen.getIdCount(),
- numBuckets,
- {},
- nullptr,
- maxMemoryUsageBytes);
+ auto bucketAutoStage = DocumentSourceBucketAuto::create(
+ expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes, 'x');
auto mock = DocumentSourceMock::create(
@@ -683,18 +665,12 @@ TEST_F(BucketAutoTests, ShouldCorrectlyTrackMemoryUsageBetweenPauses) {
expCtx->extSortAllowed = false;
const size_t maxMemoryUsageBytes = 1000;
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps);
const int numBuckets = 2;
- auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx,
- groupByExpression,
- idGen.getIdCount(),
- numBuckets,
- {},
- nullptr,
- maxMemoryUsageBytes);
+ auto bucketAutoStage = DocumentSourceBucketAuto::create(
+ expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes / 2, 'x');
auto mock = DocumentSourceMock::create({Document{{"a", 0}, {"largeStr", largeStr}},
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
index 7cc78ba8e95..d0c338c0855 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
@@ -325,9 +325,10 @@ void DocumentSourceGraphLookUp::performSearch() {
// Make sure _input is set before calling performSearch().
invariant(_input);
- _variables->setRoot(*_input);
- Value startingValue = _startWith->evaluateInternal(_variables.get());
- _variables->clearRoot();
+ auto& variables = pExpCtx->variables;
+ variables.setRoot(*_input);
+ Value startingValue = _startWith->evaluateInternal();
+ variables.clearRoot();
// If _startWith evaluates to an array, treat each value as a separate starting point.
if (startingValue.isArray()) {
@@ -494,7 +495,6 @@ intrusive_ptr<DocumentSourceGraphLookUp> DocumentSourceGraphLookUp::create(
depthField,
maxDepth,
unwindSrc));
- source->_variables.reset(new Variables());
return source;
}
@@ -509,8 +509,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson(
boost::optional<long long> maxDepth;
boost::optional<BSONObj> additionalFilter;
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
for (auto&& argument : elem.Obj()) {
const auto argName = argument.fieldNameStringData();
@@ -607,8 +606,6 @@ intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson(
maxDepth,
boost::none));
- newSource->_variables.reset(new Variables(idGenerator.getIdCount()));
-
return std::move(newSource);
}
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.h b/src/mongo/db/pipeline/document_source_graph_lookup.h
index e196b4578ca..86b27c191cc 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup.h
+++ b/src/mongo/db/pipeline/document_source_graph_lookup.h
@@ -204,9 +204,6 @@ private:
// need it for multiple "getNext()" calls.
boost::optional<Document> _input;
- // The variables that are in scope to be used by the '_startWith' expression.
- std::unique_ptr<Variables> _variables;
-
// Keep track of a $unwind that was absorbed into this stage.
boost::optional<boost::intrusive_ptr<DocumentSourceUnwind>> _unwind;
diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp
index 2d625dc2f1f..c252d61e102 100644
--- a/src/mongo/db/pipeline/document_source_group.cpp
+++ b/src/mongo/db/pipeline/document_source_group.cpp
@@ -128,25 +128,26 @@ DocumentSource::GetNextResult DocumentSourceGroup::getNextStandard() {
DocumentSource::GetNextResult DocumentSourceGroup::getNextStreaming() {
// Streaming optimization is active.
+ auto& variables = pExpCtx->variables;
if (!_firstDocOfNextGroup) {
auto nextInput = pSource->getNext();
if (!nextInput.isAdvanced()) {
return nextInput;
}
_firstDocOfNextGroup = nextInput.releaseDocument();
+ variables.setRoot(*_firstDocOfNextGroup);
}
Value id;
do {
// Add to the current accumulator(s).
for (size_t i = 0; i < _currentAccumulators.size(); i++) {
- _currentAccumulators[i]->process(vpExpression[i]->evaluate(_variables.get()),
- _doingMerge);
+ _currentAccumulators[i]->process(vpExpression[i]->evaluate(), _doingMerge);
}
// Release our references to the previous input document before asking for the next. This
// makes operations like $unwind more efficient.
- _variables->clearRoot();
+ variables.clearRoot();
// Retrieve the next document.
auto nextInput = pSource->getNext();
@@ -156,11 +157,11 @@ DocumentSource::GetNextResult DocumentSourceGroup::getNextStreaming() {
_firstDocOfNextGroup = nextInput.releaseDocument();
- _variables->setRoot(*_firstDocOfNextGroup);
+ variables.setRoot(*_firstDocOfNextGroup);
// Compute the id. If it does not match _currentId, we will exit the loop, leaving
// _firstDocOfNextGroup set for the next time getNext() is called.
- id = computeId(_variables.get());
+ id = computeId();
} while (pExpCtx->getValueComparator().evaluate(_currentId == id));
Document out = makeDocument(_currentId, _currentAccumulators, pExpCtx->inShard);
@@ -251,7 +252,6 @@ intrusive_ptr<DocumentSourceGroup> DocumentSourceGroup::create(
const intrusive_ptr<ExpressionContext>& pExpCtx,
const boost::intrusive_ptr<Expression>& groupByExpression,
std::vector<AccumulationStatement> accumulationStatements,
- Variables::Id numVariables,
size_t maxMemoryUsageBytes) {
intrusive_ptr<DocumentSourceGroup> groupStage(
new DocumentSourceGroup(pExpCtx, maxMemoryUsageBytes));
@@ -259,7 +259,7 @@ intrusive_ptr<DocumentSourceGroup> DocumentSourceGroup::create(
for (auto&& statement : accumulationStatements) {
groupStage->addAccumulator(statement);
}
- groupStage->_variables = stdx::make_unique<Variables>(numVariables);
+
return groupStage;
}
@@ -339,8 +339,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson(
BSONObj groupObj(elem.Obj());
BSONObjIterator groupIterator(groupObj);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = pExpCtx->variablesParseState;
while (groupIterator.more()) {
BSONElement groupField(groupIterator.next());
const char* pFieldName = groupField.fieldName();
@@ -362,9 +361,6 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson(
}
uassert(15955, "a group specification must include an _id", !pGroup->_idExpressions.empty());
-
- pGroup->_variables.reset(new Variables(idGenerator.getIdCount()));
-
return pGroup;
}
@@ -483,10 +479,10 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() {
return firstInput;
}
_firstDocOfNextGroup = firstInput.releaseDocument();
- _variables->setRoot(*_firstDocOfNextGroup);
+ pExpCtx->variables.setRoot(*_firstDocOfNextGroup);
// Compute the _id value.
- _currentId = computeId(_variables.get());
+ _currentId = computeId();
_initialized = true;
return DocumentSource::GetNextResult::makeEOF();
}
@@ -505,9 +501,10 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() {
_memoryUsageBytes = 0;
}
- _variables->setRoot(input.releaseDocument());
+ auto& variables = pExpCtx->variables;
+ variables.setRoot(input.releaseDocument());
- Value id = computeId(_variables.get());
+ Value id = computeId();
// Look for the _id value in the map. If it's not there, add a new entry with a blank
// accumulator. This is done in a somewhat odd way in order to avoid hashing 'id' and
@@ -533,13 +530,14 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() {
/* tickle all the accumulators for the group we found */
dassert(numAccumulators == group.size());
+
for (size_t i = 0; i < numAccumulators; i++) {
- group[i]->process(vpExpression[i]->evaluate(_variables.get()), _doingMerge);
+ group[i]->process(vpExpression[i]->evaluate(), _doingMerge);
_memoryUsageBytes += group[i]->memUsageForSorter();
}
// We are done with the ROOT document so release it.
- _variables->clearRoot();
+ variables.clearRoot();
if (kDebugBuild && !storageGlobalParams.readOnly) {
// In debug mode, spill every time we have a duplicate id to stress merge logic.
@@ -784,10 +782,10 @@ BSONObjSet DocumentSourceGroup::getOutputSorts() {
}
-Value DocumentSourceGroup::computeId(Variables* vars) {
+Value DocumentSourceGroup::computeId() {
// If only one expression, return result directly
if (_idExpressions.size() == 1) {
- Value retValue = _idExpressions[0]->evaluate(vars);
+ Value retValue = _idExpressions[0]->evaluate();
return retValue.missing() ? Value(BSONNULL) : std::move(retValue);
}
@@ -795,7 +793,7 @@ Value DocumentSourceGroup::computeId(Variables* vars) {
vector<Value> vals;
vals.reserve(_idExpressions.size());
for (size_t i = 0; i < _idExpressions.size(); i++) {
- vals.push_back(_idExpressions[i]->evaluate(vars));
+ vals.push_back(_idExpressions[i]->evaluate());
}
return Value(std::move(vals));
}
@@ -850,8 +848,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::getMergeSource() {
intrusive_ptr<DocumentSourceGroup> pMerger(new DocumentSourceGroup(pExpCtx));
pMerger->setDoingMerge(true);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = pExpCtx->variablesParseState;
/* the merger will use the same grouping key */
pMerger->setIdExpression(ExpressionFieldPath::parse(pExpCtx, "$$ROOT._id", vps));
@@ -871,8 +868,6 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::getMergeSource() {
ExpressionFieldPath::parse(pExpCtx, "$$ROOT." + vFieldName[i], vps)});
}
- pMerger->_variables.reset(new Variables(idGenerator.getIdCount()));
-
return pMerger;
}
}
diff --git a/src/mongo/db/pipeline/document_source_group.h b/src/mongo/db/pipeline/document_source_group.h
index 643e291415b..474952a3203 100644
--- a/src/mongo/db/pipeline/document_source_group.h
+++ b/src/mongo/db/pipeline/document_source_group.h
@@ -60,7 +60,6 @@ public:
const boost::intrusive_ptr<ExpressionContext>& expCtx,
const boost::intrusive_ptr<Expression>& groupByExpression,
std::vector<AccumulationStatement> accumulationStatements,
- Variables::Id numVariables,
size_t maxMemoryUsageBytes = kDefaultMaxMemoryUsageBytes);
/**
@@ -141,7 +140,7 @@ private:
/**
* Computes the internal representation of the group key.
*/
- Value computeId(Variables* vars);
+ Value computeId();
/**
* Converts the internal representation of the group key to the _id shape specified by the
@@ -162,7 +161,6 @@ private:
bool _doingMerge;
size_t _memoryUsageBytes = 0;
size_t _maxMemoryUsageBytes;
- std::unique_ptr<Variables> _variables;
std::vector<std::string> _idFieldNames; // used when id is a document
std::vector<boost::intrusive_ptr<Expression>> _idExpressions;
diff --git a/src/mongo/db/pipeline/document_source_group_test.cpp b/src/mongo/db/pipeline/document_source_group_test.cpp
index 25b10ddcb5b..53234769feb 100644
--- a/src/mongo/db/pipeline/document_source_group_test.cpp
+++ b/src/mongo/db/pipeline/document_source_group_test.cpp
@@ -76,7 +76,7 @@ TEST_F(DocumentSourceGroupTest, ShouldBeAbleToPauseLoading) {
AccumulationStatement::getFactory("$sum"),
ExpressionConstant::create(expCtx, Value(1))};
auto group = DocumentSourceGroup::create(
- expCtx, ExpressionConstant::create(expCtx, Value(BSONNULL)), {countStatement}, 0);
+ expCtx, ExpressionConstant::create(expCtx, Value(BSONNULL)), {countStatement});
auto mock = DocumentSourceMock::create({DocumentSource::GetNextResult::makePauseExecution(),
Document(),
DocumentSource::GetNextResult::makePauseExecution(),
@@ -106,14 +106,13 @@ TEST_F(DocumentSourceGroupTest, ShouldBeAbleToPauseLoadingWhileSpilled) {
expCtx->extSortAllowed = true;
const size_t maxMemoryUsageBytes = 1000;
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
AccumulationStatement pushStatement{"spaceHog",
AccumulationStatement::getFactory("$push"),
ExpressionFieldPath::parse(expCtx, "$largeStr", vps)};
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$_id", vps);
auto group = DocumentSourceGroup::create(
- expCtx, groupByExpression, {pushStatement}, idGen.getIdCount(), maxMemoryUsageBytes);
+ expCtx, groupByExpression, {pushStatement}, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes, 'x');
auto mock = DocumentSourceMock::create({Document{{"_id", 0}, {"largeStr", largeStr}},
@@ -146,14 +145,13 @@ TEST_F(DocumentSourceGroupTest, ShouldErrorIfNotAllowedToSpillToDiskAndResultSet
expCtx->inRouter = true; // Disallow external sort.
// This is the only way to do this in a debug build.
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
AccumulationStatement pushStatement{"spaceHog",
AccumulationStatement::getFactory("$push"),
ExpressionFieldPath::parse(expCtx, "$largeStr", vps)};
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$_id", vps);
auto group = DocumentSourceGroup::create(
- expCtx, groupByExpression, {pushStatement}, idGen.getIdCount(), maxMemoryUsageBytes);
+ expCtx, groupByExpression, {pushStatement}, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes, 'x');
auto mock = DocumentSourceMock::create({Document{{"_id", 0}, {"largeStr", largeStr}},
@@ -169,14 +167,13 @@ TEST_F(DocumentSourceGroupTest, ShouldCorrectlyTrackMemoryUsageBetweenPauses) {
expCtx->inRouter = true; // Disallow external sort.
// This is the only way to do this in a debug build.
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
AccumulationStatement pushStatement{"spaceHog",
AccumulationStatement::getFactory("$push"),
ExpressionFieldPath::parse(expCtx, "$largeStr", vps)};
auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$_id", vps);
auto group = DocumentSourceGroup::create(
- expCtx, groupByExpression, {pushStatement}, idGen.getIdCount(), maxMemoryUsageBytes);
+ expCtx, groupByExpression, {pushStatement}, maxMemoryUsageBytes);
string largeStr(maxMemoryUsageBytes / 2, 'x');
auto mock = DocumentSourceMock::create({Document{{"_id", 0}, {"largeStr", largeStr}},
diff --git a/src/mongo/db/pipeline/document_source_redact.cpp b/src/mongo/db/pipeline/document_source_redact.cpp
index ae79729f2e0..738a55cc056 100644
--- a/src/mongo/db/pipeline/document_source_redact.cpp
+++ b/src/mongo/db/pipeline/document_source_redact.cpp
@@ -63,8 +63,9 @@ static const Value keepVal = Value("keep"_sd);
DocumentSource::GetNextResult DocumentSourceRedact::getNext() {
auto nextInput = pSource->getNext();
for (; nextInput.isAdvanced(); nextInput = pSource->getNext()) {
- _variables->setRoot(nextInput.getDocument());
- _variables->setValue(_currentId, Value(nextInput.releaseDocument()));
+ auto& variables = pExpCtx->variables;
+ variables.setRoot(nextInput.getDocument());
+ variables.setValue(_currentId, Value(nextInput.releaseDocument()));
if (boost::optional<Document> result = redactObject()) {
return std::move(*result);
}
@@ -99,7 +100,7 @@ Pipeline::SourceContainer::iterator DocumentSourceRedact::doOptimizeAt(
Value DocumentSourceRedact::redactValue(const Value& in) {
const BSONType valueType = in.getType();
if (valueType == Object) {
- _variables->setValue(_currentId, in);
+ pExpCtx->variables.setValue(_currentId, in);
const boost::optional<Document> result = redactObject();
if (result) {
return Value(*result);
@@ -127,22 +128,23 @@ Value DocumentSourceRedact::redactValue(const Value& in) {
}
boost::optional<Document> DocumentSourceRedact::redactObject() {
- const Value expressionResult = _expression->evaluate(_variables.get());
+ auto& variables = pExpCtx->variables;
+ const Value expressionResult = _expression->evaluate();
ValueComparator simpleValueCmp;
if (simpleValueCmp.evaluate(expressionResult == keepVal)) {
- return _variables->getDocument(_currentId);
+ return variables.getDocument(_currentId);
} else if (simpleValueCmp.evaluate(expressionResult == pruneVal)) {
return boost::optional<Document>();
} else if (simpleValueCmp.evaluate(expressionResult == descendVal)) {
- const Document in = _variables->getDocument(_currentId);
+ const Document in = variables.getDocument(_currentId);
MutableDocument out;
out.copyMetaDataFrom(in);
FieldIterator fields(in);
while (fields.more()) {
const Document::FieldPair field(fields.next());
- // This changes CURRENT so don't read from _variables after this
+ // This changes CURRENT so don't read from variables after this
const Value val = redactValue(field.second);
if (!val.missing()) {
out.addField(field.first, val);
@@ -169,8 +171,7 @@ Value DocumentSourceRedact::serialize(boost::optional<ExplainOptions::Verbosity>
intrusive_ptr<DocumentSource> DocumentSourceRedact::createFromBson(
BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) {
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
Variables::Id currentId = vps.defineVariable("CURRENT"); // will differ from ROOT
Variables::Id decendId = vps.defineVariable("DESCEND");
Variables::Id pruneId = vps.defineVariable("PRUNE");
@@ -181,10 +182,10 @@ intrusive_ptr<DocumentSource> DocumentSourceRedact::createFromBson(
// TODO figure out how much of this belongs in constructor and how much here.
// Set up variables. Never need to reset DESCEND, PRUNE, or KEEP.
source->_currentId = currentId;
- source->_variables.reset(new Variables(idGenerator.getIdCount()));
- source->_variables->setValue(decendId, descendVal);
- source->_variables->setValue(pruneId, pruneVal);
- source->_variables->setValue(keepId, keepVal);
+ auto& variables = expCtx->variables;
+ variables.setValue(decendId, descendVal);
+ variables.setValue(pruneId, pruneVal);
+ variables.setValue(keepId, keepVal);
return source;
diff --git a/src/mongo/db/pipeline/document_source_redact.h b/src/mongo/db/pipeline/document_source_redact.h
index 8177de12eeb..b894ec9e3f2 100644
--- a/src/mongo/db/pipeline/document_source_redact.h
+++ b/src/mongo/db/pipeline/document_source_redact.h
@@ -56,12 +56,11 @@ private:
DocumentSourceRedact(const boost::intrusive_ptr<ExpressionContext>& expCtx,
const boost::intrusive_ptr<Expression>& previsit);
- // These both work over _variables
+ // These both work over pExpCtx->variables.
boost::optional<Document> redactObject(); // redacts CURRENT
Value redactValue(const Value& in);
Variables::Id _currentId;
- std::unique_ptr<Variables> _variables;
boost::intrusive_ptr<Expression> _expression;
};
diff --git a/src/mongo/db/pipeline/document_source_replace_root.cpp b/src/mongo/db/pipeline/document_source_replace_root.cpp
index 8f6782dc47e..d089e67d097 100644
--- a/src/mongo/db/pipeline/document_source_replace_root.cpp
+++ b/src/mongo/db/pipeline/document_source_replace_root.cpp
@@ -49,12 +49,13 @@ class ReplaceRootTransformation final
: public DocumentSourceSingleDocumentTransformation::TransformerInterface {
public:
- ReplaceRootTransformation() {}
+ ReplaceRootTransformation(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : _expCtx(expCtx) {}
Document applyTransformation(Document input) final {
// Extract subdocument in the form of a Value.
- _variables->setRoot(input);
- Value newRoot = _newRoot->evaluate(_variables.get());
+ _expCtx->variables.setRoot(input);
+ Value newRoot = _newRoot->evaluate();
// The newRoot expression, if it exists, must evaluate to an object.
uassert(40228,
@@ -104,7 +105,7 @@ public:
// Create the pointer, parse the stage, and return.
std::unique_ptr<ReplaceRootTransformation> parsedReplaceRoot =
- stdx::make_unique<ReplaceRootTransformation>();
+ stdx::make_unique<ReplaceRootTransformation>(expCtx);
parsedReplaceRoot->parse(expCtx, spec);
return parsedReplaceRoot;
}
@@ -112,8 +113,7 @@ public:
// Check for valid replaceRoot options, and populate internal state variables.
void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONElement& spec) {
// We need a VariablesParseState in order to parse the 'newRoot' expression.
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
// Get the options from this stage. Currently the only option is newRoot.
for (auto&& argument : spec.Obj()) {
@@ -131,12 +131,11 @@ public:
// Check that there was a new root specified.
uassert(40231, "no newRoot specified for the $replaceRoot stage", _newRoot);
- _variables = stdx::make_unique<Variables>(idGenerator.getIdCount());
}
private:
- std::unique_ptr<Variables> _variables;
boost::intrusive_ptr<Expression> _newRoot;
+ const boost::intrusive_ptr<ExpressionContext> _expCtx;
};
REGISTER_DOCUMENT_SOURCE(replaceRoot,
diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp
index b785c89b347..e757edde217 100644
--- a/src/mongo/db/pipeline/document_source_sort.cpp
+++ b/src/mongo/db/pipeline/document_source_sort.cpp
@@ -109,8 +109,7 @@ long long DocumentSourceSort::getLimit() const {
}
void DocumentSourceSort::addKey(StringData fieldPath, bool ascending) {
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = pExpCtx->variablesParseState;
vSortKey.push_back(ExpressionFieldPath::parse(pExpCtx, "$$ROOT." + fieldPath.toString(), vps));
vAscending.push_back(ascending);
}
@@ -192,8 +191,7 @@ intrusive_ptr<DocumentSourceSort> DocumentSourceSort::create(
"$meta is the only expression supported by $sort right now",
metaDoc.firstElement().fieldNameStringData() == "$meta");
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = pExpCtx->variablesParseState;
pSort->vSortKey.push_back(ExpressionMeta::parse(pExpCtx, metaDoc.firstElement(), vps));
// If sorting by textScore, sort highest scores first. If sorting by randVal, order
@@ -308,15 +306,15 @@ void DocumentSourceSort::populateFromCursors(const vector<DBClientCursor*>& curs
}
Value DocumentSourceSort::extractKey(const Document& d) const {
- Variables vars(0, d);
+ pExpCtx->variables.setRoot(d);
if (vSortKey.size() == 1) {
- return vSortKey[0]->evaluate(&vars);
+ return vSortKey[0]->evaluate();
}
vector<Value> keys;
keys.reserve(vSortKey.size());
for (size_t i = 0; i < vSortKey.size(); i++) {
- keys.push_back(vSortKey[i]->evaluate(&vars));
+ keys.push_back(vSortKey[i]->evaluate());
}
return Value(std::move(keys));
}
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 9a5b6b7d81e..17156acae47 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -231,7 +231,7 @@ const char* ExpressionAbs::getOpName() const {
/* ------------------------- ExpressionAdd ----------------------------- */
-Value ExpressionAdd::evaluateInternal(Variables* vars) const {
+Value ExpressionAdd::evaluateInternal() const {
// We'll try to return the narrowest possible result value while avoiding overflow, loss
// of precision due to intermediate rounding or implicit use of decimal types. To do that,
// compute a compensated sum for non-decimal values and a separate decimal sum for decimal
@@ -243,7 +243,7 @@ Value ExpressionAdd::evaluateInternal(Variables* vars) const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluateInternal(vars);
+ Value val = vpOperand[i]->evaluateInternal();
switch (val.getType()) {
case NumberDecimal:
@@ -313,8 +313,8 @@ const char* ExpressionAdd::getOpName() const {
/* ------------------------- ExpressionAllElementsTrue -------------------------- */
-Value ExpressionAllElementsTrue::evaluateInternal(Variables* vars) const {
- const Value arr = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionAllElementsTrue::evaluateInternal() const {
+ const Value arr = vpOperand[0]->evaluateInternal();
uassert(17040,
str::stream() << getOpName() << "'s argument must be an array, but is "
<< typeName(arr.getType()),
@@ -391,10 +391,10 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() {
return pE;
}
-Value ExpressionAnd::evaluateInternal(Variables* vars) const {
+Value ExpressionAnd::evaluateInternal() const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value pValue(vpOperand[i]->evaluateInternal(vars));
+ Value pValue(vpOperand[i]->evaluateInternal());
if (!pValue.coerceToBool())
return Value(false);
}
@@ -409,8 +409,8 @@ const char* ExpressionAnd::getOpName() const {
/* ------------------------- ExpressionAnyElementTrue -------------------------- */
-Value ExpressionAnyElementTrue::evaluateInternal(Variables* vars) const {
- const Value arr = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionAnyElementTrue::evaluateInternal() const {
+ const Value arr = vpOperand[0]->evaluateInternal();
uassert(17041,
str::stream() << getOpName() << "'s argument must be an array, but is "
<< typeName(arr.getType()),
@@ -431,11 +431,11 @@ const char* ExpressionAnyElementTrue::getOpName() const {
/* ---------------------- ExpressionArray --------------------------- */
-Value ExpressionArray::evaluateInternal(Variables* vars) const {
+Value ExpressionArray::evaluateInternal() const {
vector<Value> values;
values.reserve(vpOperand.size());
for (auto&& expr : vpOperand) {
- Value elemVal = expr->evaluateInternal(vars);
+ Value elemVal = expr->evaluateInternal();
values.push_back(elemVal.missing() ? Value(BSONNULL) : std::move(elemVal));
}
return Value(std::move(values));
@@ -457,9 +457,9 @@ const char* ExpressionArray::getOpName() const {
/* ------------------------- ExpressionArrayElemAt -------------------------- */
-Value ExpressionArrayElemAt::evaluateInternal(Variables* vars) const {
- const Value array = vpOperand[0]->evaluateInternal(vars);
- const Value indexArg = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionArrayElemAt::evaluateInternal() const {
+ const Value array = vpOperand[0]->evaluateInternal();
+ const Value indexArg = vpOperand[1]->evaluateInternal();
if (array.nullish() || indexArg.nullish()) {
return Value(BSONNULL);
@@ -499,8 +499,8 @@ const char* ExpressionArrayElemAt::getOpName() const {
/* ------------------------- ExpressionObjectToArray -------------------------- */
-Value ExpressionObjectToArray::evaluateInternal(Variables* vars) const {
- const Value targetVal = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionObjectToArray::evaluateInternal() const {
+ const Value targetVal = vpOperand[0]->evaluateInternal();
if (targetVal.nullish()) {
return Value(BSONNULL);
@@ -531,8 +531,8 @@ const char* ExpressionObjectToArray::getOpName() const {
}
/* ------------------------- ExpressionArrayToObject -------------------------- */
-Value ExpressionArrayToObject::evaluateInternal(Variables* vars) const {
- const Value input = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionArrayToObject::evaluateInternal() const {
+ const Value input = vpOperand[0]->evaluateInternal();
if (input.nullish()) {
return Value(BSONNULL);
}
@@ -680,8 +680,8 @@ void ExpressionCoerceToBool::addDependencies(DepsTracker* deps) const {
pExpression->addDependencies(deps);
}
-Value ExpressionCoerceToBool::evaluateInternal(Variables* vars) const {
- Value pResult(pExpression->evaluateInternal(vars));
+Value ExpressionCoerceToBool::evaluateInternal() const {
+ Value pResult(pExpression->evaluateInternal());
bool b = pResult.coerceToBool();
if (b)
return Value(true);
@@ -782,9 +782,9 @@ static const CmpLookup cmpLookup[7] = {
};
}
-Value ExpressionCompare::evaluateInternal(Variables* vars) const {
- Value pLeft(vpOperand[0]->evaluateInternal(vars));
- Value pRight(vpOperand[1]->evaluateInternal(vars));
+Value ExpressionCompare::evaluateInternal() const {
+ Value pLeft(vpOperand[0]->evaluateInternal());
+ Value pRight(vpOperand[1]->evaluateInternal());
int cmp = getExpressionContext()->getValueComparator().compare(pLeft, pRight);
@@ -810,12 +810,12 @@ const char* ExpressionCompare::getOpName() const {
/* ------------------------- ExpressionConcat ----------------------------- */
-Value ExpressionConcat::evaluateInternal(Variables* vars) const {
+Value ExpressionConcat::evaluateInternal() const {
const size_t n = vpOperand.size();
StringBuilder result;
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluateInternal(vars);
+ Value val = vpOperand[i]->evaluateInternal();
if (val.nullish())
return Value(BSONNULL);
@@ -836,12 +836,12 @@ const char* ExpressionConcat::getOpName() const {
/* ------------------------- ExpressionConcatArrays ----------------------------- */
-Value ExpressionConcatArrays::evaluateInternal(Variables* vars) const {
+Value ExpressionConcatArrays::evaluateInternal() const {
const size_t n = vpOperand.size();
vector<Value> values;
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluateInternal(vars);
+ Value val = vpOperand[i]->evaluateInternal();
if (val.nullish()) {
return Value(BSONNULL);
}
@@ -864,10 +864,10 @@ const char* ExpressionConcatArrays::getOpName() const {
/* ----------------------- ExpressionCond ------------------------------ */
-Value ExpressionCond::evaluateInternal(Variables* vars) const {
- Value pCond(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionCond::evaluateInternal() const {
+ Value pCond(vpOperand[0]->evaluateInternal());
int idx = pCond.coerceToBool() ? 1 : 2;
- return vpOperand[idx]->evaluateInternal(vars);
+ return vpOperand[idx]->evaluateInternal();
}
intrusive_ptr<Expression> ExpressionCond::parse(
@@ -938,7 +938,7 @@ void ExpressionConstant::addDependencies(DepsTracker* deps) const {
/* nothing to do */
}
-Value ExpressionConstant::evaluateInternal(Variables* vars) const {
+Value ExpressionConstant::evaluateInternal() const {
return pValue;
}
@@ -1008,8 +1008,8 @@ Value ExpressionDateToString::serialize(bool explain) const {
DOC("$dateToString" << DOC("format" << _format << "date" << _date->serialize(explain))));
}
-Value ExpressionDateToString::evaluateInternal(Variables* vars) const {
- const Value date = _date->evaluateInternal(vars);
+Value ExpressionDateToString::evaluateInternal() const {
+ const Value date = _date->evaluateInternal();
if (date.nullish()) {
return Value(BSONNULL);
@@ -1158,8 +1158,8 @@ void ExpressionDateToString::addDependencies(DepsTracker* deps) const {
/* ---------------------- ExpressionDayOfMonth ------------------------- */
-Value ExpressionDayOfMonth::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionDayOfMonth::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -1170,8 +1170,8 @@ const char* ExpressionDayOfMonth::getOpName() const {
/* ------------------------- ExpressionDayOfWeek ----------------------------- */
-Value ExpressionDayOfWeek::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionDayOfWeek::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -1182,8 +1182,8 @@ const char* ExpressionDayOfWeek::getOpName() const {
/* ------------------------- ExpressionDayOfYear ----------------------------- */
-Value ExpressionDayOfYear::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionDayOfYear::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -1194,9 +1194,9 @@ const char* ExpressionDayOfYear::getOpName() const {
/* ----------------------- ExpressionDivide ---------------------------- */
-Value ExpressionDivide::evaluateInternal(Variables* vars) const {
- Value lhs = vpOperand[0]->evaluateInternal(vars);
- Value rhs = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionDivide::evaluateInternal() const {
+ Value lhs = vpOperand[0]->evaluateInternal();
+ Value rhs = vpOperand[1]->evaluateInternal();
auto assertNonZero = [](bool nonZero) { uassert(16608, "can't $divide by zero", nonZero); };
@@ -1295,10 +1295,10 @@ void ExpressionObject::addDependencies(DepsTracker* deps) const {
}
}
-Value ExpressionObject::evaluateInternal(Variables* vars) const {
+Value ExpressionObject::evaluateInternal() const {
MutableDocument outputDoc;
for (auto&& pair : _expressions) {
- outputDoc.addField(pair.first, pair.second->evaluateInternal(vars));
+ outputDoc.addField(pair.first, pair.second->evaluateInternal());
}
return outputDoc.freezeToValue();
}
@@ -1408,16 +1408,17 @@ Value ExpressionFieldPath::evaluatePath(size_t index, const Document& input) con
}
}
-Value ExpressionFieldPath::evaluateInternal(Variables* vars) const {
+Value ExpressionFieldPath::evaluateInternal() const {
+ auto& vars = getExpressionContext()->variables;
if (_fieldPath.getPathLength() == 1) // get the whole variable
- return vars->getValue(_variable);
+ return vars.getValue(_variable);
if (_variable == Variables::kRootId) {
// ROOT is always a document so use optimized code path
- return evaluatePath(1, vars->getRoot());
+ return evaluatePath(1, vars.getRoot());
}
- Value var = vars->getValue(_variable);
+ Value var = vars.getValue(_variable);
switch (var.getType()) {
case Object:
return evaluatePath(1, var.getDocument());
@@ -1511,9 +1512,9 @@ Value ExpressionFilter::serialize(bool explain) const {
<< _filter->serialize(explain))));
}
-Value ExpressionFilter::evaluateInternal(Variables* vars) const {
+Value ExpressionFilter::evaluateInternal() const {
// We are guaranteed at parse time that this isn't using our _varId.
- const Value inputVal = _input->evaluateInternal(vars);
+ const Value inputVal = _input->evaluateInternal();
if (inputVal.nullish())
return Value(BSONNULL);
@@ -1528,10 +1529,11 @@ Value ExpressionFilter::evaluateInternal(Variables* vars) const {
return inputVal;
vector<Value> output;
+ auto& vars = getExpressionContext()->variables;
for (const auto& elem : input) {
- vars->setValue(_varId, elem);
+ vars.setValue(_varId, elem);
- if (_filter->evaluateInternal(vars).coerceToBool()) {
+ if (_filter->evaluateInternal().coerceToBool()) {
output.push_back(std::move(elem));
}
}
@@ -1644,15 +1646,15 @@ Value ExpressionLet::serialize(bool explain) const {
DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain))));
}
-Value ExpressionLet::evaluateInternal(Variables* vars) const {
- for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end;
- ++it) {
+Value ExpressionLet::evaluateInternal() const {
+ for (const auto& item : _variables) {
// It is guaranteed at parse-time that these expressions don't use the variable ids we
// are setting
- vars->setValue(it->first, it->second.expression->evaluateInternal(vars));
+ getExpressionContext()->variables.setValue(item.first,
+ item.second.expression->evaluateInternal());
}
- return _subExpression->evaluateInternal(vars);
+ return _subExpression->evaluateInternal();
}
void ExpressionLet::addDependencies(DepsTracker* deps) const {
@@ -1737,9 +1739,9 @@ Value ExpressionMap::serialize(bool explain) const {
<< _each->serialize(explain))));
}
-Value ExpressionMap::evaluateInternal(Variables* vars) const {
+Value ExpressionMap::evaluateInternal() const {
// guaranteed at parse time that this isn't using our _varId
- const Value inputVal = _input->evaluateInternal(vars);
+ const Value inputVal = _input->evaluateInternal();
if (inputVal.nullish())
return Value(BSONNULL);
@@ -1755,9 +1757,9 @@ Value ExpressionMap::evaluateInternal(Variables* vars) const {
vector<Value> output;
output.reserve(input.size());
for (size_t i = 0; i < input.size(); i++) {
- vars->setValue(_varId, input[i]);
+ getExpressionContext()->variables.setValue(_varId, input[i]);
- Value toInsert = _each->evaluateInternal(vars);
+ Value toInsert = _each->evaluateInternal();
if (toInsert.missing())
toInsert = Value(BSONNULL); // can't insert missing values into array
@@ -1805,8 +1807,8 @@ Value ExpressionMeta::serialize(bool explain) const {
MONGO_UNREACHABLE;
}
-Value ExpressionMeta::evaluateInternal(Variables* vars) const {
- const Document& root = vars->getRoot();
+Value ExpressionMeta::evaluateInternal() const {
+ const Document& root = getExpressionContext()->variables.getRoot();
switch (_metaType) {
case MetaType::TEXT_SCORE:
return root.hasTextScore() ? Value(root.getTextScore()) : Value();
@@ -1824,8 +1826,8 @@ void ExpressionMeta::addDependencies(DepsTracker* deps) const {
/* ------------------------- ExpressionMillisecond ----------------------------- */
-Value ExpressionMillisecond::evaluateInternal(Variables* vars) const {
- Value date(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionMillisecond::evaluateInternal() const {
+ Value date(vpOperand[0]->evaluateInternal());
return Value(extract(date.coerceToDate()));
}
@@ -1842,8 +1844,8 @@ const char* ExpressionMillisecond::getOpName() const {
/* ------------------------- ExpressionMinute -------------------------- */
-Value ExpressionMinute::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionMinute::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -1854,9 +1856,9 @@ const char* ExpressionMinute::getOpName() const {
/* ----------------------- ExpressionMod ---------------------------- */
-Value ExpressionMod::evaluateInternal(Variables* vars) const {
- Value lhs = vpOperand[0]->evaluateInternal(vars);
- Value rhs = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionMod::evaluateInternal() const {
+ Value lhs = vpOperand[0]->evaluateInternal();
+ Value rhs = vpOperand[1]->evaluateInternal();
BSONType leftType = lhs.getType();
BSONType rightType = rhs.getType();
@@ -1911,8 +1913,8 @@ const char* ExpressionMod::getOpName() const {
/* ------------------------ ExpressionMonth ----------------------------- */
-Value ExpressionMonth::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionMonth::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -1923,7 +1925,7 @@ const char* ExpressionMonth::getOpName() const {
/* ------------------------- ExpressionMultiply ----------------------------- */
-Value ExpressionMultiply::evaluateInternal(Variables* vars) const {
+Value ExpressionMultiply::evaluateInternal() const {
/*
We'll try to return the narrowest possible result value. To do that
without creating intermediate Values, do the arithmetic for double
@@ -1938,7 +1940,7 @@ Value ExpressionMultiply::evaluateInternal(Variables* vars) const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluateInternal(vars);
+ Value val = vpOperand[i]->evaluateInternal();
if (val.numeric()) {
BSONType oldProductType = productType;
@@ -1986,8 +1988,8 @@ const char* ExpressionMultiply::getOpName() const {
/* ------------------------- ExpressionHour ----------------------------- */
-Value ExpressionHour::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionHour::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -1998,12 +2000,12 @@ const char* ExpressionHour::getOpName() const {
/* ----------------------- ExpressionIfNull ---------------------------- */
-Value ExpressionIfNull::evaluateInternal(Variables* vars) const {
- Value pLeft(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionIfNull::evaluateInternal() const {
+ Value pLeft(vpOperand[0]->evaluateInternal());
if (!pLeft.nullish())
return pLeft;
- Value pRight(vpOperand[1]->evaluateInternal(vars));
+ Value pRight(vpOperand[1]->evaluateInternal());
return pRight;
}
@@ -2014,9 +2016,9 @@ const char* ExpressionIfNull::getOpName() const {
/* ----------------------- ExpressionIn ---------------------------- */
-Value ExpressionIn::evaluateInternal(Variables* vars) const {
- Value argument(vpOperand[0]->evaluateInternal(vars));
- Value arrayOfValues(vpOperand[1]->evaluateInternal(vars));
+Value ExpressionIn::evaluateInternal() const {
+ Value argument(vpOperand[0]->evaluateInternal());
+ Value arrayOfValues(vpOperand[1]->evaluateInternal());
uassert(40081,
str::stream() << "$in requires an array as a second argument, found: "
@@ -2058,8 +2060,8 @@ void uassertIfNotIntegralAndNonNegative(Value val,
} // namespace
-Value ExpressionIndexOfArray::evaluateInternal(Variables* vars) const {
- Value arrayArg = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionIndexOfArray::evaluateInternal() const {
+ Value arrayArg = vpOperand[0]->evaluateInternal();
if (arrayArg.nullish()) {
return Value(BSONNULL);
@@ -2072,18 +2074,18 @@ Value ExpressionIndexOfArray::evaluateInternal(Variables* vars) const {
std::vector<Value> array = arrayArg.getArray();
- Value searchItem = vpOperand[1]->evaluateInternal(vars);
+ Value searchItem = vpOperand[1]->evaluateInternal();
size_t startIndex = 0;
if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluateInternal(vars);
+ Value startIndexArg = vpOperand[2]->evaluateInternal();
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
size_t endIndex = array.size();
if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluateInternal(vars);
+ Value endIndexArg = vpOperand[3]->evaluateInternal();
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endIndex' exceed the length of the array.
endIndex = std::min(array.size(), static_cast<size_t>(endIndexArg.coerceToInt()));
@@ -2116,8 +2118,8 @@ bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::st
} // namespace
-Value ExpressionIndexOfBytes::evaluateInternal(Variables* vars) const {
- Value stringArg = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionIndexOfBytes::evaluateInternal() const {
+ Value stringArg = vpOperand[0]->evaluateInternal();
if (stringArg.nullish()) {
return Value(BSONNULL);
@@ -2129,7 +2131,7 @@ Value ExpressionIndexOfBytes::evaluateInternal(Variables* vars) const {
stringArg.getType() == String);
const std::string& input = stringArg.getString();
- Value tokenArg = vpOperand[1]->evaluateInternal(vars);
+ Value tokenArg = vpOperand[1]->evaluateInternal();
uassert(40092,
str::stream() << "$indexOfBytes requires a string as the second argument, found: "
<< typeName(tokenArg.getType()),
@@ -2138,14 +2140,14 @@ Value ExpressionIndexOfBytes::evaluateInternal(Variables* vars) const {
size_t startIndex = 0;
if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluateInternal(vars);
+ Value startIndexArg = vpOperand[2]->evaluateInternal();
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
size_t endIndex = input.size();
if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluateInternal(vars);
+ Value endIndexArg = vpOperand[3]->evaluateInternal();
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endIndex' exceed the length of the string.
endIndex = std::min(input.size(), static_cast<size_t>(endIndexArg.coerceToInt()));
@@ -2170,8 +2172,8 @@ const char* ExpressionIndexOfBytes::getOpName() const {
/* ----------------------- ExpressionIndexOfCP --------------------- */
-Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const {
- Value stringArg = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionIndexOfCP::evaluateInternal() const {
+ Value stringArg = vpOperand[0]->evaluateInternal();
if (stringArg.nullish()) {
return Value(BSONNULL);
@@ -2183,7 +2185,7 @@ Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const {
stringArg.getType() == String);
const std::string& input = stringArg.getString();
- Value tokenArg = vpOperand[1]->evaluateInternal(vars);
+ Value tokenArg = vpOperand[1]->evaluateInternal();
uassert(40094,
str::stream() << "$indexOfCP requires a string as the second argument, found: "
<< typeName(tokenArg.getType()),
@@ -2192,7 +2194,7 @@ Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const {
size_t startCodePointIndex = 0;
if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluateInternal(vars);
+ Value startIndexArg = vpOperand[2]->evaluateInternal();
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startCodePointIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
@@ -2214,7 +2216,7 @@ Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const {
size_t endCodePointIndex = codePointLength;
if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluateInternal(vars);
+ Value endIndexArg = vpOperand[3]->evaluateInternal();
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endCodePointIndex' exceed the number of code points in the string.
@@ -2271,9 +2273,9 @@ const char* ExpressionLn::getOpName() const {
/* ----------------------- ExpressionLog ---------------------------- */
-Value ExpressionLog::evaluateInternal(Variables* vars) const {
- Value argVal = vpOperand[0]->evaluateInternal(vars);
- Value baseVal = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionLog::evaluateInternal() const {
+ Value argVal = vpOperand[0]->evaluateInternal();
+ Value baseVal = vpOperand[1]->evaluateInternal();
if (argVal.nullish() || baseVal.nullish())
return Value(BSONNULL);
@@ -2365,9 +2367,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
// If all the operands are constant expressions, collapse the expression into one constant
// expression.
if (constOperandCount == vpOperand.size()) {
- Variables emptyVars;
return intrusive_ptr<Expression>(
- ExpressionConstant::create(getExpressionContext(), evaluateInternal(&emptyVars)));
+ ExpressionConstant::create(getExpressionContext(), evaluateInternal()));
}
// If the expression is associative, we can collapse all the consecutive constant operands into
@@ -2410,9 +2411,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
if (constExpressions.size() > 1) {
ExpressionVector vpOperandSave = std::move(vpOperand);
vpOperand = std::move(constExpressions);
- Variables emptyVars;
- optimizedOperands.emplace_back(ExpressionConstant::create(
- getExpressionContext(), evaluateInternal(&emptyVars)));
+ optimizedOperands.emplace_back(
+ ExpressionConstant::create(getExpressionContext(), evaluateInternal()));
vpOperand = std::move(vpOperandSave);
} else {
optimizedOperands.insert(
@@ -2426,9 +2426,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
if (constExpressions.size() > 1) {
vpOperand = std::move(constExpressions);
- Variables emptyVars;
optimizedOperands.emplace_back(
- ExpressionConstant::create(getExpressionContext(), evaluateInternal(&emptyVars)));
+ ExpressionConstant::create(getExpressionContext(), evaluateInternal()));
} else {
optimizedOperands.insert(
optimizedOperands.end(), constExpressions.begin(), constExpressions.end());
@@ -2461,8 +2460,8 @@ Value ExpressionNary::serialize(bool explain) const {
/* ------------------------- ExpressionNot ----------------------------- */
-Value ExpressionNot::evaluateInternal(Variables* vars) const {
- Value pOp(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionNot::evaluateInternal() const {
+ Value pOp(vpOperand[0]->evaluateInternal());
bool b = pOp.coerceToBool();
return Value(!b);
@@ -2475,10 +2474,10 @@ const char* ExpressionNot::getOpName() const {
/* -------------------------- ExpressionOr ----------------------------- */
-Value ExpressionOr::evaluateInternal(Variables* vars) const {
+Value ExpressionOr::evaluateInternal() const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value pValue(vpOperand[i]->evaluateInternal(vars));
+ Value pValue(vpOperand[i]->evaluateInternal());
if (pValue.coerceToBool())
return Value(true);
}
@@ -2555,9 +2554,9 @@ intrusive_ptr<Expression> ExpressionPow::create(
return expr;
}
-Value ExpressionPow::evaluateInternal(Variables* vars) const {
- Value baseVal = vpOperand[0]->evaluateInternal(vars);
- Value expVal = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionPow::evaluateInternal() const {
+ Value baseVal = vpOperand[0]->evaluateInternal();
+ Value expVal = vpOperand[1]->evaluateInternal();
if (baseVal.nullish() || expVal.nullish())
return Value(BSONNULL);
@@ -2715,9 +2714,9 @@ const char* ExpressionPow::getOpName() const {
/* ------------------------- ExpressionRange ------------------------------ */
-Value ExpressionRange::evaluateInternal(Variables* vars) const {
- Value startVal(vpOperand[0]->evaluateInternal(vars));
- Value endVal(vpOperand[1]->evaluateInternal(vars));
+Value ExpressionRange::evaluateInternal() const {
+ Value startVal(vpOperand[0]->evaluateInternal());
+ Value endVal(vpOperand[1]->evaluateInternal());
uassert(34443,
str::stream() << "$range requires a numeric starting value, found value of type: "
@@ -2744,7 +2743,7 @@ Value ExpressionRange::evaluateInternal(Variables* vars) const {
int step = 1;
if (vpOperand.size() == 3) {
// A step was specified by the user.
- Value stepVal(vpOperand[2]->evaluateInternal(vars));
+ Value stepVal(vpOperand[2]->evaluateInternal());
uassert(34447,
str::stream() << "$range requires a numeric step value, found value of type:"
@@ -2815,8 +2814,8 @@ intrusive_ptr<Expression> ExpressionReduce::parse(
return reduce;
}
-Value ExpressionReduce::evaluateInternal(Variables* vars) const {
- Value inputVal = _input->evaluateInternal(vars);
+Value ExpressionReduce::evaluateInternal() const {
+ Value inputVal = _input->evaluateInternal();
if (inputVal.nullish()) {
return Value(BSONNULL);
@@ -2827,13 +2826,14 @@ Value ExpressionReduce::evaluateInternal(Variables* vars) const {
<< inputVal.toString(),
inputVal.isArray());
- Value accumulatedValue = _initial->evaluateInternal(vars);
+ Value accumulatedValue = _initial->evaluateInternal();
+ auto& vars = getExpressionContext()->variables;
for (auto&& elem : inputVal.getArray()) {
- vars->setValue(_thisVar, elem);
- vars->setValue(_valueVar, accumulatedValue);
+ vars.setValue(_thisVar, elem);
+ vars.setValue(_valueVar, accumulatedValue);
- accumulatedValue = _in->evaluateInternal(vars);
+ accumulatedValue = _in->evaluateInternal();
}
return accumulatedValue;
@@ -2861,8 +2861,8 @@ Value ExpressionReduce::serialize(bool explain) const {
/* ------------------------ ExpressionReverseArray ------------------------ */
-Value ExpressionReverseArray::evaluateInternal(Variables* vars) const {
- Value input(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionReverseArray::evaluateInternal() const {
+ Value input(vpOperand[0]->evaluateInternal());
if (input.nullish()) {
return Value(BSONNULL);
@@ -2889,8 +2889,8 @@ const char* ExpressionReverseArray::getOpName() const {
/* ------------------------- ExpressionSecond ----------------------------- */
-Value ExpressionSecond::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionSecond::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -2910,9 +2910,9 @@ ValueSet arrayToSet(const Value& val, const ValueComparator& valueComparator) {
/* ----------------------- ExpressionSetDifference ---------------------------- */
-Value ExpressionSetDifference::evaluateInternal(Variables* vars) const {
- const Value lhs = vpOperand[0]->evaluateInternal(vars);
- const Value rhs = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionSetDifference::evaluateInternal() const {
+ const Value lhs = vpOperand[0]->evaluateInternal();
+ const Value rhs = vpOperand[1]->evaluateInternal();
if (lhs.nullish() || rhs.nullish()) {
return Value(BSONNULL);
@@ -2956,13 +2956,13 @@ void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const
args.size() >= 2);
}
-Value ExpressionSetEquals::evaluateInternal(Variables* vars) const {
+Value ExpressionSetEquals::evaluateInternal() const {
const size_t n = vpOperand.size();
const auto& valueComparator = getExpressionContext()->getValueComparator();
ValueSet lhs = valueComparator.makeOrderedValueSet();
for (size_t i = 0; i < n; i++) {
- const Value nextEntry = vpOperand[i]->evaluateInternal(vars);
+ const Value nextEntry = vpOperand[i]->evaluateInternal();
uassert(17044,
str::stream() << "All operands of $setEquals must be arrays. One "
<< "argument is of type: "
@@ -2993,12 +2993,12 @@ const char* ExpressionSetEquals::getOpName() const {
/* ----------------------- ExpressionSetIntersection ---------------------------- */
-Value ExpressionSetIntersection::evaluateInternal(Variables* vars) const {
+Value ExpressionSetIntersection::evaluateInternal() const {
const size_t n = vpOperand.size();
const auto& valueComparator = getExpressionContext()->getValueComparator();
ValueSet currentIntersection = valueComparator.makeOrderedValueSet();
for (size_t i = 0; i < n; i++) {
- const Value nextEntry = vpOperand[i]->evaluateInternal(vars);
+ const Value nextEntry = vpOperand[i]->evaluateInternal();
if (nextEntry.nullish()) {
return Value(BSONNULL);
}
@@ -3054,9 +3054,9 @@ Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) {
}
}
-Value ExpressionSetIsSubset::evaluateInternal(Variables* vars) const {
- const Value lhs = vpOperand[0]->evaluateInternal(vars);
- const Value rhs = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionSetIsSubset::evaluateInternal() const {
+ const Value lhs = vpOperand[0]->evaluateInternal();
+ const Value rhs = vpOperand[1]->evaluateInternal();
uassert(17046,
str::stream() << "both operands of $setIsSubset must be arrays. First "
@@ -3089,8 +3089,8 @@ public:
vpOperand = operands;
}
- virtual Value evaluateInternal(Variables* vars) const {
- const Value lhs = vpOperand[0]->evaluateInternal(vars);
+ virtual Value evaluateInternal() const {
+ const Value lhs = vpOperand[0]->evaluateInternal();
uassert(17310,
str::stream() << "both operands of $setIsSubset must be arrays. First "
@@ -3137,11 +3137,11 @@ const char* ExpressionSetIsSubset::getOpName() const {
/* ----------------------- ExpressionSetUnion ---------------------------- */
-Value ExpressionSetUnion::evaluateInternal(Variables* vars) const {
+Value ExpressionSetUnion::evaluateInternal() const {
ValueSet unionedSet = getExpressionContext()->getValueComparator().makeOrderedValueSet();
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; i++) {
- const Value newEntries = vpOperand[i]->evaluateInternal(vars);
+ const Value newEntries = vpOperand[i]->evaluateInternal();
if (newEntries.nullish()) {
return Value(BSONNULL);
}
@@ -3163,8 +3163,8 @@ const char* ExpressionSetUnion::getOpName() const {
/* ----------------------- ExpressionIsArray ---------------------------- */
-Value ExpressionIsArray::evaluateInternal(Variables* vars) const {
- Value argument = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionIsArray::evaluateInternal() const {
+ Value argument = vpOperand[0]->evaluateInternal();
return Value(argument.isArray());
}
@@ -3175,12 +3175,12 @@ const char* ExpressionIsArray::getOpName() const {
/* ----------------------- ExpressionSlice ---------------------------- */
-Value ExpressionSlice::evaluateInternal(Variables* vars) const {
+Value ExpressionSlice::evaluateInternal() const {
const size_t n = vpOperand.size();
- Value arrayVal = vpOperand[0]->evaluateInternal(vars);
+ Value arrayVal = vpOperand[0]->evaluateInternal();
// Could be either a start index or the length from 0.
- Value arg2 = vpOperand[1]->evaluateInternal(vars);
+ Value arg2 = vpOperand[1]->evaluateInternal();
if (arrayVal.nullish() || arg2.nullish()) {
return Value(BSONNULL);
@@ -3231,7 +3231,7 @@ Value ExpressionSlice::evaluateInternal(Variables* vars) const {
start = std::min(array.size(), size_t(startInt));
}
- Value countVal = vpOperand[2]->evaluateInternal(vars);
+ Value countVal = vpOperand[2]->evaluateInternal();
if (countVal.nullish()) {
return Value(BSONNULL);
@@ -3266,8 +3266,8 @@ const char* ExpressionSlice::getOpName() const {
/* ----------------------- ExpressionSize ---------------------------- */
-Value ExpressionSize::evaluateInternal(Variables* vars) const {
- Value array = vpOperand[0]->evaluateInternal(vars);
+Value ExpressionSize::evaluateInternal() const {
+ Value array = vpOperand[0]->evaluateInternal();
uassert(17124,
str::stream() << "The argument to $size must be an array, but was of type: "
@@ -3283,9 +3283,9 @@ const char* ExpressionSize::getOpName() const {
/* ----------------------- ExpressionSplit --------------------------- */
-Value ExpressionSplit::evaluateInternal(Variables* vars) const {
- Value inputArg = vpOperand[0]->evaluateInternal(vars);
- Value separatorArg = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionSplit::evaluateInternal() const {
+ Value inputArg = vpOperand[0]->evaluateInternal();
+ Value separatorArg = vpOperand[1]->evaluateInternal();
if (inputArg.nullish() || separatorArg.nullish()) {
return Value(BSONNULL);
@@ -3361,9 +3361,9 @@ const char* ExpressionSqrt::getOpName() const {
/* ----------------------- ExpressionStrcasecmp ---------------------------- */
-Value ExpressionStrcasecmp::evaluateInternal(Variables* vars) const {
- Value pString1(vpOperand[0]->evaluateInternal(vars));
- Value pString2(vpOperand[1]->evaluateInternal(vars));
+Value ExpressionStrcasecmp::evaluateInternal() const {
+ Value pString1(vpOperand[0]->evaluateInternal());
+ Value pString2(vpOperand[1]->evaluateInternal());
/* boost::iequals returns a bool not an int so strings must actually be allocated */
string str1 = boost::to_upper_copy(pString1.coerceToString());
@@ -3385,10 +3385,10 @@ const char* ExpressionStrcasecmp::getOpName() const {
/* ----------------------- ExpressionSubstrBytes ---------------------------- */
-Value ExpressionSubstrBytes::evaluateInternal(Variables* vars) const {
- Value pString(vpOperand[0]->evaluateInternal(vars));
- Value pLower(vpOperand[1]->evaluateInternal(vars));
- Value pLength(vpOperand[2]->evaluateInternal(vars));
+Value ExpressionSubstrBytes::evaluateInternal() const {
+ Value pString(vpOperand[0]->evaluateInternal());
+ Value pLower(vpOperand[1]->evaluateInternal());
+ Value pLength(vpOperand[2]->evaluateInternal());
string str = pString.coerceToString();
uassert(16034,
@@ -3438,10 +3438,10 @@ const char* ExpressionSubstrBytes::getOpName() const {
/* ----------------------- ExpressionSubstrCP ---------------------------- */
-Value ExpressionSubstrCP::evaluateInternal(Variables* vars) const {
- Value inputVal(vpOperand[0]->evaluateInternal(vars));
- Value lowerVal(vpOperand[1]->evaluateInternal(vars));
- Value lengthVal(vpOperand[2]->evaluateInternal(vars));
+Value ExpressionSubstrCP::evaluateInternal() const {
+ Value inputVal(vpOperand[0]->evaluateInternal());
+ Value lowerVal(vpOperand[1]->evaluateInternal());
+ Value lengthVal(vpOperand[2]->evaluateInternal());
std::string str = inputVal.coerceToString();
uassert(34450,
@@ -3513,8 +3513,8 @@ const char* ExpressionSubstrCP::getOpName() const {
/* ----------------------- ExpressionStrLenBytes ------------------------- */
-Value ExpressionStrLenBytes::evaluateInternal(Variables* vars) const {
- Value str(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionStrLenBytes::evaluateInternal() const {
+ Value str(vpOperand[0]->evaluateInternal());
uassert(34473,
str::stream() << "$strLenBytes requires a string argument, found: "
@@ -3536,8 +3536,8 @@ const char* ExpressionStrLenBytes::getOpName() const {
/* ----------------------- ExpressionStrLenCP ------------------------- */
-Value ExpressionStrLenCP::evaluateInternal(Variables* vars) const {
- Value val(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionStrLenCP::evaluateInternal() const {
+ Value val(vpOperand[0]->evaluateInternal());
uassert(34471,
str::stream() << "$strLenCP requires a string argument, found: "
@@ -3565,9 +3565,9 @@ const char* ExpressionStrLenCP::getOpName() const {
/* ----------------------- ExpressionSubtract ---------------------------- */
-Value ExpressionSubtract::evaluateInternal(Variables* vars) const {
- Value lhs = vpOperand[0]->evaluateInternal(vars);
- Value rhs = vpOperand[1]->evaluateInternal(vars);
+Value ExpressionSubtract::evaluateInternal() const {
+ Value lhs = vpOperand[0]->evaluateInternal();
+ Value rhs = vpOperand[1]->evaluateInternal();
BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType());
@@ -3617,12 +3617,12 @@ const char* ExpressionSubtract::getOpName() const {
REGISTER_EXPRESSION(switch, ExpressionSwitch::parse);
-Value ExpressionSwitch::evaluateInternal(Variables* vars) const {
+Value ExpressionSwitch::evaluateInternal() const {
for (auto&& branch : _branches) {
- Value caseExpression(branch.first->evaluateInternal(vars));
+ Value caseExpression(branch.first->evaluateInternal());
if (caseExpression.coerceToBool()) {
- return branch.second->evaluateInternal(vars);
+ return branch.second->evaluateInternal();
}
}
@@ -3630,7 +3630,7 @@ Value ExpressionSwitch::evaluateInternal(Variables* vars) const {
"$switch could not find a matching branch for an input, and no default was specified.",
_default);
- return _default->evaluateInternal(vars);
+ return _default->evaluateInternal();
}
boost::intrusive_ptr<Expression> ExpressionSwitch::parse(
@@ -3744,8 +3744,8 @@ Value ExpressionSwitch::serialize(bool explain) const {
/* ------------------------- ExpressionToLower ----------------------------- */
-Value ExpressionToLower::evaluateInternal(Variables* vars) const {
- Value pString(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionToLower::evaluateInternal() const {
+ Value pString(vpOperand[0]->evaluateInternal());
string str = pString.coerceToString();
boost::to_lower(str);
return Value(str);
@@ -3758,8 +3758,8 @@ const char* ExpressionToLower::getOpName() const {
/* ------------------------- ExpressionToUpper -------------------------- */
-Value ExpressionToUpper::evaluateInternal(Variables* vars) const {
- Value pString(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionToUpper::evaluateInternal() const {
+ Value pString(vpOperand[0]->evaluateInternal());
string str(pString.coerceToString());
boost::to_upper(str);
return Value(str);
@@ -3792,8 +3792,8 @@ const char* ExpressionTrunc::getOpName() const {
/* ------------------------- ExpressionType ----------------------------- */
-Value ExpressionType::evaluateInternal(Variables* vars) const {
- Value val(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionType::evaluateInternal() const {
+ Value val(vpOperand[0]->evaluateInternal());
return Value(StringData(typeName(val.getType())));
}
@@ -3804,8 +3804,8 @@ const char* ExpressionType::getOpName() const {
/* ------------------------- ExpressionWeek ----------------------------- */
-Value ExpressionWeek::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionWeek::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -3836,8 +3836,8 @@ const char* ExpressionWeek::getOpName() const {
/* ------------------------- ExpressionIsoDayOfWeek --------------------- */
-Value ExpressionIsoDayOfWeek::evaluateInternal(Variables* vars) const {
- Value date(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionIsoDayOfWeek::evaluateInternal() const {
+ Value date(vpOperand[0]->evaluateInternal());
return Value(extract(date.coerceToTm()));
}
@@ -3853,8 +3853,8 @@ const char* ExpressionIsoDayOfWeek::getOpName() const {
/* ------------------------- ExpressionIsoWeekYear ---------------------- */
-Value ExpressionIsoWeekYear::evaluateInternal(Variables* vars) const {
- Value date(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionIsoWeekYear::evaluateInternal() const {
+ Value date(vpOperand[0]->evaluateInternal());
return Value(extract(date.coerceToTm()));
}
@@ -3939,8 +3939,8 @@ int lastWeek(int year) {
}
}
-Value ExpressionIsoWeek::evaluateInternal(Variables* vars) const {
- Value date(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionIsoWeek::evaluateInternal() const {
+ Value date(vpOperand[0]->evaluateInternal());
return Value(extract(date.coerceToTm()));
}
@@ -3988,8 +3988,8 @@ const char* ExpressionIsoWeek::getOpName() const {
/* ------------------------- ExpressionYear ----------------------------- */
-Value ExpressionYear::evaluateInternal(Variables* vars) const {
- Value pDate(vpOperand[0]->evaluateInternal(vars));
+Value ExpressionYear::evaluateInternal() const {
+ Value pDate(vpOperand[0]->evaluateInternal());
return Value(extract(pDate.coerceToTm()));
}
@@ -4053,7 +4053,7 @@ intrusive_ptr<Expression> ExpressionZip::parse(
return std::move(newZip);
}
-Value ExpressionZip::evaluateInternal(Variables* vars) const {
+Value ExpressionZip::evaluateInternal() const {
// Evaluate input values.
vector<vector<Value>> inputValues;
inputValues.reserve(_inputs.size());
@@ -4061,7 +4061,7 @@ Value ExpressionZip::evaluateInternal(Variables* vars) const {
size_t minArraySize = 0;
size_t maxArraySize = 0;
for (size_t i = 0; i < _inputs.size(); i++) {
- Value evalExpr = _inputs[i]->evaluateInternal(vars);
+ Value evalExpr = _inputs[i]->evaluateInternal();
if (evalExpr.nullish()) {
return Value(BSONNULL);
}
@@ -4090,7 +4090,7 @@ Value ExpressionZip::evaluateInternal(Variables* vars) const {
// If we need default values, evaluate each expression.
if (minArraySize != maxArraySize) {
for (size_t i = 0; i < _defaults.size(); i++) {
- evaluatedDefaults[i] = _defaults[i]->evaluateInternal(vars);
+ evaluatedDefaults[i] = _defaults[i]->evaluateInternal();
}
}
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index 62bf6904fc5..0bb366223a3 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -108,19 +108,15 @@ public:
* This method should only be used for testing.
*/
Value evaluate(const Document& root) const {
- Variables vars(0, root);
- return evaluate(&vars);
+ getExpressionContext()->variables.setRoot(root);
+ return evaluate();
}
/**
- * Evaluate expression with variables given by 'vars', and return the result.
- *
- * While vars is non-const, a subexpression's modifications to it should not effect outer
- * Expressions, since variables defined in the subexpression's scope will be given unique
- * variable ids.
+ * Evaluate expression and return the result.
*/
- Value evaluate(Variables* vars) const {
- return evaluateInternal(vars);
+ Value evaluate() const {
+ return evaluateInternal();
}
/**
@@ -168,12 +164,13 @@ public:
*/
static std::string removeFieldPrefix(const std::string& prefixedField);
- /** Evaluate the subclass Expression using the given Variables as context and return result.
+ /**
+ * Evaluate the subclass Expression and return result.
*
* Should only be called by subclasses, but can't be protected because they need to call
* this function on each other.
*/
- virtual Value evaluateInternal(Variables* vars) const = 0;
+ virtual Value evaluateInternal() const = 0;
/**
* Registers an Parser so it can be called from parseExpression.
@@ -323,13 +320,13 @@ public:
explicit ExpressionFromAccumulator(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionFromAccumulator<Accumulator>>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final {
+ Value evaluateInternal() const final {
Accumulator accum(this->getExpressionContext());
const size_t n = this->vpOperand.size();
// If a single array arg is given, loop through it passing each member to the accumulator.
// If a single, non-array arg is given, pass it directly to the accumulator.
if (n == 1) {
- Value singleVal = this->vpOperand[0]->evaluateInternal(vars);
+ Value singleVal = this->vpOperand[0]->evaluateInternal();
if (singleVal.getType() == Array) {
for (const Value& val : singleVal.getArray()) {
accum.process(val, false);
@@ -340,7 +337,7 @@ public:
} else {
// If multiple arguments are given, pass all arguments to the accumulator.
for (auto&& argument : this->vpOperand) {
- accum.process(argument->evaluateInternal(vars), false);
+ accum.process(argument->evaluateInternal(), false);
}
}
return accum.getValue(false);
@@ -375,8 +372,8 @@ public:
virtual ~ExpressionSingleNumericArg() {}
- Value evaluateInternal(Variables* vars) const final {
- Value arg = this->vpOperand[0]->evaluateInternal(vars);
+ Value evaluateInternal() const final {
+ Value arg = this->vpOperand[0]->evaluateInternal();
if (arg.nullish())
return Value(BSONNULL);
@@ -407,7 +404,7 @@ public:
explicit ExpressionAdd(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionAdd>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -425,7 +422,7 @@ public:
explicit ExpressionAllElementsTrue(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionAllElementsTrue, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -436,7 +433,7 @@ public:
: ExpressionVariadic<ExpressionAnd>(expCtx) {}
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -454,7 +451,7 @@ public:
explicit ExpressionAnyElementTrue(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionAnyElementTrue, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -464,7 +461,7 @@ public:
explicit ExpressionArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionArray>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
Value serialize(bool explain) const final;
const char* getOpName() const final;
};
@@ -475,7 +472,7 @@ public:
explicit ExpressionArrayElemAt(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionArrayElemAt, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -484,7 +481,7 @@ public:
explicit ExpressionObjectToArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionObjectToArray, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -493,7 +490,7 @@ public:
explicit ExpressionArrayToObject(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionArrayToObject, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -511,7 +508,7 @@ class ExpressionCoerceToBool final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
Value serialize(bool explain) const final;
static boost::intrusive_ptr<ExpressionCoerceToBool> create(
@@ -545,7 +542,7 @@ public:
ExpressionCompare(const boost::intrusive_ptr<ExpressionContext>& expCtx, CmpOp cmpOp)
: ExpressionFixedArity<ExpressionCompare, 2>(expCtx), cmpOp(cmpOp) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static boost::intrusive_ptr<Expression> parse(
@@ -570,7 +567,7 @@ public:
explicit ExpressionConcat(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionConcat>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -584,7 +581,7 @@ public:
explicit ExpressionConcatArrays(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionConcatArrays>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -597,7 +594,7 @@ class ExpressionCond final : public ExpressionFixedArity<ExpressionCond, 3> {
public:
explicit ExpressionCond(const boost::intrusive_ptr<ExpressionContext>& expCtx) : Base(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static boost::intrusive_ptr<Expression> parse(
@@ -614,7 +611,7 @@ class ExpressionConstant final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
Value serialize(bool explain) const final;
const char* getOpName() const;
@@ -646,7 +643,7 @@ class ExpressionDateToString final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
void addDependencies(DepsTracker* deps) const final;
static boost::intrusive_ptr<Expression> parse(
@@ -677,7 +674,7 @@ public:
explicit ExpressionDayOfMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionDayOfMonth, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static inline int extract(const tm& tm) {
@@ -691,7 +688,7 @@ public:
explicit ExpressionDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionDayOfWeek, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
// MySQL uses 1-7, tm uses 0-6
@@ -706,7 +703,7 @@ public:
explicit ExpressionDayOfYear(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionDayOfYear, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
// MySQL uses 1-366, tm uses 0-365
@@ -721,7 +718,7 @@ public:
explicit ExpressionDivide(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionDivide, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -740,7 +737,7 @@ class ExpressionFieldPath final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
Value serialize(bool explain) const final;
/*
@@ -805,7 +802,7 @@ class ExpressionFilter final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
void addDependencies(DepsTracker* deps) const final;
static boost::intrusive_ptr<Expression> parse(
@@ -846,7 +843,7 @@ public:
explicit ExpressionHour(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionHour, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static inline int extract(const tm& tm) {
@@ -860,7 +857,7 @@ public:
explicit ExpressionIfNull(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIfNull, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -870,7 +867,7 @@ public:
explicit ExpressionIn(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIn, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -880,7 +877,7 @@ public:
explicit ExpressionIndexOfArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionIndexOfArray, 2, 4>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -890,7 +887,7 @@ public:
explicit ExpressionIndexOfBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionIndexOfBytes, 2, 4>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -903,7 +900,7 @@ public:
explicit ExpressionIndexOfCP(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionIndexOfCP, 2, 4>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -912,7 +909,7 @@ class ExpressionLet final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
void addDependencies(DepsTracker* deps) const final;
static boost::intrusive_ptr<Expression> parse(
@@ -954,7 +951,7 @@ public:
explicit ExpressionLog(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionLog, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -971,7 +968,7 @@ class ExpressionMap final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
void addDependencies(DepsTracker* deps) const final;
static boost::intrusive_ptr<Expression> parse(
@@ -996,7 +993,7 @@ private:
class ExpressionMeta final : public Expression {
public:
Value serialize(bool explain) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
void addDependencies(DepsTracker* deps) const final;
static boost::intrusive_ptr<Expression> parse(
@@ -1020,7 +1017,7 @@ public:
explicit ExpressionMillisecond(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionMillisecond, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static int extract(const long long date);
@@ -1032,7 +1029,7 @@ public:
explicit ExpressionMinute(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionMinute, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static int extract(const tm& tm) {
@@ -1046,7 +1043,7 @@ public:
explicit ExpressionMod(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionMod, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1056,7 +1053,7 @@ public:
explicit ExpressionMultiply(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionMultiply>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1074,7 +1071,7 @@ public:
explicit ExpressionMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionMonth, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
// MySQL uses 1-12, tm uses 0-11
@@ -1089,7 +1086,7 @@ public:
explicit ExpressionNot(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionNot, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1106,7 +1103,7 @@ class ExpressionObject final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
Value serialize(bool explain) const final;
static boost::intrusive_ptr<ExpressionObject> create(
@@ -1146,7 +1143,7 @@ public:
: ExpressionVariadic<ExpressionOr>(expCtx) {}
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1167,7 +1164,7 @@ public:
const boost::intrusive_ptr<ExpressionContext>& expCtx, Value base, Value exp);
private:
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1177,7 +1174,7 @@ public:
explicit ExpressionRange(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionRange, 2, 3>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1188,7 +1185,7 @@ public:
: Expression(expCtx) {}
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1211,7 +1208,7 @@ public:
explicit ExpressionSecond(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSecond, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static inline int extract(const tm& tm) {
@@ -1225,7 +1222,7 @@ public:
explicit ExpressionSetDifference(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSetDifference, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1235,7 +1232,7 @@ public:
explicit ExpressionSetEquals(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionSetEquals>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
void validateArguments(const ExpressionVector& args) const final;
};
@@ -1246,7 +1243,7 @@ public:
explicit ExpressionSetIntersection(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionSetIntersection>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1266,7 +1263,7 @@ public:
: ExpressionFixedArity<ExpressionSetIsSubset, 2>(expCtx) {}
boost::intrusive_ptr<Expression> optimize() override;
- Value evaluateInternal(Variables* vars) const override;
+ Value evaluateInternal() const override;
const char* getOpName() const final;
private:
@@ -1279,7 +1276,7 @@ public:
explicit ExpressionSetUnion(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionSetUnion>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1297,7 +1294,7 @@ public:
explicit ExpressionSize(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSize, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1307,7 +1304,7 @@ public:
explicit ExpressionReverseArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionReverseArray, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1317,7 +1314,7 @@ public:
explicit ExpressionSlice(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionSlice, 2, 3>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1327,7 +1324,7 @@ public:
explicit ExpressionIsArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIsArray, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1337,7 +1334,7 @@ public:
explicit ExpressionSplit(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSplit, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1357,7 +1354,7 @@ public:
explicit ExpressionStrcasecmp(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionStrcasecmp, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1367,7 +1364,7 @@ public:
explicit ExpressionSubstrBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSubstrBytes, 3>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const;
};
@@ -1377,7 +1374,7 @@ public:
explicit ExpressionSubstrCP(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSubstrCP, 3>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1387,7 +1384,7 @@ public:
explicit ExpressionStrLenBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionStrLenBytes, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1397,7 +1394,7 @@ public:
explicit ExpressionStrLenCP(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionStrLenCP, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1407,7 +1404,7 @@ public:
explicit ExpressionSubtract(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSubtract, 2>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1418,7 +1415,7 @@ public:
: Expression(expCtx) {}
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1440,7 +1437,7 @@ public:
explicit ExpressionToLower(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionToLower, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1450,7 +1447,7 @@ public:
explicit ExpressionToUpper(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionToUpper, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1470,7 +1467,7 @@ public:
explicit ExpressionType(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionType, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
};
@@ -1480,7 +1477,7 @@ public:
explicit ExpressionWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionWeek, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static int extract(const tm& tm);
@@ -1492,7 +1489,7 @@ public:
explicit ExpressionIsoWeekYear(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIsoWeekYear, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static int extract(const tm& tm);
@@ -1504,7 +1501,7 @@ public:
explicit ExpressionIsoDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIsoDayOfWeek, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static int extract(const tm& tm);
@@ -1516,7 +1513,7 @@ public:
explicit ExpressionIsoWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIsoWeek, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
static int extract(const tm& tm);
@@ -1528,7 +1525,7 @@ public:
explicit ExpressionYear(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionYear, 1>(expCtx) {}
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
const char* getOpName() const final;
// tm_year is years since 1990
@@ -1544,7 +1541,7 @@ public:
: Expression(expCtx) {}
void addDependencies(DepsTracker* deps) const final;
- Value evaluateInternal(Variables* vars) const final;
+ Value evaluateInternal() const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
diff --git a/src/mongo/db/pipeline/expression_context.cpp b/src/mongo/db/pipeline/expression_context.cpp
index 2b149b1af9c..d2a16a6ca87 100644
--- a/src/mongo/db/pipeline/expression_context.cpp
+++ b/src/mongo/db/pipeline/expression_context.cpp
@@ -50,6 +50,7 @@ ExpressionContext::ExpressionContext(OperationContext* opCtx,
ns(request.getNamespaceString()),
opCtx(opCtx),
collation(request.getCollation()),
+ variablesParseState(variables.useIdGenerator()),
_collator(std::move(collator)),
_documentComparator(_collator.get()),
_valueComparator(_collator.get()),
diff --git a/src/mongo/db/pipeline/expression_context.h b/src/mongo/db/pipeline/expression_context.h
index 4d4c0b75f3a..1d0f04dd1a0 100644
--- a/src/mongo/db/pipeline/expression_context.h
+++ b/src/mongo/db/pipeline/expression_context.h
@@ -40,6 +40,7 @@
#include "mongo/db/pipeline/aggregation_request.h"
#include "mongo/db/pipeline/document_comparator.h"
#include "mongo/db/pipeline/value_comparator.h"
+#include "mongo/db/pipeline/variables.h"
#include "mongo/db/query/collation/collator_interface.h"
#include "mongo/db/query/explain_options.h"
#include "mongo/util/intrusive_counter.h"
@@ -117,13 +118,13 @@ public:
// collation.
BSONObj collation;
+ Variables variables;
+ VariablesParseState variablesParseState;
+
protected:
static const int kInterruptCheckPeriod = 128;
- /**
- * Should only be used by 'ExpressionContextForTest'.
- */
- ExpressionContext() = default;
+ ExpressionContext() : variablesParseState(variables.useIdGenerator()) {}
/**
* Sets '_collator' and resets '_documentComparator' and '_valueComparator'.
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index 4e35f76931d..a470873058a 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -60,8 +60,7 @@ using std::list;
static Value evaluateExpression(const string& expressionName,
const vector<ImplicitValue>& operands) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
const BSONObj obj = BSON(expressionName << ImplicitValue::convertToValue(operands));
auto expression = Expression::parseExpression(expCtx, obj, vps);
Value result = expression->evaluate(Document());
@@ -145,13 +144,15 @@ Value valueFromBson(BSONObj obj) {
template <typename T>
intrusive_ptr<ExpressionConstant> makeConstant(T&& val) {
- return ExpressionConstant::create(nullptr, Value(std::forward<T>(val)));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ return ExpressionConstant::create(expCtx, Value(std::forward<T>(val)));
}
class ExpressionBaseTest : public unittest::Test {
public:
void addOperand(intrusive_ptr<ExpressionNary> expr, Value arg) {
- expr->addOperand(ExpressionConstant::create(nullptr, arg));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ expr->addOperand(ExpressionConstant::create(expCtx, arg));
}
};
@@ -171,13 +172,13 @@ public:
/** A dummy child of ExpressionNary used for testing. */
class Testable : public ExpressionNary {
public:
- virtual Value evaluateInternal(Variables* vars) const {
+ virtual Value evaluateInternal() const {
// Just put all the values in a list.
// By default, this is not associative/commutative so the results will change if
// instantiated as commutative or associative and operations are reordered.
vector<Value> values;
for (ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end(); ++i) {
- values.push_back((*i)->evaluateInternal(vars));
+ values.push_back((*i)->evaluateInternal());
}
return Value(values);
}
@@ -238,8 +239,7 @@ protected:
void addOperandArrayToExpr(const intrusive_ptr<Testable>& expr, const BSONArray& operands) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
BSONObjIterator i(operands);
while (i.more()) {
BSONElement element = i.next();
@@ -253,12 +253,14 @@ protected:
};
TEST_F(ExpressionNaryTest, AddedConstantOperandIsSerialized) {
- _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(9)));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(9)));
assertContents(_notAssociativeNorCommutative, BSON_ARRAY(9));
}
TEST_F(ExpressionNaryTest, AddedFieldPathOperandIsSerialized) {
- _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(nullptr, "ab.c"));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(expCtx, "ab.c"));
assertContents(_notAssociativeNorCommutative, BSON_ARRAY("$ab.c"));
}
@@ -267,12 +269,14 @@ TEST_F(ExpressionNaryTest, ValidateEmptyDependencies) {
}
TEST_F(ExpressionNaryTest, ValidateConstantExpressionDependency) {
- _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(1)));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(1)));
assertDependencies(_notAssociativeNorCommutative, BSONArray());
}
TEST_F(ExpressionNaryTest, ValidateFieldPathExpressionDependency) {
- _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(nullptr, "ab.c"));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(expCtx, "ab.c"));
assertDependencies(_notAssociativeNorCommutative, BSON_ARRAY("ab.c"));
}
@@ -283,8 +287,7 @@ TEST_F(ExpressionNaryTest, ValidateObjectExpressionDependency) {
<< "$r"));
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONElement specElement = spec.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
_notAssociativeNorCommutative->addOperand(
Expression::parseObject(expCtx, specElement.Obj(), vps));
assertDependencies(_notAssociativeNorCommutative,
@@ -293,13 +296,15 @@ TEST_F(ExpressionNaryTest, ValidateObjectExpressionDependency) {
}
TEST_F(ExpressionNaryTest, SerializationToBsonObj) {
- _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(5)));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(5)));
ASSERT_BSONOBJ_EQ(BSON("foo" << BSON("$testable" << BSON_ARRAY(BSON("$const" << 5)))),
BSON("foo" << _notAssociativeNorCommutative->serialize(false)));
}
TEST_F(ExpressionNaryTest, SerializationToBsonArr) {
- _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(5)));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(5)));
ASSERT_BSONOBJ_EQ(constify(BSON_ARRAY(BSON("$testable" << BSON_ARRAY(5)))),
BSON_ARRAY(_notAssociativeNorCommutative->serialize(false)));
}
@@ -954,7 +959,7 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
- expression->addOperand(ExpressionConstant::create(nullptr, Value(2)));
+ expression->addOperand(ExpressionConstant::create(expCtx, Value(2)));
ASSERT_BSONOBJ_EQ(BSON("" << 2), toBson(expression->evaluate(Document())));
}
};
@@ -973,7 +978,7 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
- expression->addOperand(ExpressionConstant::create(nullptr, Value("a"_sd)));
+ expression->addOperand(ExpressionConstant::create(expCtx, Value("a"_sd)));
ASSERT_THROWS(expression->evaluate(Document()), UserException);
}
};
@@ -984,14 +989,15 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
- expression->addOperand(ExpressionConstant::create(nullptr, Value(true)));
+ expression->addOperand(ExpressionConstant::create(expCtx, Value(true)));
ASSERT_THROWS(expression->evaluate(Document()), UserException);
}
};
class SingleOperandBase : public ExpectedResultBase {
void populateOperands(intrusive_ptr<ExpressionNary>& expression) {
- expression->addOperand(ExpressionConstant::create(nullptr, valueFromBson(operand())));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ expression->addOperand(ExpressionConstant::create(expCtx, valueFromBson(operand())));
}
BSONObj expectedResult() {
return operand();
@@ -1061,10 +1067,11 @@ public:
protected:
void populateOperands(intrusive_ptr<ExpressionNary>& expression) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
expression->addOperand(
- ExpressionConstant::create(nullptr, valueFromBson(_reverse ? operand2() : operand1())));
+ ExpressionConstant::create(expCtx, valueFromBson(_reverse ? operand2() : operand1())));
expression->addOperand(
- ExpressionConstant::create(nullptr, valueFromBson(_reverse ? operand1() : operand2())));
+ ExpressionConstant::create(expCtx, valueFromBson(_reverse ? operand1() : operand2())));
}
virtual BSONObj operand1() = 0;
virtual BSONObj operand2() = 0;
@@ -1219,8 +1226,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
@@ -1242,8 +1248,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
intrusive_ptr<Expression> optimized = expression->optimize();
@@ -1516,8 +1521,9 @@ namespace CoerceToBool {
class EvaluateTrue {
public:
void run() {
- intrusive_ptr<Expression> nested = ExpressionConstant::create(nullptr, Value(5));
- intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(nullptr, nested);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> nested = ExpressionConstant::create(expCtx, Value(5));
+ intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested);
ASSERT(expression->evaluate(Document()).getBool());
}
};
@@ -1526,8 +1532,9 @@ public:
class EvaluateFalse {
public:
void run() {
- intrusive_ptr<Expression> nested = ExpressionConstant::create(nullptr, Value(0));
- intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(nullptr, nested);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> nested = ExpressionConstant::create(expCtx, Value(0));
+ intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested);
ASSERT(!expression->evaluate(Document()).getBool());
}
};
@@ -1536,8 +1543,9 @@ public:
class Dependencies {
public:
void run() {
- intrusive_ptr<Expression> nested = ExpressionFieldPath::create(nullptr, "a.b");
- intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(nullptr, nested);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> nested = ExpressionFieldPath::create(expCtx, "a.b");
+ intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested);
DepsTracker dependencies;
expression->addDependencies(&dependencies);
ASSERT_EQUALS(1U, dependencies.fields.size());
@@ -1551,8 +1559,9 @@ public:
class AddToBsonObj {
public:
void run() {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression =
- ExpressionCoerceToBool::create(nullptr, ExpressionFieldPath::create(nullptr, "foo"));
+ ExpressionCoerceToBool::create(expCtx, ExpressionFieldPath::create(expCtx, "foo"));
// serialized as $and because CoerceToBool isn't an ExpressionNary
assertBinaryEqual(fromjson("{field:{$and:['$foo']}}"), toBsonObj(expression));
@@ -1568,8 +1577,9 @@ private:
class AddToBsonArray {
public:
void run() {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression =
- ExpressionCoerceToBool::create(nullptr, ExpressionFieldPath::create(nullptr, "foo"));
+ ExpressionCoerceToBool::create(expCtx, ExpressionFieldPath::create(expCtx, "foo"));
// serialized as $and because CoerceToBool isn't an ExpressionNary
assertBinaryEqual(BSON_ARRAY(fromjson("{$and:['$foo']}")), toBsonArray(expression));
@@ -1596,9 +1606,8 @@ public:
void run() {
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
intrusive_ptr<Expression> optimized = expression->optimize();
ASSERT_BSONOBJ_EQ(constify(expectedOptimized()), expressionToBson(optimized));
@@ -1628,9 +1637,8 @@ public:
OptimizeBase::run();
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
// Check expression spec round trip.
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
@@ -1670,8 +1678,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(Expression::parseOperand(expCtx, specElement, vps), UserException);
}
@@ -1873,9 +1880,8 @@ public:
void run() {
BSONObj specObject = BSON("" << BSON("$ne" << BSON_ARRAY("a" << 1)));
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_VALUE_EQ(expression->evaluate(Document()), Value(true));
}
@@ -2008,7 +2014,8 @@ namespace Constant {
class Create {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5));
assertBinaryEqual(BSON("" << 5), toBson(expression->evaluate(Document())));
}
};
@@ -2021,8 +2028,7 @@ public:
<< "foo");
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONElement specElement = spec.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = ExpressionConstant::parse(expCtx, specElement, vps);
assertBinaryEqual(BSON(""
<< "foo"),
@@ -2034,7 +2040,8 @@ public:
class Optimize {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5));
// An attempt to optimize returns the Expression itself.
ASSERT_EQUALS(expression, expression->optimize());
}
@@ -2044,7 +2051,8 @@ public:
class Dependencies {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5));
DepsTracker dependencies;
expression->addDependencies(&dependencies);
ASSERT_EQUALS(0U, dependencies.fields.size());
@@ -2057,7 +2065,8 @@ public:
class AddToBsonObj {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5));
// The constant is replaced with a $ expression.
assertBinaryEqual(BSON("field" << BSON("$const" << 5)), toBsonObj(expression));
}
@@ -2072,7 +2081,8 @@ private:
class AddToBsonArray {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5));
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5));
// The constant is copied out as is.
assertBinaryEqual(constify(BSON_ARRAY(5)), toBsonArray(expression));
}
@@ -2086,12 +2096,14 @@ private:
};
TEST(ExpressionConstantTest, ConstantOfValueMissingRemovesField) {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value());
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value());
assertBinaryEqual(BSONObj(), toBson(expression->evaluate(Document{{"foo", Value("bar"_sd)}})));
}
TEST(ExpressionConstantTest, ConstantOfValueMissingSerializesToRemoveSystemVar) {
- intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value());
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value());
assertBinaryEqual(BSON("field"
<< "$$REMOVE"),
BSON("field" << expression->serialize(false)));
@@ -2172,7 +2184,8 @@ namespace FieldPath {
class Invalid {
public:
void run() {
- ASSERT_THROWS(ExpressionFieldPath::create(nullptr, ""), UserException);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ASSERT_THROWS(ExpressionFieldPath::create(expCtx, ""), UserException);
}
};
@@ -2180,7 +2193,8 @@ public:
class Optimize {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a");
// An attempt to optimize returns the Expression itself.
ASSERT_EQUALS(expression, expression->optimize());
}
@@ -2190,7 +2204,8 @@ public:
class Dependencies {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
DepsTracker dependencies;
expression->addDependencies(&dependencies);
ASSERT_EQUALS(1U, dependencies.fields.size());
@@ -2204,7 +2219,8 @@ public:
class Missing {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a");
assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(Document())));
}
};
@@ -2213,7 +2229,8 @@ public:
class Present {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a");
assertBinaryEqual(fromjson("{'':123}"),
toBson(expression->evaluate(fromBson(BSON("a" << 123)))));
}
@@ -2223,7 +2240,8 @@ public:
class NestedBelowNull {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{}"),
toBson(expression->evaluate(fromBson(fromjson("{a:null}")))));
}
@@ -2233,7 +2251,8 @@ public:
class NestedBelowUndefined {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{}"),
toBson(expression->evaluate(fromBson(fromjson("{a:undefined}")))));
}
@@ -2243,7 +2262,8 @@ public:
class NestedBelowMissing {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{}"),
toBson(expression->evaluate(fromBson(fromjson("{z:1}")))));
}
@@ -2253,7 +2273,8 @@ public:
class NestedBelowInt {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(BSON("a" << 2)))));
}
};
@@ -2262,7 +2283,8 @@ public:
class NestedValue {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(BSON("" << 55),
toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55))))));
}
@@ -2272,7 +2294,8 @@ public:
class NestedBelowEmptyObject {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{}"),
toBson(expression->evaluate(fromBson(BSON("a" << BSONObj())))));
}
@@ -2282,7 +2305,8 @@ public:
class NestedBelowEmptyArray {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(BSON("" << BSONArray()),
toBson(expression->evaluate(fromBson(BSON("a" << BSONArray())))));
}
@@ -2292,7 +2316,8 @@ public:
class NestedBelowArrayWithNull {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[null]}")))));
}
@@ -2302,7 +2327,8 @@ public:
class NestedBelowArrayWithUndefined {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}")))));
}
@@ -2312,7 +2338,8 @@ public:
class NestedBelowArrayWithInt {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[1]}")))));
}
@@ -2322,7 +2349,8 @@ public:
class NestedWithinArray {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[9]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}")))));
}
@@ -2332,7 +2360,8 @@ public:
class MultipleArrayValues {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[9,20]}"),
toBson(expression->evaluate(
fromBson(fromjson("{a:[{b:9},null,undefined,{g:4},{b:20},{}]}")))));
@@ -2343,7 +2372,8 @@ public:
class ExpandNestedArrays {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b.c");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b.c");
assertBinaryEqual(fromjson("{'':[[1,2],3,[4],[[5]],[6,7]]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[{b:[{c:1},{c:2}]},"
"{b:{c:3}},"
@@ -2357,7 +2387,8 @@ public:
class AddToBsonObj {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b.c");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b.c");
assertBinaryEqual(BSON("foo"
<< "$a.b.c"),
BSON("foo" << expression->serialize(false)));
@@ -2368,7 +2399,8 @@ public:
class AddToBsonArray {
public:
void run() {
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b.c");
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b.c");
BSONArrayBuilder bab;
bab << expression->serialize(false);
assertBinaryEqual(BSON_ARRAY("$a.b.c"), bab.arr());
@@ -2391,16 +2423,14 @@ Document literal(T&& value) {
TEST(ExpressionObjectParse, ShouldAcceptEmptyObject) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object = ExpressionObject::parse(expCtx, BSONObj(), vps);
ASSERT_VALUE_EQ(Value(Document{}), object->serialize(false));
}
TEST(ExpressionObjectParse, ShouldAcceptLiteralsAsValues) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object = ExpressionObject::parse(expCtx,
BSON("a" << 5 << "b"
<< "string"
@@ -2414,8 +2444,7 @@ TEST(ExpressionObjectParse, ShouldAcceptLiteralsAsValues) {
TEST(ExpressionObjectParse, ShouldAccept_idAsFieldName) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object = ExpressionObject::parse(expCtx, BSON("_id" << 5), vps);
auto expectedResult = Value(Document{{"_id", literal(5)}});
ASSERT_VALUE_EQ(expectedResult, object->serialize(false));
@@ -2423,8 +2452,7 @@ TEST(ExpressionObjectParse, ShouldAccept_idAsFieldName) {
TEST(ExpressionObjectParse, ShouldAcceptFieldNameContainingDollar) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object = ExpressionObject::parse(expCtx, BSON("a$b" << 5), vps);
auto expectedResult = Value(Document{{"a$b", literal(5)}});
ASSERT_VALUE_EQ(expectedResult, object->serialize(false));
@@ -2432,8 +2460,7 @@ TEST(ExpressionObjectParse, ShouldAcceptFieldNameContainingDollar) {
TEST(ExpressionObjectParse, ShouldAcceptNestedObjects) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object =
ExpressionObject::parse(expCtx, fromjson("{a: {b: 1}, c: {d: {e: 1, f: 1}}}"), vps);
auto expectedResult =
@@ -2444,8 +2471,7 @@ TEST(ExpressionObjectParse, ShouldAcceptNestedObjects) {
TEST(ExpressionObjectParse, ShouldAcceptArrays) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object = ExpressionObject::parse(expCtx, fromjson("{a: [1, 2]}"), vps);
auto expectedResult =
Value(Document{{"a", vector<Value>{Value(literal(1)), Value(literal(2))}}});
@@ -2454,8 +2480,7 @@ TEST(ExpressionObjectParse, ShouldAcceptArrays) {
TEST(ObjectParsing, ShouldAcceptExpressionAsValue) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto object = ExpressionObject::parse(expCtx, BSON("a" << BSON("$and" << BSONArray())), vps);
ASSERT_VALUE_EQ(object->serialize(false),
Value(Document{{"a", Document{{"$and", BSONArray()}}}}));
@@ -2467,8 +2492,7 @@ TEST(ObjectParsing, ShouldAcceptExpressionAsValue) {
TEST(ExpressionObjectParse, ShouldRejectDottedFieldNames) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a.b" << 1), vps), UserException);
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("c" << 3 << "a.b" << 1), vps),
UserException);
@@ -2478,8 +2502,7 @@ TEST(ExpressionObjectParse, ShouldRejectDottedFieldNames) {
TEST(ExpressionObjectParse, ShouldRejectDuplicateFieldNames) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a" << 1 << "a" << 1), vps), UserException);
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a" << 1 << "b" << 2 << "a" << 1), vps),
UserException);
@@ -2493,8 +2516,7 @@ TEST(ExpressionObjectParse, ShouldRejectDuplicateFieldNames) {
TEST(ExpressionObjectParse, ShouldRejectInvalidFieldName) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("$a" << 1), vps), UserException);
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("" << 1), vps), UserException);
ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON(std::string("a\0b", 3) << 1), vps),
@@ -2503,8 +2525,7 @@ TEST(ExpressionObjectParse, ShouldRejectInvalidFieldName) {
TEST(ExpressionObjectParse, ShouldRejectInvalidFieldPathAsValue) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(ExpressionObject::parse(expCtx,
BSON("a"
<< "$field."),
@@ -2514,8 +2535,7 @@ TEST(ExpressionObjectParse, ShouldRejectInvalidFieldPathAsValue) {
TEST(ParseObject, ShouldRejectExpressionAsTheSecondField) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(
ExpressionObject::parse(
expCtx, BSON("a" << BSON("$and" << BSONArray()) << "$or" << BSONArray()), vps),
@@ -2617,8 +2637,7 @@ TEST(ExpressionObjectDependencies, FieldPathsShouldBeAddedToDependencies) {
TEST(ExpressionObjectOptimizations, OptimizingAnObjectShouldOptimizeSubExpressions) {
// Build up the object {a: {$add: [1, 2]}}.
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGen;
- VariablesParseState vps(&idGen);
+ VariablesParseState vps = expCtx->variablesParseState;
auto addExpression =
ExpressionAdd::parse(expCtx, BSON("$add" << BSON_ARRAY(1 << 2)).firstElement(), vps);
auto object = ExpressionObject::create(expCtx, {{"a", addExpression}});
@@ -2647,8 +2666,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
@@ -2670,8 +2688,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObject = BSON("" << spec());
BSONElement specElement = specObject.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
intrusive_ptr<Expression> optimized = expression->optimize();
@@ -2948,8 +2965,7 @@ namespace Object {
*/
boost::intrusive_ptr<Expression> parseObject(BSONObj specification) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
return Expression::parseObject(expCtx, specification, vps);
};
@@ -2983,8 +2999,7 @@ using mongo::Expression;
*/
boost::intrusive_ptr<Expression> parseExpression(BSONObj specification) {
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
return Expression::parseExpression(expCtx, specification, vps);
}
@@ -3091,8 +3106,7 @@ using mongo::Expression;
intrusive_ptr<Expression> parseOperand(BSONObj specification) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONElement specElement = specification.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
return Expression::parseOperand(expCtx, specElement, vps);
}
@@ -3164,8 +3178,7 @@ public:
const Document::FieldPair field(fields.next());
const Value expected = field.second;
const BSONObj obj = BSON(field.first << args);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
const intrusive_ptr<Expression> expr =
Expression::parseExpression(expCtx, obj, vps);
Value result = expr->evaluate(Document());
@@ -3188,8 +3201,7 @@ public:
size_t n = asserters.size();
for (size_t i = 0; i < n; i++) {
const BSONObj obj = BSON(asserters[i].getString() << args);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(
{
// NOTE: parse and evaluatation failures are treated the
@@ -3475,8 +3487,7 @@ private:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObj = BSON("" << spec);
BSONElement specElement = specObj.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec), expressionToBson(expression));
ASSERT_BSONOBJ_EQ(BSON("" << expectedResult), toBson(expression->evaluate(Document())));
@@ -3602,8 +3613,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObj = BSON("" << spec());
BSONElement specElement = specObj.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document())));
@@ -3707,8 +3717,7 @@ namespace SubstrCP {
TEST(ExpressionSubstrCPTest, DoesThrowWithBadContinuationByte) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
const auto continuationByte = "\x80\x00"_sd;
const auto expr = Expression::parseExpression(
@@ -3718,8 +3727,7 @@ TEST(ExpressionSubstrCPTest, DoesThrowWithBadContinuationByte) {
TEST(ExpressionSubstrCPTest, DoesThrowWithInvalidLeadingByte) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
const auto leadingByte = "\xFF\x00"_sd;
const auto expr = Expression::parseExpression(
@@ -3871,27 +3879,27 @@ TEST(BuiltinRemoveVariableTest, LiteralEscapesRemoveVar) {
}
TEST(BuiltinRemoveVariableTest, RemoveSerializesCorrectly) {
- VariablesIdGenerator idGenerator{};
- VariablesParseState vps{&idGenerator};
- auto expression = ExpressionFieldPath::parse(nullptr, "$$REMOVE", vps);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ VariablesParseState vps = expCtx->variablesParseState;
+ auto expression = ExpressionFieldPath::parse(expCtx, "$$REMOVE", vps);
ASSERT_BSONOBJ_EQ(BSON("foo"
<< "$$REMOVE"),
BSON("foo" << expression->serialize(false)));
}
TEST(BuiltinRemoveVariableTest, RemoveSerializesCorrectlyWithTrailingPath) {
- VariablesIdGenerator idGenerator{};
- VariablesParseState vps{&idGenerator};
- auto expression = ExpressionFieldPath::parse(nullptr, "$$REMOVE.a.b", vps);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ VariablesParseState vps = expCtx->variablesParseState;
+ auto expression = ExpressionFieldPath::parse(expCtx, "$$REMOVE.a.b", vps);
ASSERT_BSONOBJ_EQ(BSON("foo"
<< "$$REMOVE.a.b"),
BSON("foo" << expression->serialize(false)));
}
TEST(BuiltinRemoveVariableTest, RemoveSerializesCorrectlyAfterOptimization) {
- VariablesIdGenerator idGenerator{};
- VariablesParseState vps{&idGenerator};
- auto expression = ExpressionFieldPath::parse(nullptr, "$$REMOVE.a.b", vps);
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ VariablesParseState vps = expCtx->variablesParseState;
+ auto expression = ExpressionFieldPath::parse(expCtx, "$$REMOVE.a.b", vps);
auto optimizedExpression = expression->optimize();
ASSERT(dynamic_cast<ExpressionConstant*>(optimizedExpression.get()));
ASSERT_BSONOBJ_EQ(BSON("foo"
@@ -3985,8 +3993,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObj = BSON("" << spec());
BSONElement specElement = specObj.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document())));
@@ -4043,8 +4050,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
BSONObj specObj = BSON("" << spec());
BSONElement specElement = specObj.firstElement();
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document())));
@@ -4106,8 +4112,7 @@ public:
const Document::FieldPair field(fields.next());
const Value expected = field.second;
const BSONObj obj = BSON(field.first << args);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
const intrusive_ptr<Expression> expr =
Expression::parseExpression(expCtx, obj, vps);
const Value result = expr->evaluate(Document());
@@ -4127,8 +4132,7 @@ public:
size_t n = asserters.size();
for (size_t i = 0; i < n; i++) {
const BSONObj obj = BSON(asserters[i].getString() << args);
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
+ VariablesParseState vps = expCtx->variablesParseState;
ASSERT_THROWS(
{
// NOTE: parse and evaluatation failures are treated the
diff --git a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp
index 078936e8145..68182291d3a 100644
--- a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp
+++ b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp
@@ -80,8 +80,7 @@ Value GranularityRounderPowersOfTwo::roundUp(Value value) {
exp = Value(63 - countLeadingZeros64(number) + 1);
}
- Variables vars;
- return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(&vars);
+ return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate();
}
Value GranularityRounderPowersOfTwo::roundDown(Value value) {
@@ -113,8 +112,7 @@ Value GranularityRounderPowersOfTwo::roundDown(Value value) {
}
}
- Variables vars;
- return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(&vars);
+ return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate();
}
string GranularityRounderPowersOfTwo::getName() {
diff --git a/src/mongo/db/pipeline/parsed_add_fields.cpp b/src/mongo/db/pipeline/parsed_add_fields.cpp
index ae78664a8a5..18b13c6aa61 100644
--- a/src/mongo/db/pipeline/parsed_add_fields.cpp
+++ b/src/mongo/db/pipeline/parsed_add_fields.cpp
@@ -46,22 +46,20 @@ std::unique_ptr<ParsedAddFields> ParsedAddFields::create(
uasserted(status.location(),
str::stream() << "Invalid $addFields specification: " << status.reason());
}
- std::unique_ptr<ParsedAddFields> parsedAddFields = stdx::make_unique<ParsedAddFields>();
+ std::unique_ptr<ParsedAddFields> parsedAddFields = stdx::make_unique<ParsedAddFields>(expCtx);
// Actually parse the specification.
- parsedAddFields->parse(expCtx, spec);
+ parsedAddFields->parse(spec);
return parsedAddFields;
}
-void ParsedAddFields::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec,
- const VariablesParseState& variablesParseState) {
+void ParsedAddFields::parse(const BSONObj& spec) {
for (auto elem : spec) {
auto fieldName = elem.fieldNameStringData();
if (elem.type() == BSONType::Object) {
// This is either an expression, or a nested specification.
- if (parseObjectAsExpression(expCtx, fieldName, elem.Obj(), variablesParseState)) {
+ if (parseObjectAsExpression(fieldName, elem.Obj(), _expCtx->variablesParseState)) {
// It was an expression.
} else {
// The field name might be a dotted path. If so, we need to keep adding children
@@ -75,12 +73,13 @@ void ParsedAddFields::parse(const boost::intrusive_ptr<ExpressionContext>& expCt
// It is illegal to construct an empty FieldPath, so the above loop ends one
// iteration too soon. Add the last path here.
child = child->addOrGetChild(remainingPath.fullPath());
- parseSubObject(expCtx, elem.Obj(), variablesParseState, child);
+ parseSubObject(elem.Obj(), _expCtx->variablesParseState, child);
}
} else {
// This is a literal or regular value.
- _root->addComputedField(FieldPath(elem.fieldName()),
- Expression::parseOperand(expCtx, elem, variablesParseState));
+ _root->addComputedField(
+ FieldPath(elem.fieldName()),
+ Expression::parseOperand(_expCtx, elem, _expCtx->variablesParseState));
}
}
}
@@ -92,15 +91,14 @@ Document ParsedAddFields::applyProjection(Document inputDoc, Variables* vars) co
// The output doc is the same as the input doc, with the added fields.
MutableDocument output(inputDoc);
- _root->addComputedFields(&output, vars);
+ _root->addComputedFields(&output);
// Pass through the metadata.
output.copyMetaDataFrom(inputDoc);
return output.freeze();
}
-bool ParsedAddFields::parseObjectAsExpression(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- StringData pathToObject,
+bool ParsedAddFields::parseObjectAsExpression(StringData pathToObject,
const BSONObj& objSpec,
const VariablesParseState& variablesParseState) {
if (objSpec.firstElementFieldName()[0] == '$') {
@@ -108,14 +106,13 @@ bool ParsedAddFields::parseObjectAsExpression(const boost::intrusive_ptr<Express
// field.
invariant(objSpec.nFields() == 1);
_root->addComputedField(pathToObject,
- Expression::parseExpression(expCtx, objSpec, variablesParseState));
+ Expression::parseExpression(_expCtx, objSpec, variablesParseState));
return true;
}
return false;
}
-void ParsedAddFields::parseSubObject(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& subObj,
+void ParsedAddFields::parseSubObject(const BSONObj& subObj,
const VariablesParseState& variablesParseState,
InclusionNode* node) {
for (auto&& elem : subObj) {
@@ -128,18 +125,17 @@ void ParsedAddFields::parseSubObject(const boost::intrusive_ptr<ExpressionContex
// This is either an expression, or a nested specification.
auto fieldName = elem.fieldNameStringData().toString();
if (!parseObjectAsExpression(
- expCtx,
FieldPath::getFullyQualifiedPath(node->getPath(), fieldName),
elem.Obj(),
variablesParseState)) {
// It was a nested subobject
auto child = node->addOrGetChild(fieldName);
- parseSubObject(expCtx, elem.Obj(), variablesParseState, child);
+ parseSubObject(elem.Obj(), variablesParseState, child);
}
} else {
// This is a literal or regular value.
node->addComputedField(FieldPath(elem.fieldName()),
- Expression::parseOperand(expCtx, elem, variablesParseState));
+ Expression::parseOperand(_expCtx, elem, variablesParseState));
}
}
}
diff --git a/src/mongo/db/pipeline/parsed_add_fields.h b/src/mongo/db/pipeline/parsed_add_fields.h
index f4c1b535f63..261dbb903aa 100644
--- a/src/mongo/db/pipeline/parsed_add_fields.h
+++ b/src/mongo/db/pipeline/parsed_add_fields.h
@@ -51,7 +51,8 @@ namespace parsed_aggregation_projection {
*/
class ParsedAddFields : public ParsedAggregationProjection {
public:
- ParsedAddFields() : ParsedAggregationProjection(), _root(new InclusionNode()) {}
+ ParsedAddFields(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : ParsedAggregationProjection(expCtx), _root(new InclusionNode()) {}
/**
* Creates the data needed to perform an AddFields.
@@ -68,12 +69,7 @@ public:
/**
* Parses the addFields specification given by 'spec', populating internal data structures.
*/
- void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec) final {
- VariablesIdGenerator idGenerator;
- VariablesParseState variablesParseState(&idGenerator);
- parse(expCtx, spec, variablesParseState);
- _variables = stdx::make_unique<Variables>(idGenerator.getIdCount());
- }
+ void parse(const BSONObj& spec) final;
Document serialize(boost::optional<ExplainOptions::Verbosity> explain) const final {
MutableDocument output;
@@ -115,21 +111,15 @@ public:
* with {"0": "hello"}. See SERVER-25200 for more details.
*/
Document applyProjection(Document inputDoc) const final {
- _variables->setRoot(inputDoc);
- return applyProjection(inputDoc, _variables.get());
+ auto& variables = _expCtx->variables;
+ variables.setRoot(inputDoc);
+ return applyProjection(inputDoc, &variables);
}
Document applyProjection(Document inputDoc, Variables* vars) const;
private:
/**
- * Parses 'spec' to determine which fields to add.
- */
- void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec,
- const VariablesParseState& variablesParseState);
-
- /**
* Attempts to parse 'objSpec' as an expression like {$add: [...]}. Adds a computed field to
* '_root' and returns true if it was successfully parsed as an expression. Returns false if it
* was not an expression specification.
@@ -137,8 +127,7 @@ private:
* Throws an error if it was determined to be an expression specification, but failed to parse
* as a valid expression.
*/
- bool parseObjectAsExpression(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- StringData pathToObject,
+ bool parseObjectAsExpression(StringData pathToObject,
const BSONObj& objSpec,
const VariablesParseState& variablesParseState);
@@ -146,17 +135,12 @@ private:
* Traverses 'subObj' and parses each field. Adds any computed fields at this level
* to 'node'.
*/
- void parseSubObject(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& subObj,
+ void parseSubObject(const BSONObj& subObj,
const VariablesParseState& variablesParseState,
InclusionNode* node);
// The InclusionNode tree does most of the execution work once constructed.
std::unique_ptr<InclusionNode> _root;
-
- // This is needed to give the expressions knowledge about the context in which they are being
- // executed.
- std::unique_ptr<Variables> _variables;
};
} // namespace parsed_aggregation_projection
} // namespace mongo
diff --git a/src/mongo/db/pipeline/parsed_add_fields_test.cpp b/src/mongo/db/pipeline/parsed_add_fields_test.cpp
index c9adc2b1a36..9a39b54ae8a 100644
--- a/src/mongo/db/pipeline/parsed_add_fields_test.cpp
+++ b/src/mongo/db/pipeline/parsed_add_fields_test.cpp
@@ -118,8 +118,8 @@ TEST(ParsedAddFields, DoesNotErrorOnTwoNestedFields) {
// Verify that replaced fields are not included as dependencies.
TEST(ParsedAddFieldsDeps, RemovesReplaceFieldsFromDependencies) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a" << true));
DepsTracker deps;
addition.addDependencies(&deps);
@@ -132,8 +132,8 @@ TEST(ParsedAddFieldsDeps, RemovesReplaceFieldsFromDependencies) {
// Verify that adding nested fields keeps the top-level field as a dependency.
TEST(ParsedAddFieldsDeps, IncludesTopLevelFieldInDependenciesWhenAddingNestedFields) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("x.y" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("x.y" << true));
DepsTracker deps;
addition.addDependencies(&deps);
@@ -147,9 +147,8 @@ TEST(ParsedAddFieldsDeps, IncludesTopLevelFieldInDependenciesWhenAddingNestedFie
// Verify that fields that an expression depends on are added to the dependencies.
TEST(ParsedAddFieldsDeps, AddsDependenciesForComputedFields) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("x.y"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("x.y"
<< "$z"
<< "a"
<< "$b"));
@@ -169,8 +168,8 @@ TEST(ParsedAddFieldsDeps, AddsDependenciesForComputedFields) {
// their corresponding $const form.
TEST(ParsedAddFieldsSerialize, SerializesToCorrectForm) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}"));
+ ParsedAddFields addition(expCtx);
+ addition.parse(fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}"));
auto expectedSerialization = Document(
fromjson("{a: {$add: [\"$a\", {$const: 2}]}, b: {d: {$const: 3}}, x: {y: {$const: 4}}}"));
@@ -188,8 +187,8 @@ TEST(ParsedAddFieldsSerialize, SerializesToCorrectForm) {
// Verify that serialize treats the _id field as any other field: including when explicity included.
TEST(ParsedAddFieldsSerialize, AddsIdToSerializeWhenExplicitlyIncluded) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("_id" << false));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("_id" << false));
// Adds explicit "_id" setting field, serializes expressions.
auto expectedSerialization = Document(fromjson("{_id: {$const: false}}"));
@@ -210,8 +209,8 @@ TEST(ParsedAddFieldsSerialize, AddsIdToSerializeWhenExplicitlyIncluded) {
// fail.
TEST(ParsedAddFieldsSerialize, OmitsIdFromSerializeWhenNotIncluded) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a" << true));
// Does not implicitly include "_id" field.
auto expectedSerialization = Document(fromjson("{a: {$const: true}}"));
@@ -229,8 +228,8 @@ TEST(ParsedAddFieldsSerialize, OmitsIdFromSerializeWhenNotIncluded) {
// Verify that the $addFields stage optimizes expressions into simpler forms when possible.
TEST(ParsedAddFieldsOptimize, OptimizesTopLevelExpressions) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2))));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2))));
addition.optimize();
auto expectedSerialization = Document{{"a", Document{{"$const", 3}}}};
@@ -247,8 +246,8 @@ TEST(ParsedAddFieldsOptimize, OptimizesTopLevelExpressions) {
// Verify that the $addFields stage optimizes expressions even when they are nested.
TEST(ParsedAddFieldsOptimize, ShouldOptimizeNestedExpressions) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2))));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2))));
addition.optimize();
auto expectedSerialization = Document{{"a", Document{{"b", Document{{"$const", 3}}}}}};
@@ -269,8 +268,8 @@ TEST(ParsedAddFieldsOptimize, ShouldOptimizeNestedExpressions) {
// Verify that a new field is added to the end of the document.
TEST(ParsedAddFieldsExecutionTest, AddsNewFieldToEndOfDocument) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("c" << 3));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("c" << 3));
// There are no fields in the document.
auto result = addition.applyProjection(Document{});
@@ -286,8 +285,8 @@ TEST(ParsedAddFieldsExecutionTest, AddsNewFieldToEndOfDocument) {
// Verify that an existing field is replaced and stays in the same order in the document.
TEST(ParsedAddFieldsExecutionTest, ReplacesFieldThatAlreadyExistsInDocument) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("c" << 3));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("c" << 3));
// Specified field is the only field in the document, and is replaced.
auto result = addition.applyProjection(Document{{"c", 1}});
@@ -303,9 +302,8 @@ TEST(ParsedAddFieldsExecutionTest, ReplacesFieldThatAlreadyExistsInDocument) {
// Verify that replacing multiple fields preserves the original field order in the document.
TEST(ParsedAddFieldsExecutionTest, ReplacesMultipleFieldsWhilePreservingInputFieldOrder) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("second"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("second"
<< "SECOND"
<< "first"
<< "FIRST"));
@@ -317,9 +315,8 @@ TEST(ParsedAddFieldsExecutionTest, ReplacesMultipleFieldsWhilePreservingInputFie
// Verify that adding multiple fields adds the fields in the order specified.
TEST(ParsedAddFieldsExecutionTest, AddsNewFieldsAfterExistingFieldsInOrderSpecified) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("firstComputed"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("firstComputed"
<< "FIRST"
<< "secondComputed"
<< "SECOND"));
@@ -336,9 +333,8 @@ TEST(ParsedAddFieldsExecutionTest, AddsNewFieldsAfterExistingFieldsInOrderSpecif
// each independently.
TEST(ParsedAddFieldsExecutionTest, ReplacesAndAddsNewFieldsWithSameOrderingRulesAsSeparately) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("firstComputed"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("firstComputed"
<< "FIRST"
<< "second"
<< "SECOND"));
@@ -352,9 +348,8 @@ TEST(ParsedAddFieldsExecutionTest, ReplacesAndAddsNewFieldsWithSameOrderingRules
// input document, when adding new fields.
TEST(ParsedAddFieldsExecutionTest, IdFieldIsKeptInOrderItAppearsInInputDocument) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("newField"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("newField"
<< "computedVal"));
auto result = addition.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}});
auto expectedResult = Document{{"_id", "ID"_sd}, {"a", 1}, {"newField", "computedVal"_sd}};
@@ -368,9 +363,8 @@ TEST(ParsedAddFieldsExecutionTest, IdFieldIsKeptInOrderItAppearsInInputDocument)
// Verify that replacing or adding _id works just like any other field.
TEST(ParsedAddFieldsExecutionTest, ShouldReplaceIdWithComputedId) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("_id"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("_id"
<< "newId"));
auto result = addition.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}});
auto expectedResult = Document{{"_id", "newId"_sd}, {"a", 1}};
@@ -392,8 +386,8 @@ TEST(ParsedAddFieldsExecutionTest, ShouldReplaceIdWithComputedId) {
// Verify that adding a dotted field keeps the other fields in the subdocument.
TEST(ParsedAddFieldsExecutionTest, KeepsExistingSubFieldsWhenAddingSimpleDottedFieldToSubDoc) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a.b" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a.b" << true));
// More than one field in sub document.
auto result = addition.applyProjection(Document{{"a", Document{{"b", 1}, {"c", 2}}}});
@@ -419,8 +413,8 @@ TEST(ParsedAddFieldsExecutionTest, KeepsExistingSubFieldsWhenAddingSimpleDottedF
// Verify that creating a dotted field creates the subdocument structure necessary.
TEST(ParsedAddFieldsExecutionTest, CreatesSubDocIfDottedAddedFieldDoesNotExist) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("sub.target" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("sub.target" << true));
// Should add the path if it doesn't exist.
auto result = addition.applyProjection(Document{});
@@ -437,8 +431,8 @@ TEST(ParsedAddFieldsExecutionTest, CreatesSubDocIfDottedAddedFieldDoesNotExist)
// SERVER-25200: make this agree with $set.
TEST(ParsedAddFieldsExecutionTest, AppliesDottedAdditionToEachElementInArray) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a.b" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a.b" << true));
vector<Value> nestedValues = {Value(1),
Value(Document{}),
@@ -463,9 +457,8 @@ TEST(ParsedAddFieldsExecutionTest, AppliesDottedAdditionToEachElementInArray) {
// Verify that creation of the subdocument structure works for many layers of nesting.
TEST(ParsedAddFieldsExecutionTest, CreatesNestedSubDocumentsAllTheWayToAddedField) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("a.b.c.d"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a.b.c.d"
<< "computedVal"));
// Should add the path if it doesn't exist.
@@ -482,9 +475,8 @@ TEST(ParsedAddFieldsExecutionTest, CreatesNestedSubDocumentsAllTheWayToAddedFiel
// Verify that _id is not special: we can add subfields to it as well.
TEST(ParsedAddFieldsExecutionTest, AddsSubFieldsOfId) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("_id.X" << true << "_id.Z"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("_id.X" << true << "_id.Z"
<< "NEW"));
auto result = addition.applyProjection(Document{{"_id", Document{{"X", 1}, {"Y", 2}}}});
auto expectedResult = Document{{"_id", Document{{"X", true}, {"Y", 2}, {"Z", "NEW"_sd}}}};
@@ -495,11 +487,10 @@ TEST(ParsedAddFieldsExecutionTest, AddsSubFieldsOfId) {
// can be used together in the same specification.
TEST(ParsedAddFieldsExecutionTest, ShouldAllowMixedNestedAndDottedFields) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
+ ParsedAddFields addition(expCtx);
// Include all of "a.b", "a.c", "a.d", and "a.e".
// Add new computed fields "a.W", "a.X", "a.Y", and "a.Z".
- addition.parse(expCtx,
- BSON("a.b" << true << "a.c" << true << "a.W"
+ addition.parse(BSON("a.b" << true << "a.c" << true << "a.W"
<< "W"
<< "a.X"
<< "X"
@@ -527,9 +518,8 @@ TEST(ParsedAddFieldsExecutionTest, ShouldAllowMixedNestedAndDottedFields) {
// Verify that adding nested fields preserves the addition order in the spec.
TEST(ParsedAddFieldsExecutionTest, AddsNestedAddedFieldsInOrderSpecified) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx,
- BSON("b.d"
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("b.d"
<< "FIRST"
<< "b.c"
<< "SECOND"));
@@ -545,8 +535,8 @@ TEST(ParsedAddFieldsExecutionTest, AddsNestedAddedFieldsInOrderSpecified) {
// Verify that the metadata is kept from the original input document.
TEST(ParsedAddFieldsExecutionTest, AlwaysKeepsMetadataFromOriginalDoc) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ParsedAddFields addition;
- addition.parse(expCtx, BSON("a" << true));
+ ParsedAddFields addition(expCtx);
+ addition.parse(BSON("a" << true));
MutableDocument inputDocBuilder(Document{{"a", 1}});
inputDocBuilder.setRandMetaField(1.0);
diff --git a/src/mongo/db/pipeline/parsed_aggregation_projection.cpp b/src/mongo/db/pipeline/parsed_aggregation_projection.cpp
index 69b196faab3..26894147b17 100644
--- a/src/mongo/db/pipeline/parsed_aggregation_projection.cpp
+++ b/src/mongo/db/pipeline/parsed_aggregation_projection.cpp
@@ -278,11 +278,11 @@ std::unique_ptr<ParsedAggregationProjection> ParsedAggregationProjection::create
// We can't use make_unique() here, since the branches have different types.
std::unique_ptr<ParsedAggregationProjection> parsedProject(
projectionType == ProjectionType::kInclusion
- ? static_cast<ParsedAggregationProjection*>(new ParsedInclusionProjection())
- : static_cast<ParsedAggregationProjection*>(new ParsedExclusionProjection()));
+ ? static_cast<ParsedAggregationProjection*>(new ParsedInclusionProjection(expCtx))
+ : static_cast<ParsedAggregationProjection*>(new ParsedExclusionProjection(expCtx)));
// Actually parse the specification.
- parsedProject->parse(expCtx, spec);
+ parsedProject->parse(spec);
return parsedProject;
}
diff --git a/src/mongo/db/pipeline/parsed_aggregation_projection.h b/src/mongo/db/pipeline/parsed_aggregation_projection.h
index 8d92e4ab768..227663a1342 100644
--- a/src/mongo/db/pipeline/parsed_aggregation_projection.h
+++ b/src/mongo/db/pipeline/parsed_aggregation_projection.h
@@ -133,8 +133,7 @@ public:
* inclusions and exclusions. 'variablesParseState' is used by any contained expressions to
* track which variables are defined so that they can later be referenced at execution time.
*/
- virtual void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec) = 0;
+ virtual void parse(const BSONObj& spec) = 0;
/**
* Optimize any expressions contained within this projection.
@@ -156,12 +155,15 @@ public:
}
protected:
- ParsedAggregationProjection() = default;
+ ParsedAggregationProjection(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : _expCtx(expCtx){};
/**
* Apply the projection to 'input'.
*/
virtual Document applyProjection(Document input) const = 0;
+
+ boost::intrusive_ptr<ExpressionContext> _expCtx;
};
} // namespace parsed_aggregation_projection
} // namespace mongo
diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection.cpp b/src/mongo/db/pipeline/parsed_exclusion_projection.cpp
index ae3fa4ba2dc..51809826dfe 100644
--- a/src/mongo/db/pipeline/parsed_exclusion_projection.cpp
+++ b/src/mongo/db/pipeline/parsed_exclusion_projection.cpp
@@ -144,10 +144,7 @@ Document ParsedExclusionProjection::applyProjection(Document inputDoc) const {
return _root->applyProjection(inputDoc);
}
-void ParsedExclusionProjection::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec,
- ExclusionNode* node,
- size_t depth) {
+void ParsedExclusionProjection::parse(const BSONObj& spec, ExclusionNode* node, size_t depth) {
for (auto elem : spec) {
const auto fieldName = elem.fieldNameStringData().toString();
@@ -192,7 +189,7 @@ void ParsedExclusionProjection::parse(const boost::intrusive_ptr<ExpressionConte
child = child->addOrGetChild(fullPath.fullPath());
}
- parse(expCtx, elem.Obj(), child, depth + 1);
+ parse(elem.Obj(), child, depth + 1);
break;
}
default: { MONGO_UNREACHABLE; }
diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection.h b/src/mongo/db/pipeline/parsed_exclusion_projection.h
index 8f64ac5258e..feb868f08ca 100644
--- a/src/mongo/db/pipeline/parsed_exclusion_projection.h
+++ b/src/mongo/db/pipeline/parsed_exclusion_projection.h
@@ -97,7 +97,8 @@ private:
*/
class ParsedExclusionProjection : public ParsedAggregationProjection {
public:
- ParsedExclusionProjection() : ParsedAggregationProjection(), _root(new ExclusionNode()) {}
+ ParsedExclusionProjection(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : ParsedAggregationProjection(expCtx), _root(new ExclusionNode()) {}
ProjectionType getType() const final {
return ProjectionType::kExclusion;
@@ -108,8 +109,8 @@ public:
/**
* Parses the projection specification given by 'spec', populating internal data structures.
*/
- void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec) final {
- parse(expCtx, spec, _root.get(), 0);
+ void parse(const BSONObj& spec) final {
+ parse(spec, _root.get(), 0);
}
/**
@@ -134,10 +135,7 @@ private:
* Traverses 'spec' and parses each field. Adds any excluded fields at this level to 'node',
* and recurses on any sub-objects.
*/
- void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec,
- ExclusionNode* node,
- size_t depth);
+ void parse(const BSONObj& spec, ExclusionNode* node, size_t depth);
// The ExclusionNode tree does most of the execution work once constructed.
diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp b/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp
index f0e860067f7..a79950a13b3 100644
--- a/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp
+++ b/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp
@@ -57,33 +57,33 @@ using std::vector;
DEATH_TEST(ExclusionProjection,
ShouldRejectComputedField,
"Invariant failure fieldName[0] != '$'") {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ParsedExclusionProjection exclusion(expCtx);
// Top-level expression.
- exclusion.parse(expCtx, BSON("a" << false << "b" << BSON("$literal" << 1)));
+ exclusion.parse(BSON("a" << false << "b" << BSON("$literal" << 1)));
}
DEATH_TEST(ExclusionProjection,
ShouldFailWhenGivenIncludedField,
"Invariant failure !elem.trueValue()") {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a" << true));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << true));
}
DEATH_TEST(ExclusionProjection,
ShouldFailWhenGivenIncludedId,
"Invariant failure !elem.trueValue()") {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("_id" << true << "a" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("_id" << true << "a" << false));
}
TEST(ExclusionProjection, ShouldSerializeToEquivalentProjection) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ParsedExclusionProjection exclusion(expCtx);
exclusion.parse(
- expCtx, fromjson("{a: 0, b: {c: NumberLong(0), d: 0.0}, 'x.y': false, _id: NumberInt(0)}"));
+ fromjson("{a: 0, b: {c: NumberLong(0), d: 0.0}, 'x.y': false, _id: NumberInt(0)}"));
// Converts numbers to bools, converts dotted paths to nested documents. Note order of excluded
// fields is subject to change.
@@ -111,10 +111,9 @@ TEST(ExclusionProjection, ShouldNotAddAnyDependencies) {
// need to include the "a" in the dependencies of this projection, since it will just be ignored
// later. If there are no later stages, then we will finish the dependency computation
// cycle without full knowledge of which fields are needed, and thus include all the fields.
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx,
- BSON("_id" << false << "a" << false << "b.c" << false << "x.y.z" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("_id" << false << "a" << false << "b.c" << false << "x.y.z" << false));
DepsTracker deps;
exclusion.addDependencies(&deps);
@@ -125,9 +124,9 @@ TEST(ExclusionProjection, ShouldNotAddAnyDependencies) {
}
TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModified) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("_id" << false << "a" << false << "b.c" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("_id" << false << "a" << false << "b.c" << false));
auto modifiedPaths = exclusion.getModifiedPaths();
ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kFiniteSet);
@@ -138,9 +137,9 @@ TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModified) {
}
TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModifiedWhenSpecifiedAsNestedObj) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a" << BSON("b" << false << "c" << BSON("d" << false))));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << BSON("b" << false << "c" << BSON("d" << false))));
auto modifiedPaths = exclusion.getModifiedPaths();
ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kFiniteSet);
@@ -154,9 +153,9 @@ TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModifiedWhenSpecifiedAsNes
//
TEST(ExclusionProjectionExecutionTest, ShouldExcludeTopLevelField) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << false));
// More than one field in document.
auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}});
@@ -180,10 +179,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeTopLevelField) {
}
TEST(ExclusionProjectionExecutionTest, ShouldCoerceNumericsToBools) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx,
- BSON("a" << Value(0) << "b" << Value(0LL) << "c" << Value(0.0) << "d"
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << Value(0) << "b" << Value(0LL) << "c" << Value(0.0) << "d"
<< Value(Decimal128(0))));
auto result =
@@ -193,36 +191,36 @@ TEST(ExclusionProjectionExecutionTest, ShouldCoerceNumericsToBools) {
}
TEST(ExclusionProjectionExecutionTest, ShouldPreserveOrderOfExistingFields) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("second" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("second" << false));
auto result = exclusion.applyProjection(Document{{"first", 0}, {"second", 1}, {"third", 2}});
auto expectedResult = Document{{"first", 0}, {"third", 2}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(ExclusionProjectionExecutionTest, ShouldImplicitlyIncludeId) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << false));
auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}});
auto expectedResult = Document{{"b", 2}, {"_id", "ID"_sd}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(ExclusionProjectionExecutionTest, ShouldExcludeIdIfExplicitlyExcluded) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a" << false << "_id" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << false << "_id" << false));
auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}});
auto expectedResult = Document{{"b", 2}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(ExclusionProjectionExecutionTest, ShouldExcludeIdAndKeepAllOtherFields) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("_id" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("_id" << false));
auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}});
auto expectedResult = Document{{"a", 1}, {"b", 2}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
@@ -233,9 +231,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeIdAndKeepAllOtherFields) {
//
TEST(ExclusionProjectionExecutionTest, ShouldExcludeSubFieldsOfId) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("_id.x" << false << "_id" << BSON("y" << false)));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("_id.x" << false << "_id" << BSON("y" << false)));
auto result = exclusion.applyProjection(
Document{{"_id", Document{{"x", 1}, {"y", 2}, {"z", 3}}}, {"a", 1}});
auto expectedResult = Document{{"_id", Document{{"z", 3}}}, {"a", 1}};
@@ -243,9 +241,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeSubFieldsOfId) {
}
TEST(ExclusionProjectionExecutionTest, ShouldExcludeSimpleDottedFieldFromSubDoc) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a.b" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a.b" << false));
// More than one field in sub document.
auto result = exclusion.applyProjection(Document{{"a", Document{{"b", 1}, {"c", 2}}}});
@@ -269,9 +267,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeSimpleDottedFieldFromSubDoc)
}
TEST(ExclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedExcludedFieldDoesNotExist) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("sub.target" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("sub.target" << false));
// Should not add the path if it doesn't exist.
auto result = exclusion.applyProjection(Document{});
@@ -285,9 +283,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedExcludedFiel
}
TEST(ExclusionProjectionExecutionTest, ShouldApplyDottedExclusionToEachElementInArray) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a.b" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a.b" << false));
std::vector<Value> nestedValues = {
Value(1),
@@ -309,11 +307,10 @@ TEST(ExclusionProjectionExecutionTest, ShouldApplyDottedExclusionToEachElementIn
}
TEST(ExclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ParsedExclusionProjection exclusion(expCtx);
// Exclude all of "a.b", "a.c", "a.d", and "a.e".
exclusion.parse(
- expCtx,
BSON("a.b" << false << "a.c" << false << "a" << BSON("d" << false << "e" << false)));
auto result = exclusion.applyProjection(
Document{{"a", Document{{"b", 1}, {"c", 2}, {"d", 3}, {"e", 4}, {"f", 5}}}});
@@ -322,9 +319,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) {
}
TEST(ExclusionProjectionExecutionTest, ShouldAlwaysKeepMetadataFromOriginalDoc) {
- ParsedExclusionProjection exclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- exclusion.parse(expCtx, BSON("a" << false));
+ ParsedExclusionProjection exclusion(expCtx);
+ exclusion.parse(BSON("a" << false));
MutableDocument inputDocBuilder(Document{{"_id", "ID"_sd}, {"a", 1}});
inputDocBuilder.setRandMetaField(1.0);
diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection.cpp b/src/mongo/db/pipeline/parsed_inclusion_projection.cpp
index a2c80238570..765ee6f125b 100644
--- a/src/mongo/db/pipeline/parsed_inclusion_projection.cpp
+++ b/src/mongo/db/pipeline/parsed_inclusion_projection.cpp
@@ -141,29 +141,29 @@ Value InclusionNode::applyInclusionsToValue(Value inputValue) const {
}
}
-void InclusionNode::addComputedFields(MutableDocument* outputDoc, Variables* vars) const {
+void InclusionNode::addComputedFields(MutableDocument* outputDoc) const {
for (auto&& field : _orderToProcessAdditionsAndChildren) {
auto childIt = _children.find(field);
if (childIt != _children.end()) {
outputDoc->setField(field,
- childIt->second->addComputedFields(outputDoc->peek()[field], vars));
+ childIt->second->addComputedFields(outputDoc->peek()[field]));
} else {
auto expressionIt = _expressions.find(field);
invariant(expressionIt != _expressions.end());
- outputDoc->setField(field, expressionIt->second->evaluate(vars));
+ outputDoc->setField(field, expressionIt->second->evaluate());
}
}
}
-Value InclusionNode::addComputedFields(Value inputValue, Variables* vars) const {
+Value InclusionNode::addComputedFields(Value inputValue) const {
if (inputValue.getType() == BSONType::Object) {
MutableDocument outputDoc(inputValue.getDocument());
- addComputedFields(&outputDoc, vars);
+ addComputedFields(&outputDoc);
return outputDoc.freezeToValue();
} else if (inputValue.getType() == BSONType::Array) {
std::vector<Value> values = inputValue.getArray();
for (auto it = values.begin(); it != values.end(); ++it) {
- *it = addComputedFields(*it, vars);
+ *it = addComputedFields(*it);
}
return Value(std::move(values));
} else {
@@ -172,7 +172,7 @@ Value InclusionNode::addComputedFields(Value inputValue, Variables* vars) const
// document of all the computed values. This case represents applying a projection like
// {"a.b": {$literal: 1}} to the document {a: 1}. This should yield {a: {b: 1}}.
MutableDocument outputDoc;
- addComputedFields(&outputDoc, vars);
+ addComputedFields(&outputDoc);
return outputDoc.freezeToValue();
}
// We didn't have any expressions, so just return the missing value.
@@ -273,9 +273,7 @@ void InclusionNode::addComputedPaths(std::set<std::string>* computedPaths,
// ParsedInclusionProjection
//
-void ParsedInclusionProjection::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec,
- const VariablesParseState& variablesParseState) {
+void ParsedInclusionProjection::parse(const BSONObj& spec) {
// It is illegal to specify a projection with no output fields.
bool atLeastOneFieldInOutput = false;
@@ -311,7 +309,7 @@ void ParsedInclusionProjection::parse(const boost::intrusive_ptr<ExpressionConte
}
case BSONType::Object: {
// This is either an expression, or a nested specification.
- if (parseObjectAsExpression(expCtx, fieldName, elem.Obj(), variablesParseState)) {
+ if (parseObjectAsExpression(fieldName, elem.Obj(), _expCtx->variablesParseState)) {
// It was an expression.
break;
}
@@ -328,14 +326,14 @@ void ParsedInclusionProjection::parse(const boost::intrusive_ptr<ExpressionConte
// iteration too soon. Add the last path here.
child = child->addOrGetChild(remainingPath.fullPath());
- parseSubObject(expCtx, elem.Obj(), variablesParseState, child);
+ parseSubObject(elem.Obj(), _expCtx->variablesParseState, child);
break;
}
default: {
// This is a literal value.
_root->addComputedField(
FieldPath(elem.fieldName()),
- Expression::parseOperand(expCtx, elem, variablesParseState));
+ Expression::parseOperand(_expCtx, elem, _expCtx->variablesParseState));
}
}
}
@@ -358,7 +356,7 @@ Document ParsedInclusionProjection::applyProjection(Document inputDoc, Variables
MutableDocument output;
_root->applyInclusions(inputDoc, &output);
- _root->addComputedFields(&output, vars);
+ _root->addComputedFields(&output);
// Always pass through the metadata.
output.copyMetaDataFrom(inputDoc);
@@ -366,7 +364,6 @@ Document ParsedInclusionProjection::applyProjection(Document inputDoc, Variables
}
bool ParsedInclusionProjection::parseObjectAsExpression(
- const boost::intrusive_ptr<ExpressionContext>& expCtx,
StringData pathToObject,
const BSONObj& objSpec,
const VariablesParseState& variablesParseState) {
@@ -375,17 +372,15 @@ bool ParsedInclusionProjection::parseObjectAsExpression(
// field.
invariant(objSpec.nFields() == 1);
_root->addComputedField(pathToObject,
- Expression::parseExpression(expCtx, objSpec, variablesParseState));
+ Expression::parseExpression(_expCtx, objSpec, variablesParseState));
return true;
}
return false;
}
-void ParsedInclusionProjection::parseSubObject(
- const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& subObj,
- const VariablesParseState& variablesParseState,
- InclusionNode* node) {
+void ParsedInclusionProjection::parseSubObject(const BSONObj& subObj,
+ const VariablesParseState& variablesParseState,
+ InclusionNode* node) {
for (auto elem : subObj) {
invariant(elem.fieldName()[0] != '$');
// Dotted paths in a sub-object have already been disallowed in
@@ -407,20 +402,20 @@ void ParsedInclusionProjection::parseSubObject(
// This is either an expression, or a nested specification.
auto fieldName = elem.fieldNameStringData().toString();
if (parseObjectAsExpression(
- expCtx,
FieldPath::getFullyQualifiedPath(node->getPath(), fieldName),
elem.Obj(),
variablesParseState)) {
break;
}
auto child = node->addOrGetChild(fieldName);
- parseSubObject(expCtx, elem.Obj(), variablesParseState, child);
+ parseSubObject(elem.Obj(), variablesParseState, child);
break;
}
default: {
// This is a literal value.
- node->addComputedField(FieldPath(elem.fieldName()),
- Expression::parseOperand(expCtx, elem, variablesParseState));
+ node->addComputedField(
+ FieldPath(elem.fieldName()),
+ Expression::parseOperand(_expCtx, elem, variablesParseState));
}
}
}
diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection.h b/src/mongo/db/pipeline/parsed_inclusion_projection.h
index eb81015f8a4..f86b9970519 100644
--- a/src/mongo/db/pipeline/parsed_inclusion_projection.h
+++ b/src/mongo/db/pipeline/parsed_inclusion_projection.h
@@ -86,10 +86,9 @@ public:
void applyInclusions(Document inputDoc, MutableDocument* outputDoc) const;
/**
- * Add computed fields to 'outputDoc'. 'vars' is passed through to be used in Expression
- * evaluation.
+ * Add computed fields to 'outputDoc'.
*/
- void addComputedFields(MutableDocument* outputDoc, Variables* vars) const;
+ void addComputedFields(MutableDocument* outputDoc) const;
/**
* Creates the child if it doesn't already exist. 'field' is not allowed to be dotted.
@@ -140,7 +139,7 @@ private:
// Helpers for the Document versions above. These will apply the transformation recursively to
// each element of any arrays, and ensure non-documents are handled appropriately.
Value applyInclusionsToValue(Value inputVal) const;
- Value addComputedFields(Value inputVal, Variables* vars) const;
+ Value addComputedFields(Value inputVal) const;
/**
* Returns nullptr if no such child exists.
@@ -185,7 +184,8 @@ private:
*/
class ParsedInclusionProjection : public ParsedAggregationProjection {
public:
- ParsedInclusionProjection() : ParsedAggregationProjection(), _root(new InclusionNode()) {}
+ ParsedInclusionProjection(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : ParsedAggregationProjection(expCtx), _root(new InclusionNode()) {}
ProjectionType getType() const final {
return ProjectionType::kInclusion;
@@ -194,12 +194,7 @@ public:
/**
* Parses the projection specification given by 'spec', populating internal data structures.
*/
- void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec) final {
- VariablesIdGenerator idGenerator;
- VariablesParseState variablesParseState(&idGenerator);
- parse(expCtx, spec, variablesParseState);
- _variables = stdx::make_unique<Variables>(idGenerator.getIdCount());
- }
+ void parse(const BSONObj& spec) final;
/**
* Serialize the projection.
@@ -248,22 +243,15 @@ public:
* each element in the array.
*/
Document applyProjection(Document inputDoc) const final {
- _variables->setRoot(inputDoc);
- return applyProjection(inputDoc, _variables.get());
+ auto& vars = _expCtx->variables;
+ vars.setRoot(inputDoc);
+ return applyProjection(inputDoc, &vars);
}
Document applyProjection(Document inputDoc, Variables* vars) const;
private:
/**
- * Parses 'spec' to determine which fields to include, which are computed, and whether to
- * include '_id' or not.
- */
- void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& spec,
- const VariablesParseState& variablesParseState);
-
- /**
* Attempts to parse 'objSpec' as an expression like {$add: [...]}. Adds a computed field to
* '_root' and returns true if it was successfully parsed as an expression. Returns false if it
* was not an expression specification.
@@ -271,8 +259,7 @@ private:
* Throws an error if it was determined to be an expression specification, but failed to parse
* as a valid expression.
*/
- bool parseObjectAsExpression(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- StringData pathToObject,
+ bool parseObjectAsExpression(StringData pathToObject,
const BSONObj& objSpec,
const VariablesParseState& variablesParseState);
@@ -280,8 +267,7 @@ private:
* Traverses 'subObj' and parses each field. Adds any included or computed fields at this level
* to 'node'.
*/
- void parseSubObject(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const BSONObj& subObj,
+ void parseSubObject(const BSONObj& subObj,
const VariablesParseState& variablesParseState,
InclusionNode* node);
@@ -290,10 +276,6 @@ private:
// The InclusionNode tree does most of the execution work once constructed.
std::unique_ptr<InclusionNode> _root;
-
- // This is needed to give the expressions knowledge about the context in which they are being
- // executed.
- std::unique_ptr<Variables> _variables;
};
} // namespace parsed_aggregation_projection
} // namespace mongo
diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp b/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp
index a0e7398ef6f..add0c9e7bb3 100644
--- a/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp
+++ b/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp
@@ -53,24 +53,23 @@ BSONObj wrapInLiteral(const T& arg) {
}
TEST(InclusionProjection, ShouldThrowWhenParsingInvalidExpression) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ASSERT_THROWS(inclusion.parse(expCtx,
- BSON("a" << BSON("$gt" << BSON("bad"
+ ParsedInclusionProjection inclusion(expCtx);
+ ASSERT_THROWS(inclusion.parse(BSON("a" << BSON("$gt" << BSON("bad"
<< "arguments")))),
UserException);
}
TEST(InclusionProjection, ShouldRejectProjectionWithNoOutputFields) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- ASSERT_THROWS(inclusion.parse(expCtx, BSON("_id" << false)), UserException);
+ ParsedInclusionProjection inclusion(expCtx);
+ ASSERT_THROWS(inclusion.parse(BSON("_id" << false)), UserException);
}
TEST(InclusionProjection, ShouldAddIncludedFieldsToDependencies) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("_id" << false << "a" << true << "x.y" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("_id" << false << "a" << true << "x.y" << true));
DepsTracker deps;
inclusion.addDependencies(&deps);
@@ -82,9 +81,9 @@ TEST(InclusionProjection, ShouldAddIncludedFieldsToDependencies) {
}
TEST(InclusionProjection, ShouldAddIdToDependenciesIfNotSpecified) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true));
DepsTracker deps;
inclusion.addDependencies(&deps);
@@ -95,10 +94,9 @@ TEST(InclusionProjection, ShouldAddIdToDependenciesIfNotSpecified) {
}
TEST(InclusionProjection, ShouldAddDependenciesOfComputedFields) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx,
- BSON("a"
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a"
<< "$a"
<< "x"
<< "$z"));
@@ -113,10 +111,9 @@ TEST(InclusionProjection, ShouldAddDependenciesOfComputedFields) {
}
TEST(InclusionProjection, ShouldAddPathToDependenciesForNestedComputedFields) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx,
- BSON("x.y"
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("x.y"
<< "$z"));
DepsTracker deps;
@@ -132,9 +129,9 @@ TEST(InclusionProjection, ShouldAddPathToDependenciesForNestedComputedFields) {
}
TEST(InclusionProjection, ShouldSerializeToEquivalentProjection) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}"));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}"));
// Adds implicit "_id" inclusion, converts numbers to bools, serializes expressions.
auto expectedSerialization = Document(fromjson(
@@ -151,9 +148,9 @@ TEST(InclusionProjection, ShouldSerializeToEquivalentProjection) {
}
TEST(InclusionProjection, ShouldSerializeExplicitExclusionOfId) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("_id" << false << "a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("_id" << false << "a" << true));
// Adds implicit "_id" inclusion, converts numbers to bools, serializes expressions.
auto expectedSerialization = Document{{"_id", false}, {"a", true}};
@@ -170,9 +167,9 @@ TEST(InclusionProjection, ShouldSerializeExplicitExclusionOfId) {
TEST(InclusionProjection, ShouldOptimizeTopLevelExpressions) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2))));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2))));
inclusion.optimize();
@@ -189,9 +186,9 @@ TEST(InclusionProjection, ShouldOptimizeTopLevelExpressions) {
}
TEST(InclusionProjection, ShouldOptimizeNestedExpressions) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2))));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2))));
inclusion.optimize();
@@ -209,14 +206,12 @@ TEST(InclusionProjection, ShouldOptimizeNestedExpressions) {
}
TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModified) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(
- expCtx,
- BSON("a" << wrapInLiteral("computedVal") << "b.c" << wrapInLiteral("computedVal") << "d"
- << true
- << "e.f"
- << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON(
+ "a" << wrapInLiteral("computedVal") << "b.c" << wrapInLiteral("computedVal") << "d" << true
+ << "e.f"
+ << true));
auto modifiedPaths = inclusion.getModifiedPaths();
ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kAllExcept);
@@ -231,10 +226,9 @@ TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModified) {
}
TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModifiedWithIdExclusion) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx,
- BSON("_id" << false << "a" << wrapInLiteral("computedVal") << "b.c"
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("_id" << false << "a" << wrapInLiteral("computedVal") << "b.c"
<< wrapInLiteral("computedVal")
<< "d"
<< true
@@ -260,9 +254,9 @@ TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModifiedWith
//
TEST(InclusionProjectionExecutionTest, ShouldIncludeTopLevelField) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true));
// More than one field in document.
auto result = inclusion.applyProjection(Document{{"a", 1}, {"b", 2}});
@@ -286,9 +280,9 @@ TEST(InclusionProjectionExecutionTest, ShouldIncludeTopLevelField) {
}
TEST(InclusionProjectionExecutionTest, ShouldAddComputedTopLevelField) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("newField" << wrapInLiteral("computedVal")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("newField" << wrapInLiteral("computedVal")));
auto result = inclusion.applyProjection(Document{});
auto expectedResult = Document{{"newField", "computedVal"_sd}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
@@ -300,28 +294,27 @@ TEST(InclusionProjectionExecutionTest, ShouldAddComputedTopLevelField) {
}
TEST(InclusionProjectionExecutionTest, ShouldApplyBothInclusionsAndComputedFields) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true << "newField" << wrapInLiteral("computedVal")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true << "newField" << wrapInLiteral("computedVal")));
auto result = inclusion.applyProjection(Document{{"a", 1}});
auto expectedResult = Document{{"a", 1}, {"newField", "computedVal"_sd}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(InclusionProjectionExecutionTest, ShouldIncludeFieldsInOrderOfInputDoc) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("first" << true << "second" << true << "third" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("first" << true << "second" << true << "third" << true));
auto inputDoc = Document{{"second", 1}, {"first", 0}, {"third", 2}};
auto result = inclusion.applyProjection(inputDoc);
ASSERT_DOCUMENT_EQ(result, inputDoc);
}
TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsInOrderSpecified) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx,
- BSON("firstComputed" << wrapInLiteral("FIRST") << "secondComputed"
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("firstComputed" << wrapInLiteral("FIRST") << "secondComputed"
<< wrapInLiteral("SECOND")));
auto result = inclusion.applyProjection(Document{{"first", 0}, {"second", 1}, {"third", 2}});
auto expectedResult = Document{{"firstComputed", "FIRST"_sd}, {"secondComputed", "SECOND"_sd}};
@@ -329,9 +322,9 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsInOrderSpecified
}
TEST(InclusionProjectionExecutionTest, ShouldImplicitlyIncludeId) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true));
auto result = inclusion.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}, {"b", 2}});
auto expectedResult = Document{{"_id", "ID"_sd}, {"a", 1}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
@@ -343,18 +336,18 @@ TEST(InclusionProjectionExecutionTest, ShouldImplicitlyIncludeId) {
}
TEST(InclusionProjectionExecutionTest, ShouldImplicitlyIncludeIdWithComputedFields) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("newField" << wrapInLiteral("computedVal")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("newField" << wrapInLiteral("computedVal")));
auto result = inclusion.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}});
auto expectedResult = Document{{"_id", "ID"_sd}, {"newField", "computedVal"_sd}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(InclusionProjectionExecutionTest, ShouldIncludeIdIfExplicitlyIncluded) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true << "_id" << true << "b" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true << "_id" << true << "b" << true));
auto result =
inclusion.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}, {"b", 2}, {"c", 3}});
auto expectedResult = Document{{"_id", "ID"_sd}, {"a", 1}, {"b", 2}};
@@ -362,18 +355,18 @@ TEST(InclusionProjectionExecutionTest, ShouldIncludeIdIfExplicitlyIncluded) {
}
TEST(InclusionProjectionExecutionTest, ShouldExcludeIdIfExplicitlyExcluded) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true << "_id" << false));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true << "_id" << false));
auto result = inclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}});
auto expectedResult = Document{{"a", 1}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(InclusionProjectionExecutionTest, ShouldReplaceIdWithComputedId) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("_id" << wrapInLiteral("newId")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("_id" << wrapInLiteral("newId")));
auto result = inclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}});
auto expectedResult = Document{{"_id", "newId"_sd}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
@@ -384,9 +377,9 @@ TEST(InclusionProjectionExecutionTest, ShouldReplaceIdWithComputedId) {
//
TEST(InclusionProjectionExecutionTest, ShouldIncludeSimpleDottedFieldFromSubDoc) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a.b" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a.b" << true));
// More than one field in sub document.
auto result = inclusion.applyProjection(Document{{"a", Document{{"b", 1}, {"c", 2}}}});
@@ -410,9 +403,9 @@ TEST(InclusionProjectionExecutionTest, ShouldIncludeSimpleDottedFieldFromSubDoc)
}
TEST(InclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedIncludedFieldDoesNotExist) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("sub.target" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("sub.target" << true));
// Should not add the path if it doesn't exist.
auto result = inclusion.applyProjection(Document{});
@@ -426,9 +419,9 @@ TEST(InclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedIncludedFiel
}
TEST(InclusionProjectionExecutionTest, ShouldApplyDottedInclusionToEachElementInArray) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a.b" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a.b" << true));
vector<Value> nestedValues = {Value(1),
Value(Document{}),
@@ -451,9 +444,9 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyDottedInclusionToEachElementIn
}
TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToSubDocument) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("sub.target" << wrapInLiteral("computedVal")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("sub.target" << wrapInLiteral("computedVal")));
// Other fields exist in sub document, one of which is the specified field.
auto result = inclusion.applyProjection(Document{{"sub", Document{{"target", 1}, {"c", 2}}}});
@@ -472,9 +465,9 @@ TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToSubDocument
}
TEST(InclusionProjectionExecutionTest, ShouldCreateSubDocIfDottedComputedFieldDoesntExist) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("sub.target" << wrapInLiteral("computedVal")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("sub.target" << wrapInLiteral("computedVal")));
// Should add the path if it doesn't exist.
auto result = inclusion.applyProjection(Document{});
@@ -487,9 +480,9 @@ TEST(InclusionProjectionExecutionTest, ShouldCreateSubDocIfDottedComputedFieldDo
}
TEST(InclusionProjectionExecutionTest, ShouldCreateNestedSubDocumentsAllTheWayToComputedField) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a.b.c.d" << wrapInLiteral("computedVal")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a.b.c.d" << wrapInLiteral("computedVal")));
// Should add the path if it doesn't exist.
auto result = inclusion.applyProjection(Document{});
@@ -503,9 +496,9 @@ TEST(InclusionProjectionExecutionTest, ShouldCreateNestedSubDocumentsAllTheWayTo
}
TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToEachElementInArray) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a.b" << wrapInLiteral("COMPUTED")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a.b" << wrapInLiteral("COMPUTED")));
vector<Value> nestedValues = {Value(1),
Value(Document{}),
@@ -527,9 +520,9 @@ TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToEachElement
}
TEST(InclusionProjectionExecutionTest, ShouldApplyInclusionsAndAdditionsToEachElementInArray) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a.inc" << true << "a.comp" << wrapInLiteral("COMPUTED")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a.inc" << true << "a.comp" << wrapInLiteral("COMPUTED")));
vector<Value> nestedValues = {Value(1),
Value(Document{}),
@@ -555,26 +548,24 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyInclusionsAndAdditionsToEachEl
}
TEST(InclusionProjectionExecutionTest, ShouldAddOrIncludeSubFieldsOfId) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("_id.X" << true << "_id.Z" << wrapInLiteral("NEW")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("_id.X" << true << "_id.Z" << wrapInLiteral("NEW")));
auto result = inclusion.applyProjection(Document{{"_id", Document{{"X", 1}, {"Y", 2}}}});
auto expectedResult = Document{{"_id", Document{{"X", 1}, {"Z", "NEW"_sd}}}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(InclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ ParsedInclusionProjection inclusion(expCtx);
// Include all of "a.b", "a.c", "a.d", and "a.e".
// Add new computed fields "a.W", "a.X", "a.Y", and "a.Z".
- inclusion.parse(
- expCtx,
- BSON("a.b" << true << "a.c" << true << "a.W" << wrapInLiteral("W") << "a.X"
- << wrapInLiteral("X")
- << "a"
- << BSON("d" << true << "e" << true << "Y" << wrapInLiteral("Y") << "Z"
- << wrapInLiteral("Z"))));
+ inclusion.parse(BSON(
+ "a.b" << true << "a.c" << true << "a.W" << wrapInLiteral("W") << "a.X" << wrapInLiteral("X")
+ << "a"
+ << BSON("d" << true << "e" << true << "Y" << wrapInLiteral("Y") << "Z"
+ << wrapInLiteral("Z"))));
auto result = inclusion.applyProjection(Document{
{"a",
Document{{"b", "b"_sd}, {"c", "c"_sd}, {"d", "d"_sd}, {"e", "e"_sd}, {"f", "f"_sd}}}});
@@ -591,19 +582,18 @@ TEST(InclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) {
}
TEST(InclusionProjectionExecutionTest, ShouldApplyNestedComputedFieldsInOrderSpecified) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx,
- BSON("a" << wrapInLiteral("FIRST") << "b.c" << wrapInLiteral("SECOND")));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << wrapInLiteral("FIRST") << "b.c" << wrapInLiteral("SECOND")));
auto result = inclusion.applyProjection(Document{});
auto expectedResult = Document{{"a", "FIRST"_sd}, {"b", Document{{"c", "SECOND"_sd}}}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
}
TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsAfterAllInclusions) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("b.c" << wrapInLiteral("NEW") << "a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("b.c" << wrapInLiteral("NEW") << "a" << true));
auto result = inclusion.applyProjection(Document{{"a", 1}});
auto expectedResult = Document{{"a", 1}, {"b", Document{{"c", "NEW"_sd}}}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
@@ -621,9 +611,9 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsAfterAllInclusio
}
TEST(InclusionProjectionExecutionTest, ComputedFieldReplacingExistingShouldAppearAfterInclusions) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("b" << wrapInLiteral("NEW") << "a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("b" << wrapInLiteral("NEW") << "a" << true));
auto result = inclusion.applyProjection(Document{{"b", 1}, {"a", 1}});
auto expectedResult = Document{{"a", 1}, {"b", "NEW"_sd}};
ASSERT_DOCUMENT_EQ(result, expectedResult);
@@ -637,9 +627,9 @@ TEST(InclusionProjectionExecutionTest, ComputedFieldReplacingExistingShouldAppea
//
TEST(InclusionProjectionExecutionTest, ShouldAlwaysKeepMetadataFromOriginalDoc) {
- ParsedInclusionProjection inclusion;
const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- inclusion.parse(expCtx, BSON("a" << true));
+ ParsedInclusionProjection inclusion(expCtx);
+ inclusion.parse(BSON("a" << true));
MutableDocument inputDocBuilder(Document{{"a", 1}});
inputDocBuilder.setRandMetaField(1.0);
diff --git a/src/mongo/db/pipeline/variables.cpp b/src/mongo/db/pipeline/variables.cpp
index 199517b5f7a..ba1b42e4fb1 100644
--- a/src/mongo/db/pipeline/variables.cpp
+++ b/src/mongo/db/pipeline/variables.cpp
@@ -29,6 +29,7 @@
#include "mongo/platform/basic.h"
#include "mongo/db/pipeline/variables.h"
+#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -99,9 +100,13 @@ void Variables::uassertValidNameForUserRead(StringData varName) {
void Variables::setValue(Id id, const Value& value) {
uassert(17199, "can't use Variables::setValue to set a reserved builtin variable", id >= 0);
- auto varIndex = static_cast<size_t>(id);
- invariant(varIndex < _numVars);
- _rest[varIndex] = value;
+
+ const auto idAsSizeT = static_cast<size_t>(id);
+ if (idAsSizeT >= _valueList.size()) {
+ _valueList.resize(idAsSizeT + 1);
+ }
+
+ _valueList[id] = value;
}
Value Variables::getValue(Id id) const {
@@ -117,9 +122,10 @@ Value Variables::getValue(Id id) const {
}
}
- auto varIndex = static_cast<size_t>(id);
- invariant(varIndex < _numVars);
- return _rest[varIndex];
+ uassert(40434,
+ str::stream() << "Requesting Variables::getValue with an out of range id: " << id,
+ static_cast<size_t>(id) < _valueList.size());
+ return _valueList[id];
}
Document Variables::getDocument(Id id) const {
@@ -142,7 +148,6 @@ Variables::Id VariablesParseState::defineVariable(StringData name) {
Variables::kBuiltinVarNameToId.find(name) == Variables::kBuiltinVarNameToId.end());
Variables::Id id = _idGenerator->generateId();
- invariant(id >= 0);
_variables[name] = id;
return id;
}
diff --git a/src/mongo/db/pipeline/variables.h b/src/mongo/db/pipeline/variables.h
index 5bbbf4f3d9c..b1e0f0c7deb 100644
--- a/src/mongo/db/pipeline/variables.h
+++ b/src/mongo/db/pipeline/variables.h
@@ -29,6 +29,8 @@
#pragma once
#include "mongo/db/pipeline/document.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/stdx/unordered_map.h"
#include "mongo/util/string_map.h"
namespace mongo {
@@ -38,27 +40,33 @@ class Variables {
MONGO_DISALLOW_COPYING(Variables);
public:
- /**
- * Each unique variable is assigned a unique id of this type. Negative ids are reserved for
- * system variables and non-negative ids are allocated for user variables.
- */
- typedef int64_t Id;
+ // Each unique variable is assigned a unique id of this type. Negative ids are reserved for
+ // system variables and non-negative ids are allocated for user variables.
+ using Id = int64_t;
/**
- * Constructs a placeholder for expressions that use no variables (even builtins like ROOT or
- * REMOVE).
+ * Generates Variables::Id and keeps track of the number of Ids handed out.
*/
- Variables() : _numVars(0) {}
+ class IdGenerator {
+ public:
+ IdGenerator() : _nextId(0) {}
+
+ Variables::Id generateId() {
+ return _nextId++;
+ }
- explicit Variables(size_t numVars, const Document& root = Document())
- : _root(root), _rest(numVars == 0 ? NULL : new Value[numVars]), _numVars(numVars) {}
+ private:
+ Variables::Id _nextId;
+ };
+
+ Variables() = default;
static void uassertValidNameForUserWrite(StringData varName);
static void uassertValidNameForUserRead(StringData varName);
// Ids for builtin variables.
- static constexpr Id kRootId = Id(-1);
- static constexpr Id kRemoveId = Id(-2);
+ static constexpr Variables::Id kRootId = Id(-1);
+ static constexpr Variables::Id kRemoveId = Id(-2);
// Map from builtin var name to reserved id number.
static const StringMap<Id> kBuiltinVarNameToId;
@@ -80,45 +88,27 @@ public:
* Sets the value of a user-defined variable. Illegal to use with the reserved builtin variables
* defined above.
*/
- void setValue(Id id, const Value& value);
+ void setValue(Variables::Id id, const Value& value);
/**
* Gets the value of a user-defined or system variable.
*/
- Value getValue(Id id) const;
+ Value getValue(Variables::Id id) const;
/**
* Returns Document() for non-document values, but otherwise identical to getValue().
*/
- Document getDocument(Id id) const;
+ Document getDocument(Variables::Id id) const;
-private:
- Document _root;
- const std::unique_ptr<Value[]> _rest;
- const size_t _numVars;
-};
-
-/**
- * Generates Variables::Ids and keeps track of the number of Ids handed out.
- */
-class VariablesIdGenerator {
-public:
- VariablesIdGenerator() : _nextId(0) {}
-
- Variables::Id generateId() {
- return _nextId++;
+ IdGenerator* useIdGenerator() {
+ return &_idGenerator;
}
- /**
- * Returns the number of Ids handed out by this Generator.
- * Return value is intended to be passed to Variables constructor.
- */
- Variables::Id getIdCount() const {
- return _nextId;
- }
private:
- Variables::Id _nextId;
+ Document _root;
+ IdGenerator _idGenerator;
+ std::vector<Value> _valueList;
};
/**
@@ -130,7 +120,8 @@ private:
*/
class VariablesParseState {
public:
- explicit VariablesParseState(VariablesIdGenerator* idGenerator) : _idGenerator(idGenerator) {}
+ explicit VariablesParseState(Variables::IdGenerator* variableIdGenerator)
+ : _idGenerator(variableIdGenerator) {}
/**
* Assigns a named variable a unique Id. This differs from all other variables, even
@@ -150,8 +141,10 @@ public:
Variables::Id getVariable(StringData name) const;
private:
+ // Not owned here.
+ Variables::IdGenerator* _idGenerator;
+
StringMap<Variables::Id> _variables;
- VariablesIdGenerator* _idGenerator;
};
} // namespace mongo