Describe the bug
Collection.Update / UpdateWithCtx persists changes with:
UpdateOne(ctx, bson.M{"_id": id}, bson.M{"$set": model})
When the model is marshaled into $set, fields tagged with bson:",omitempty" that hold Go zero values (false, "", 0, nil slices/maps) are omitted from the update document.
Because those keys are missing from $set, MongoDB does not change the existing values. The in-memory model (and any API response built from it) can show the new zero values, while the database still has the previous non-zero values. Callers often see HTTP 200 / success even though persistence failed for those fields.
This is especially painful for:
bool flags cleared to false (e.g. is_primary)
string fields cleared to "" (e.g. optional fallback name)
To Reproduce
- Define a model with clearable fields using
omitempty:
type Template struct {
mgm.DefaultModel `bson:",inline"`
IsPrimary bool `bson:"is_primary,omitempty"`
SecondaryFallback string `bson:"secondary_fallback,omitempty"`
Name string `bson:"name"`
}
- Insert a document with non-zero values:
t := &Template{Name: "welcome", IsPrimary: true, SecondaryFallback: "sms_fallback"}
_ = mgm.Coll(t).CreateWithCtx(ctx, t)
- Load it, clear the fields, and update via mgm:
_ = mgm.Coll(t).FirstWithCtx(ctx, bson.M{"_id": t.ID}, t)
t.IsPrimary = false
t.SecondaryFallback = ""
_ = mgm.Coll(t).UpdateWithCtx(ctx, t) // returns nil
- Reload from MongoDB:
reloaded := &Template{}
_ = mgm.Coll(reloaded).FindByIDWithCtx(ctx, t.ID, reloaded)
fmt.Println(reloaded.IsPrimary, reloaded.SecondaryFallback)
// still true, "sms_fallback" — not false, ""
Expected behavior
After a successful UpdateWithCtx:
is_primary should be false in MongoDB
secondary_fallback should be "" (or explicitly unset, if that is the intended clear semantics)
Zero values that the application set on the model should be persisted, or the library should document clearly that $set: model + omitempty cannot clear fields and recommend ReplaceOne / explicit $set / pointer fields.
Environment (please complete the following information):
- OS: Linux (also reproducible on macOS/Windows)
- Go: 1.22+
- mgm:
github.com/kamva/mgm/v3 (e.g. v3.5.0)
- mongo-driver: compatible with mgm v3
- MongoDB: 6.x / 7.x
Additional context
Relevant implementation (mgm v3):
res, err := c.UpdateOne(ctx, bson.M{field.ID: model.GetID()}, bson.M{"$set": model}, opts...)
Possible directions (for maintainers / discussion):
- Document the pitfall — warn that
Update cannot clear omitempty zero-values; suggest removing omitempty on clearable fields, using pointers (*bool, *string), or ReplaceOne.
- Use
ReplaceOne for Update/UpdateWithCtx — treat the Go model as the full document source of truth (breaking change for apps that rely on leaving unknown Mongo-only fields untouched).
- Helper / option — e.g.
UpdateWithReplace or UpdateOptions{Replace: true} so callers can opt into full-document replace while keeping hook behavior (Updating / Saving / updated_at).
We hit this in production-style admin CRUD: the API returned the cleared values from the in-memory model after UpdateWithCtx, but MongoDB retained the previous values until we removed omitempty on those fields.
Describe the bug
Collection.Update/UpdateWithCtxpersists changes with:When the model is marshaled into
$set, fields tagged withbson:",omitempty"that hold Go zero values (false,"",0,nilslices/maps) are omitted from the update document.Because those keys are missing from
$set, MongoDB does not change the existing values. The in-memory model (and any API response built from it) can show the new zero values, while the database still has the previous non-zero values. Callers often see HTTP 200 / success even though persistence failed for those fields.This is especially painful for:
boolflags cleared tofalse(e.g.is_primary)stringfields cleared to""(e.g. optional fallback name)To Reproduce
omitempty:Expected behavior
After a successful
UpdateWithCtx:is_primaryshould befalsein MongoDBsecondary_fallbackshould be""(or explicitly unset, if that is the intended clear semantics)Zero values that the application set on the model should be persisted, or the library should document clearly that
$set: model+omitemptycannot clear fields and recommendReplaceOne/ explicit$set/ pointer fields.Environment (please complete the following information):
github.com/kamva/mgm/v3(e.g. v3.5.0)Additional context
Relevant implementation (mgm v3):
Possible directions (for maintainers / discussion):
Updatecannot clearomitemptyzero-values; suggest removingomitemptyon clearable fields, using pointers (*bool,*string), orReplaceOne.ReplaceOneforUpdate/UpdateWithCtx— treat the Go model as the full document source of truth (breaking change for apps that rely on leaving unknown Mongo-only fields untouched).UpdateWithReplaceorUpdateOptions{Replace: true}so callers can opt into full-document replace while keeping hook behavior (Updating/Saving/updated_at).We hit this in production-style admin CRUD: the API returned the cleared values from the in-memory model after
UpdateWithCtx, but MongoDB retained the previous values until we removedomitemptyon those fields.