Context
Whenever a new document is created without an embedded object the update with the $set operator does not generate the _id. For this discussion I’ve used the nodejs driver. Consider the following JSON as an example of the bike model
{
"businessId": "Bike1",
"name": "Bert",
"properties": {
"type": "Electric",
"weight": "1337mg"
}
}
Suppose I create a new record without specifying the properties like:
{
"businessId": "Bike2",
"name": "Ernie",
}
This results in a new record.
{
"_id": "6453cb0914251323e1b9f40e",
"businessId": "Bike2",
"name": "Ernie",
"__v": 0
}
At this point the properties are not known. Later on the properties object might be added to the document. Now I want to add the type and weight of Bike2. For this I use a findOneAndUpdate.
const doc = await bikeModel.findOneAndUpdate({ businessId: businessid }, { $set: { 'properties.type': 'Gas', 'properties.weight': '1kg' } }, { returnDocument: "after", runValidators: true });
This findOneAndUpdate results in:
{
"_id": "6453cb0914251323e1b9f40e",
"businessId": "Bike2",
"name": "Ernie",
"__v": 0,
"properties": {
"type": "Gas",
"weight": "1kg",
"_id": "6453d38e14251323e1b9f415"
}
}
Note that the returnDocument contains an _id in the properties. Whenever I retrieve this record the _id does not exist on my document. Neither does the _id exist in the when viewing this record in Compass. See below the retrieved record
{
"items": [
{
"_id": "6453cb0914251323e1b9f40e",
"businessId": "Bike2",
"name": "Ernie",
"__v": 0,
"properties": {
"type": "Gas",
"weight": "1kg"
}
}
],
"size": 1,
"hasMore": false
}
Whenever I add the properties to the initial creation of the record it does work. Instead of the previous create I now add an empty object. Suppose I create a new record without specifying the properties like:
{
"businessId": "Bike2",
"name": "Ernie",
"properties": { }
}
This results in a record with _id on the properties object. With this situation the $set operation works as expected. It results in the creation of a type and weight on the existing properties object.
{
"_id": "6453cb0914251323e1b9f40e",
"businessId": "Bike2",
"name": "Ernie",
"properties": {
"_id": "6454bfdd9bb9cb2afd60b6e0"
},
"__v": 0
}
According to the $set documentation:
“If the field does not exist, $set
will add a new field with the specified value, provided that the new field does not violate a type constraint. If you specify a dotted path for a non-existent field, $set
will create the embedded documents as needed to fulfill the dotted path to the field.”
Questions
To me it’s unclear why the $set operator is not able to generate a properties object with _id. Perhaps I misinterpreted the documentation.
- Is this expected behaviour of the $set operator?
- How should I update the properties of a bike document? (possibly without having to define an empty properties object upon creation)
My apologies for the long description.
Thank you in advance.