I'm working with https://github.com/mongodb/mongo-go-driver and currently trying to implement a partial update of such struct
type NoteUpdate struct { ID string `json:"id,omitempty" bson:"_id,omitempty"` Title string `json:"title" bson:"title,omitempty"` Content string `json:"content" bson:"content,omitempty"` ChangedAt int64 `json:"changed_at" bson:"changed_at"` }
For instance, if I have
noteUpdate := NoteUpdate{ Title: "New Title" }
Then I expect that the only "title" field in the stored document will be changed.
I need to write something like
collection.FindOneAndUpdate(context.Background(), bson.NewDocument(bson.EC.String("_id", noteUpdate.ID)), // I need to encode non-empty fields here bson.NewDocument(bson.EC.SubDocument("$set", bson.NewDocument(...))) )
The problem is that I don't want to manually encode each non-empty field with bson.EC.String(...)
or bson.EC.Int64(...)
. I tried to use bson.EC.InterfaceErr(...)
but got an error
Cannot create element for type *models.NoteUpdate, try using bsoncodec.ConstructElementErr
Unfortunately, there is no such function in bsoncodec. The only way I found is to create wrapper
type SetWrapper struct { Set interface{} `bson:"$set,omitempty"` }
And use it like
partialUpdate := &NoteUpdate{ ID: "some-note-id", Title: "Some new title", } updateParam := SetWrapper{Set: partialUpdate} collection.FindOneAndUpdate( context.Background(), bson.NewDocument(bson.EC.String("_id", noteUpdate.ID)), updateParam, )
It works, but is it possible to achieve the same with bson/bsoncodec document builders ?
UPD. The full context of my question: I wrote the REST endpoint for partially updating "Note" documents(stored in MongoDB). Code that I have now:
var noteUpdate models.NoteUpdate ctx.BindJSON(¬eUpdate) //omit validation and errors handling updateParams := services.SetWrapper{Set: noteUpdate} res := collection.FindOneAndUpdate( context.Background(), bson.NewDocument(bson.EC.String("_id", noteUpdate.ID)), updateParams, findopt.OptReturnDocument(option.After), )
Code that I want to have
var noteUpdate models.NoteUpdate ctx.BindJSON(¬eUpdate) //omit validation and errors handling res := collection.FindOneAndUpdate( context.Background(), bson.NewDocument(bson.EC.String("_id", noteUpdate.ID)), bson.NewDocument( //bsoncodec.ConstructElement doesn't exists bsoncodec.ConstructElement("$set", ¬eUpdate)), ), findopt.OptReturnDocument(option.After), )
Code that I don't want to have
var noteUpdate models.NoteUpdate ctx.BindJSON(¬eUpdate) //omit validation and errors handling bsonNote := bson.NewDocument() if noteUpdate.Title != "" { bsonNote.Append(bson.EC.String("title", noteUpdate.Title)) } if noteUpdate.Content != "" { bsonNote.Append(bson.EC.String("content", noteUpdate.Content)) } //..setting the rest of the fields... res := collection.FindOneAndUpdate( context.Background(), bson.NewDocument(bson.EC.String("_id", noteUpdate.ID)), bson.NewDocument(bson.EC.SubDocument("$set", bsonNote)), findopt.OptReturnDocument(option.After), )
So, the precise question is - is there any way to build *bson.Document dynamically based on bson
tags(without predefined wrappers like my SetWrapper)?