Christian Giacomi

An attempt at a small developer blog

Mongo Go Driver findOneAndUpdate

Posted at — Sep 25, 2019   Category — software 

When I started to develop in Go I found that one of the things that sometimes is missing is the multitude of examples on the net like you would find with NodeJS. I am not saying that Go is not popular, all I am saying is that sometimes I wish there were more examples out there. This is especially true for the mongo-go-driver. That is why I decided to write this post.

This is a very short post just to show you how to use the findOneAndUpdate function from the official mongodb driver for Go.

mongo-go-driver

The mongo-go-driver is the official driver for mongo for Go. It is developed by the mongodb team and is what you would use today to perform any CRUD operations on mongodb from your Go application.

There are several really good blog posts out there, especially the ones from the mongodb team. Here are a few just in case you didn’t come across them yourself. This is the getting started with Go tutorial and it’s extremely good.

Also if you have been using a community driver like mgo here is a post regarding migration steps to the official mongo driver.

And finally here is the official Go Doc for the driver.

Find One And Update

All the examples and tutorials I found seem to focus solely on the default and most commonly used functions in the driver.

In the past when using mongodb with NodeJS I made heavy use of Mongoose.js and usually in my REST API’s I like to return any newly created resources to facilitate the client, thus avoiding that a client has to perform a subsequent GET request after updating a resource.

To do so I make use of the findOneAndUpdate function when I need to update a resource, and so it is natural for me to do so even when using Go. Unfortunately none of the tutorials out there show you how to use this function.

Below is a very simple example to illustrate how to use FindOneAndUpdate using the mongo-go-driver.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func findOneAndUpdate(url string, db string, coll string) (bson.M, error) {

	// 1) Create the context
	exp := 5*time.Second
	ctx, cancel := context.WithTimeout(context.Background(), exp)
	defer cancel()

	// 2) Create the connection
	conn, err := mongo.Connect(ctx, options.Client().ApplyURI(url))
	if err != nil {
		log.Fatal(err.Error())
	}

	// 2) Select the database
	database := conn.Database(db)

	// 4) Select the collection
	collection := database.Collection(coll)

	// 5) Create the search filter
	filter := bson.M{"name": "luke"}

	// 6) Create the update
	update := bson.M{
		"$set": bson.M{"lastname": "skywalker"},
	}

	// 7) Create an instance of an options and set the desired options
	upsert := true
	after := options.After
	opt := options.FindOneAndUpdateOptions{
		ReturnDocument: &after,
		Upsert:         &upsert,
	}

	// 8) Find one result and update it
	result := collection.FindOneAndUpdate(ctx, filter, update, &opt)
	if result.Err() != nil {
		return nil, result.Err()
	}

	// 9) Decode the result
	doc := bson.M{}
	decodeErr := result.Decode(&doc)

	return doc, decodeErr
}

And you can call the function like so

package main

func main() {
	url := "mongodburl"
	db := "jedi"
	coll := "characters"

	doc, err := findOneAndUpdate(url, db, coll)
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
	}

	fmt.Printf("%+v", doc)
}

The FindOneAndUpdateOptions struct allows you to choose several different options.

As you can see from the code above I have set the options to return document after the update and to run an upsert in case the document is not found in the collection.

For more information about the possible options feel free to browse the the mongo options package. You will see that the FindOneAndUpdateOptions struct is defined as such.

// From the official mongo-go-driver source code.
type FindOneAndUpdateOptions struct {
	ArrayFilters             *ArrayFilters
	BypassDocumentValidation *bool
	Collation                *Collation
	MaxTime                  *time.Duration
	Projection               interface{}
	ReturnDocument           *ReturnDocument
	Sort                     interface{}
	Upsert                   *bool
}

Here are the links to the official mongo-go-driver source code and relative documentation

Also, here is the link to a working example. Please feel free to test the code.

If this post was helpful tweet it or share it.