Go - How to properly upsert with UpdateOne/ReplaceOne

In the case of a new user with the following struct

type User struct {
  Id primitive.ObjectId `bson:"_id"`
  Name string `bson:"name"`
}

and using

user := &User{
Name: "John",
}
opts := options.FindOneAndReplace().SetUpsert(true).SetReturnDocument(options.After)
results, err := collection.FindOneAndReplace(context.Background(), bson.D{{ "_id", user.Id }}, user, opts)

Upsert works but it creates a document with primitive.Object ID ObjectId(“000000000000”) instead of generating a ID.

If we were to change the struct to use the pointer instead.

type User struct {
  Id *primitive.ObjectId `bson:"_id"`
  Name string `bson:"name"`
}

It also doesn’t generate a new _id, instead it upserts with _id: null even though *primitive.ObjectId is nil. I was expecting it to generate a new object ID when it’s

What’s the solution to this, other than generating the objectid manually at the application side?

Try including the omitempty BSON directive on the User.Id field. That should prevent a “zero value” ObjectID from being written to the BSON document, prompting the Go Driver to automatically add a random _id to the document before sending it to the database.

For example:

type User struct {
	Id   primitive.ObjectID `bson:"_id,omitempty"`
	Name string             `bson:"name"`
}
1 Like