mongodbgomongo-gomongo-go-driver

How to replace a document in MongoDB with Go driver?


I'm trying to update a document in MongoDB with mongodb/mongo-go-driver. From its doc, one document can be replaced with:

var coll *mongo.Collection
var id primitive.ObjectID

// find the document for which the _id field matches id and add a field called "location"
// specify the Upsert option to insert a new document if a document matching the filter isn't found
opts := options.Replace().SetUpsert(true)
filter := bson.D{{"_id", id}}
replacement := bson.D{{"location", "NYC"}}
result, err := coll.ReplaceOne(context.TODO(), filter, replacement, opts)
if err != nil {
    log.Fatal(err)
}

if result.MatchedCount != 0 {
    fmt.Println("matched and replaced an existing document")
    return
}
if result.UpsertedCount != 0 {
    fmt.Printf("inserted a new document with ID %v\n", result.UpsertedID)
}

But what if I have fields more than like 20. I am wondering if I can update without re-writing all fields again, I tried something like this:

// Replacement struct
type Input struct {
    Name        *string `json:"name,omitempty" bson:"name,omitempty" validate:"required"`
    Description *string `json:"description,omitempty" bson:"description,omitempty"`
    Location    *string `json:"location,omitempty" bson:"location,omitempty"`
}

... 
oid, _ := primitive.ObjectIDFromHex(id) // because 'id' from request is string
filter := bson.M{"_id", oid} // throws `[compiler] [E] missing key in map literal`
//                      ^^^
replacement := bson.D{{"$set", input}} // throws `composite literal uses unkeyed fields`
//                             ^^^^^
result, err := coll.ReplaceOne(context.TODO(), filter, replacement, opts)
...

But it throws errors at filter and replacement. How can I replace a whole document properly?


Solution

  • bson.M is a map, so if you use it, you have to write: bson.M{"_id": oid}. See missing type in composite literal go AND missing key in map literal go.

    And bson.D is a slice of structs, so if you use it, you should write bson.D{{Key:"$set", Value: input}}. Omitting the field names is not a compiler error, it's just a go vet warning.

    Now on to replacing. The replacement must be a document itself, without using $set (this is not updating but replacing). For reference see MongoDB's collection.replaceOne() and the driver's doc: Collection.ReplaceOne():

    The replacement parameter must be a document that will be used to replace the selected document. It cannot be nil and cannot contain any update operators (https://docs.mongodb.com/manual/reference/operator/update/).

    So do it like this:

    filter := bson.M{"_id": oid}
    result, err := coll.ReplaceOne(context.TODO(), filter, input, opts)