[SERVER-54046] Permit empty objects as expressions in $set and $addFields aggregation stages Created: 26/Jan/21  Updated: 29/Oct/23  Resolved: 28/Jun/22

Status: Closed
Project: Core Server
Component/s: Querying
Affects Version/s: None
Fix Version/s: 6.1.0-rc0

Type: Improvement Priority: Major - P3
Reporter: Mindaugas Malinauskas Assignee: Naomie Gao (Inactive)
Resolution: Fixed Votes: 4
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
is depended on by TOOLS-3140 Investigate changes in SERVER-54046: ... Closed
Documented
is documented by DOCS-15453 Investigate changes in SERVER-54046: ... Closed
Duplicate
Related
related to GODRIVER-1967 Pipeline can't handle empty structs a... Closed
Backwards Compatibility: Fully Compatible
Sprint: QE 2022-06-27, QE 2022-07-11
Participants:

 Description   

Permit empty objects as expressions in $set  and $addFields aggregation stages. For example, an aggregation stage definition

 {$set: {a: {}}} 

should be accepted and should add field "a: {}" to the resulting object (this looks like an intuitively expected system behavior). Nesting of objects should be supported, like in a following example: 

{$set: {a: {b: {}}}}

 Currently, to achieve this behavior a wrapper expression $literal has to be used like this:

{$set: {a: {$literal: {}}}}

But an aggregation stage definition 

{$set: {a: []}} 

is accepted and results in field "a: []", which is somewhat inconsistent with handling of empty objects.

$set update operator expression with a field with an empty object  

db.foo.updateOne({}, {$set: { a: {} }})

is accepted.

Furthermore, we have recently relaxed restrictions for update modifiers (SERVER-38909) and $addFields (SERVER-48890)(permits {$set: {}}) to permit empty documents, thus this improvement would increase consistency.

Currently, execution of a $set aggregation stage with an empty object as an expression, like 

db.foo.updateOne({}, [{$set: { a: {} }}])

fails with the following error message: "Invalid $set :: caused by :: an empty object is not a valid value. Found empty object at path a".



 Comments   
Comment by Naomie Gao (Inactive) [ 28/Jun/22 ]

ing.giuliorizzo@gmail.com bluaqua@gmail.com asanti@leanitconsulting.ch teeling.sean@gmail.com richard.scarrott@paradeworld.co 

$addFields and $set will no longer throw an error if you want to set a path to an empty object.
For example the stage {$set: {a: {}}} would take an incoming document and set the 'a' field to an empty object.
The behavior of $project behavior has not been changed and in order to get the same behavior a user would still have to wrap the object with $literal like this: {$project: {a: {$literal: {}}}}

This feature will be available starting in MongoDB version 6.1.

Comment by Githook User [ 28/Jun/22 ]

Author:

{'name': 'Naomie Gao', 'email': 'naomie.gao@mongodb.com', 'username': 'naogao'}

Message: SERVER-54046 Permit empty objects in $set and $addFields stages
Branch: master
https://github.com/mongodb/mongo/commit/9ee587f10c71de038f345801fd8e6fa5c377bfd0

Comment by Giulio Rizzo [ 20/May/22 ]

We have the same issue with the Java driver also us. The priority of this bug is Major. Please provide an ETA... 

Comment by Raffaele Marranzini [ 20/May/22 ]

We are having the same issue that Andrea is having. Can you guys please fix asap?

Comment by Andrea Santi [ 20/May/22 ]

We have the same issue with the Java driver. It is really annoying, we would really avoid to handle it manually in the code with $literal like reported by Richard above.

Is there any ETA for the fix? The priority is major but is open from more than one year...

Comment by Richard Scarrott [ 11/Apr/22 ]

Would be great to see this fixed as it's pretty grim having to handle this in application code; we're doing this as the update object changes often:

 

import mapObj from 'map-obj';
 
// MongoDB agg updates blow up when $set is passed an empty object
// https://jira.mongodb.org/browse/SERVER-54046export
const sanitizeMongoSetValue = (value: any) => {
  if (value == null) {
    return value;
  }
  return mapObj(value, (key, value) => {
    if (isEmptyObject(value) && key !== '$literal') {
      return [key, { $literal: value }];
    }
    return [key, value];
  }, { deep: true });
};
 
const isEmptyObject = (value: any) => {
  if (!value) {
    return false;
  }
  const prototype = Object.getPrototypeOf(value);
  return (
    Object.keys(value).length === 0 &&
    (prototype === Object.prototype || prototype === null)
  );
};
 
db.collection.updateOne({ _id: fields._id }, [{ $set: sanitizeMongoSetValue(fields) }])
 

 

 

Comment by Sean Teeling [ 14/May/21 ]

Any updates on this?

Comment by Sean Teeling [ 21/Apr/21 ]

Commenting on this, coming from the Go driver: go doesn't natively have sets, but instead recommends using a construct of a map that points to an empty struct, which pipelines don't support using a $set for.

This is a pretty frustrating experience coming from the Go driver, and in general (any driver), the inconsistency between a regular update and an aggregation update is  both not well documented, and offers an inconsistent API (read: "non-user friendly"). 

Generated at Thu Feb 08 05:32:31 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.