我正在使用https://github.com/mongodb/mongo-go- driver,目前正在尝试实现这种结构的部分更新
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"` }
例如,如果我有
noteUpdate := NoteUpdate{ Title: "New Title" }
然后,我希望存储文档中唯一的“标题”字段将被更改。
我需要写类似
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(...))) )
问题是我不想用bson.EC.String(...)或手动编码每个非空字段bson.EC.Int64(...)。我尝试使用bson.EC.InterfaceErr(...)但出现错误
bson.EC.String(...)
bson.EC.Int64(...)
bson.EC.InterfaceErr(...)
无法为* models.NoteUpdate类型创建元素,请尝试使用bsoncodec.ConstructElementErr
不幸的是,bsoncodec中没有这样的功能。我发现的唯一方法是创建包装器
type SetWrapper struct { Set interface{} `bson:"$set,omitempty"` }
并像这样使用
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, )
它可以工作,但是使用bson / bsoncodec文档构建器是否可以实现相同的目的?
UPD。我的问题的全部内容:我编写了REST端点,用于 部分 更新“注释”文档(存储在MongoDB中)。我现在拥有的代码:
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), )
我想要的代码
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), )
代码,我 不 希望有
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), )
因此,确切的问题是-是否有任何方法可以基于bson标签动态构建* bson.Document (没有像我的SetWrapper这样的预定义包装器)?
bson
不幸的是,目前不支持此功能。
您可以创建一个辅助函数,将其“转换”为一个bson.Document类似这样的结构值:
bson.Document
func toDoc(v interface{}) (doc *bson.Document, err error) { data, err := bson.Marshal(v) if err != nil { return } err = bson.Unmarshal(data, &doc) return }
然后可以这样使用:
partialUpdate := &NoteUpdate{ Title: "Some new title", } doc, err := toDoc(partialUpdate) // check error res := c.FindOneAndUpdate( context.Background(), bson.NewDocument(bson.EC.String("_id", "some-note-id")), bson.NewDocument(bson.EC.SubDocument("$set", doc)), )
希望ElementConstructor.Interface()将来会有所改进,并允许直接传递结构值或指向结构值的指针。
ElementConstructor.Interface()