Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-62322

Consistent validity treatment of empty objects (i.e., `{}`)

    • Type: Icon: Improvement Improvement
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 5.3.0
    • Affects Version/s: None
    • Component/s: None
    • Labels:
    • Fully Compatible
    • v5.2
    • 173

      Currently, there are two places in the code (i.e., here and here) that empty objects are strictly rejected in a user/intermediate document via assertions with this message: "an empty object is not a valid value". As empty objects are valid values, the reason for these assertions in the abovementioned code segments is unclear.

      Now, we have two options in front of us:

      1- Keep these assertions: In this case, we need to make sure that valid user documents are cleaned before getting to these assertions (and make sure they don't have empty objects at any level). In addition, we need to make sure that no empty object is produced as an intermediate document (i.e., a result of previous stages).

      Here's an example for the latter:
      Currently, it's possible to produce empty objects using $project. Here's a small program to show this:

      (function () {
      'use strict';
      
      const coll = db.test_bf;
      coll.drop();
      
      const aggregationList = [
        [
          {
            $project:{
              "obj.date":null,
              "obj.obj.str":1,
              "obj.num":1
            }
          },
          {
            $setWindowFields:{
              output:{
                obj:{
                  $max:{
                    $mergeObjects:[
                      "$obj"
                    ]
                  }
                }
              }
            }
          }
        ]
      ];
      
      const documentList = [
        {
          "obj":{
            "num":NumberLong("34921"),
            "obj":{
              "num":NumberLong("34922"),
            },
          },
        },
        {
          "obj":{
            "num":NumberLong("34920"),
          },
        },
      ];
      
      assert.commandWorked(coll.insertMany(documentList));
      
      const res = coll.aggregate(aggregationList[0]).toArray();
      print("Result:");
      printjson(res);
      
      }());
      

      Running this program on 5.2.0-rc2@4fab480 produces this output:

      Result:
      [
      	{
      		"_id" : ObjectId("61ce0c951237cda2512da341"),
      		"obj" : {
      			"num" : NumberLong(34921),
      			"date" : null,
      			"obj" : {
      				
      			}
      		}
      	},
      	{
      		"_id" : ObjectId("61ce0c951237cda2512da342"),
      		"obj" : {
      			"num" : NumberLong(34920),
      			"date" : null
      		}
      	}
      ]
      

      As you observe, an empty object is produced in the first result. Now, if we run this program instead (which adds a $setWindowFields at the end of pipeline):

      (function () {
      'use strict';
      
      const coll = db.test_bf;
      coll.drop();
      
      const aggregationList = [
        [
          {
            $project:{
              "obj.date":null,
              "obj.obj.str":1,
              "obj.num":1
            }
          },
          {
            $setWindowFields:{
              output:{
                obj:{
                  $max:{
                    $mergeObjects:[
                      "$obj"
                    ]
                  }
                }
              }
            }
          }
        ]
      ];
      
      const documentList = [
        {
          "obj":{
            "num":NumberLong("34921"),
            "obj":{
              "num":NumberLong("34922"),
            },
          },
        },
        {
          "obj":{
            "num":NumberLong("34920"),
          },
        },
      ];
      
      assert.commandWorked(coll.insertMany(documentList));
      
      const res = coll.aggregate(aggregationList[0]).toArray();
      print("Result:");
      printjson(res);
      
      }());
      
      

      Running the program produces the following error:

      uncaught exception: Error: command failed: {
      	"ok" : 0,
      	"errmsg" : "PlanExecutor error during aggregation :: caused by :: an empty object is not a valid value. Found empty object at path obj.obj >>> {}",
      	"code" : 40180,
      	"codeName" : "Location40180"
      } with original command request: {
      	"aggregate" : "test_bf",
      	"pipeline" : [
      		{
      			"$project" : {
      				"obj.date" : null,
      				"obj.obj.str" : 1,
      				"obj.num" : 1
      			}
      		},
      		{
      			"$setWindowFields" : {
      				"output" : {
      					"obj" : {
      						"$max" : {
      							"$mergeObjects" : [
      								"$obj"
      							]
      						}
      					}
      				}
      			}
      		}
      	],
      	"cursor" : {
      		
      	},
      	"lsid" : {
      		"id" : UUID("bf0b9111-f13b-44ed-8d3e-f3784c5a3df8")
      	}
      }
      

      2- Remove these assertions and fix the rest of the code that depends on this assumption (i.e., there's no empty object in the input).

      IMO, with enough caution, taking the second option makes more sense.

      anton.korshunov as it seems that you have added these assertions, it'd be great if you could advise on the best way to deal with this issue.

            Assignee:
            nicholas.zolnierz@mongodb.com Nicholas Zolnierz
            Reporter:
            mohammad.dashti@mongodb.com Mohammad Dashti (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            9 Start watching this issue

              Created:
              Updated:
              Resolved: