Consider the following 2 structs:
type Struct1 struct {
foo string `bson:"foo,omitempty"`
bar string `bson:"bar,omitempty"`
Struct2 *struct2 `bson:"struct2,omitempty"`
}
type Struct2 struct {
foo string `bson:"foo,omitempty"`
bar string `bson:"bar,omitempty"`
}
Using FindOneAndUpdate and only setting values for foo in the parent and foo in the nested struct has the following behavior. It retains the existing value for the struct1 bar in the db document, great, but deletes the bar value nested in the struct2 object, not great. Is this by design?
If I add:
Struct2 *struct2 `bson:"struct2,omitempty,inline"`
It works fine, but I lose the structure of my document in Mongo which is not ideal.
EDIT -- Adding minimum reproducable example:
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// Connect to mongo
mongoClient, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://***:***@localhost:27017/test?connect=direct"))
if err != nil {
return
}
db := mongoClient.Database("test")
coll := db.Collection("test")
// Insert example record
id, err := coll.InsertOne(context.TODO(), bson.D{
{Key: "foo", Value: "foo_value"},
{Key: "bar", Value: "bar_value"},
{Key: "nestedObject", Value: bson.D{
{Key: "foo", Value: "foo_value"},
{Key: "bar", Value: "bar_value"},
},
}})
if err != nil {
fmt.Printf("Error: %v", err)
}
// Grab example record and print
result := &bson.D{}
coll.FindOne(context.TODO(), bson.M{"_id": id.InsertedID}).Decode(result)
fmt.Printf("Initial DB record -> %v\n", result)
// Update record, return SingleResult after and print
aft := options.After
opt := options.FindOneAndUpdateOptions{
ReturnDocument: &aft,
}
// Just changing foo, not bar
newResult := &bson.D{}
if err := coll.FindOneAndUpdate(context.TODO(), bson.M{"_id": id.InsertedID}, bson.D{{Key: "$set", Value: bson.D{
{Key: "foo", Value: "foo_changed"},
{Key: "nestedObject", Value: bson.D{
{Key: "foo", Value: "foo_changed"},
},
}}}}, &opt).Decode(newResult); err != nil {
fmt.Printf("Error: %v", err)
}
fmt.Printf("Updated DB record -> %v\n", newResult)
}
Outputs the following, where did my nestedObject.bar go??? (note the non nested one is still there):
Initial DB record -> &[{_id ObjectID("65d8f675a5418eef83425156")} {foo foo_value} {bar bar_value} {nestedObject [{foo foo_value} {bar bar_value}]}]
Updated DB record -> &[{_id ObjectID("65d8f675a5418eef83425156")} {foo foo_changed} {bar bar_value} {nestedObject [{foo foo_changed}]}]
This explains why using ",inline" on the bson works fine, because the bson.D is flattened before updating.
The question is, is it possible to retain the nested structure and have mongo only update the provided values?
Your update operation sets the
nestedObject
field to an object having a singlefoo
property. That's why itsbar
field is gone.If you just want to change
foo
inside the nested objectnestedObject
, use the "dot" notation: