From 76d67ea903cdad9cb574f197ba04e63b0747a3ca Mon Sep 17 00:00:00 2001 From: Nicholas Zolnierz Date: Wed, 19 Jan 2022 15:21:32 -0500 Subject: SERVER-62322 Allow an empty object to be projected in $setWindowFields --- jstests/aggregation/sources/setWindowFields/parse.js | 13 +++++++++++-- src/mongo/db/pipeline/document_source_set_window_fields.cpp | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/jstests/aggregation/sources/setWindowFields/parse.js b/jstests/aggregation/sources/setWindowFields/parse.js index 63b92e952e0..b766a76d8ae 100644 --- a/jstests/aggregation/sources/setWindowFields/parse.js +++ b/jstests/aggregation/sources/setWindowFields/parse.js @@ -213,8 +213,7 @@ assert.commandWorked(run({ })); // Not every accumulator is automatically a window function. - -var err = assert.commandFailedWithCode(run({$setWindowFields: {output: {a: {b: {$sum: "$a"}}}}}), +let err = assert.commandFailedWithCode(run({$setWindowFields: {output: {a: {b: {$sum: "$a"}}}}}), ErrorCodes.FailedToParse); assert.includes(err.errmsg, 'Expected a $-prefixed window function, b'); @@ -250,4 +249,14 @@ err = assert.commandFailedWithCode( }), ErrorCodes.FailedToParse); assert.includes(err.errmsg, 'Unrecognized window function, $summ'); + +// Test that an empty object is a valid projected field. +assert.commandWorked(coll.insert({})); +assert.commandWorked(run({$setWindowFields: {output: {v: {$max: {mergeObjects: {}}}}}})); + +// However conflicting field paths is always an error. +err = assert.commandFailedWithCode( + run({$setWindowFields: {output: {a: {$sum: 1}, 'a.b': {$sum: 1}}}}), 40176); +assert.includes(err.errmsg, 'specification contains two conflicting paths'); + })(); diff --git a/src/mongo/db/pipeline/document_source_set_window_fields.cpp b/src/mongo/db/pipeline/document_source_set_window_fields.cpp index 87cd3ad0267..09e27df140a 100644 --- a/src/mongo/db/pipeline/document_source_set_window_fields.cpp +++ b/src/mongo/db/pipeline/document_source_set_window_fields.cpp @@ -461,10 +461,12 @@ DocumentSource::GetNextResult DocumentSourceInternalSetWindowFields::doGetNext() MutableDocument addFieldsSpec; for (auto&& [fieldName, function] : _executableOutputs) { try { + // Wrap the projected value in a $literal since there are limitations on a user-facing + // $addFields that we don't want to enforce here (e.g. empty object). + addFieldsSpec.addField(fieldName, Value(DOC("$literal" << function->getNext()))); + } catch (const DBException&) { // If we hit a uassert while evaluating expressions on user data, delete the temporary // table before aborting the operation. - addFieldsSpec.addField(fieldName, function->getNext()); - } catch (const DBException&) { _iterator.finalize(); throw; } @@ -506,6 +508,7 @@ DocumentSource::GetNextResult DocumentSourceInternalSetWindowFields::doGetNext() _iterator.finalize(); break; } + auto projExec = projection_executor::AddFieldsProjectionExecutor::create( pExpCtx, addFieldsSpec.freeze().toBson()); -- cgit v1.2.1