Golang mgo findAndModify auto increment id tutorial

17 May, 2016

Here is a brief explanation for the 'findAndModify()' equivalent for mgo. Instead of 'findAndModify()' we will use 'mgo.Find().Apply()'. I will demonstrate how to use auto incremented ids instead of random unique generated IDs for documents, since mongdb does not have a native way of doing this (because MongoDB is not a relational database).

It work's like this, we create a collection, insert a document in that collection that contains the last generated ID. Every time we call 'findAndModify()', mongoDB will increment the value and we get that new value back, which you should use to insert a new document with.

Create a collection and document to contain your counter value

In our example we are going to use auto increment for our collection "persons", so create this collection and insert the following document, in the mongo client:

db.persons.insert{"_id": "counterForPersons", "counterValue": 0}

mgo equivalent

First of import the 'mgo' and 'bson' packages, create your connection to your MongoDB, and select your collection.

package main
import (
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

func main() {
	session, _:= mgo.Dial("localhost")
	defer session.Close()
	collection := session.DB("test").C("persons")
	//Add rest of the code here
}

Then we will create a  mgo 'Change' type, this is the modify part in 'findAndModify'.

changeInDocument := mgo.Change{
	Update:    bson.M{"$inc": bson.M{"counterValue": 1}},
	ReturnNew: true,
}

The function says "Increment 'counterValue' with 1, return the new value'.

Also we will create a variable to store the new returned value in. This variable has the 'bson.M' type.

var result bson.M

Now we will find the document we created earlier and apply the modification to it.

_, err :=collection.Find(bson.M{"_id": "counterForPersons"}).Apply(changeInDocument, &result)

The newly returned ID is stored in the result under 'counterValue'. You can access it like this.

result["counterValue"]

And this is the value you should use to insert a new document with.

Full code

package main
import (
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

func main() {
	//Connect to your MongoDB host
	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}	
	defer session.Close()

	//Select your collection in your database
	collection := session.DB("test").C("persons")
//Create a bson.M interface to store result to var result bson.M //mongodb findAndModify() equivalent //This is to tell what field and how to modify it changeInDocument := mgo.Change{ Update: bson.M{"$inc": bson.M{"counterValue": 1}}, ReturnNew: true, } //Find the document that keeps the counter value, and apply the modification _, err :=collection.Find(bson.M{"_id": "counterForPersons"}).Apply(changeInDocument, &result) //Handle error if err != nil { panic(err) } newID := result["counterValue"] //Insert a document with the new id! }

Why let MongoDB handle this increment?

You could indeed just get the document with the highest ID, increment it yourself to use in a new document. But this method will not guarantee it's uniqueness. Imagine if you do it like that multiple times at the same time, you may end up with colliding IDs, and since you are using incremented IDs for a reason (relations?), this will cause very unwanted behavior. 

So when MongoDB manages the increment, you will (most likely) always get a unique ID.