How to connect Go with MongoDB using official mongodb-go-driver!
Kartik Budhiraja
Posted on October 28, 2019
On December 13, 2018 MongoDB released its official Go Driver into beta, ready for the wider Go and MongoDB community to put it into test.🤩 MongoDB GO driver is pretty easy and quick to set up and is still WIP but is improving functionalities and features day by day. This tutorial is based on the official tutorial released by MongoDB, but I tried to make it as simple as possible along with a working reference.
TLDR: The source code can be found here with CRUD operations
In this tutorial, we will cover:
- Install the MongoDB Go Driver
- Connect the MongoDB using the Go Driver
- Use of BSON Objects in Go
- Perform CRUD Operations
- Installing the MongoDB Go Driver
The MongoDB Go Driver is made up of several packages. If you are just using go get, you can install the driver using:
go get github.com/mongodb/mongo-go-driver
The output of this may look like a warning stating something like package github.com/mongodb/mongo-go-driver: no Go files in (...). This is an expected output.
If you are using govendor package manager, you need to install the main mongo package as well as the bson and mongo/options package using the following commands:
govendor fetch github.com/mongodb/mongo-go-driver/mongo
govendor fetch go.mongodb.org/mongo-driver/bson
govendor fetch go.mongodb.org/mongo-driver/mongo/options
All of the required packages should get installed, now let's build up a connection to Mongo.
2. Setting up the connection
Once the MongoDB Go Driver has been imported, we can connect to a MongoDB deployment using the Client.Connect(context).We need to set up a new client, we need to pass the URI of our mongo database while setting up the client. Then we just need to call client.Connect(context) with the context, which will set up our connection.
The following code sets up a connection for us:
For keeping it simple, I would only be providing the code snippets of the functions being used, you may find the complete source code here.
//Set up a context required by mongo.Connect
ctx, cancel := context.WithTimeout(context.Background(), 10\*time.Second)
//To close the connection at the end
defer cancel()
//We need to set up a client first
//It takes the URI of your database
client, error := mongo.NewClient(options.Client().ApplyURI("your\_database\_uri"))
if error != nil {
log.Fatal(err)
}
//Call the connect function of client
error = client.Connect(ctx)
//Checking the connection
error = client.Ping(context.TODO(), nil)
fmt.Println("Database connected")
3. Using BSON Objects in Go
JSON documents in MongoDB are stored in a binary representation called BSON (Binary-encoded JSON). Unlike other databases that store JSON data as simple strings and numbers, the BSON encoding extends the JSON representation to include additional types such as int, long, date, floating point, and decimal128. This makes it much easier for applications to reliably process, sort, and compare data. The Go Driver has two families of types for representing BSON data: The D types and the Raw types.
The D family of types is used to concisely build BSON objects using native Go types. This can be particularly useful for constructing commands passed to MongoDB. The D family consists of four types:
- D: A BSON document. This type should be used in situations where order matters, such as MongoDB commands.
- M: An unordered map. It is the same as D, except it does not preserve order.
- A: A BSON array.
- E: A single element inside a D.
Here is an example of a filter document built using D types which may be used to find documents where the name field matches either Alice or Bob:
bson.D{{
"name",
bson.D{{
"$in",
bson.A{"Alice", "Bob"}
}}
}}
4. CRUD Operations
For CRUD and other operations, we need to use the collection object, which we can create by referencing to our respective collection in our database like:
BooksCollection := client.Database("test").Collection("books")
- Insert
For create, we can use collection.InsertOne() for individual entry or could use collection.InsertMany() which takes in the slice of objects.
/\*\*
\* Create - Adding a new book
\* res -\> the insert command returns the inserted id of the oject
\*/
res, err := BooksCollection.InsertOne(ctx, bson.M{"name": "The Go Language", "genre": "Coding", "authorId": "4"})
if err != nil {
log.Fatal(err)
}
In collection.InsertOne(), we can either pass a string object through bson.M{} or we could create an object of our respective type struct and pass that object.
- Read
For finding documents, we need a filter document as well as a pointer to a value into which result can be decoded. To find a single document, use collection.FindOne(). This method returns a single result which can be decoded into a value. The filter object specifies what are we looking for.
filter := bson.D{{"name", "Book 1"}}
// create a value into which the result can be decoded
var result bookType
err = collection.FindOne(context.TODO(), filter).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single Book: %+v\n", result)
To find multiple documents, use collection.Find(). This method returns a Cursor. A Cursor provides a stream of documents through which we can iterate and decode one at a time. Once a Cursor has been exhausted, we should close the Cursor.
cur, error := BooksCollection.Find(ctx, bson.D{{}})
var allbooks []\*bookType
//Loops over the cursor stream and appends to result array
for cur.Next(context.TODO()) {
var booksResultHolder bookType
err := cur.Decode(&bookResultHolder)
if err != nil {
log.Fatal(err)
}
allbooks = append(allbooks, &booksResultHolder)
}
//dont forget to close the cursor
defer cur.Close(context.TODO())
// Loop over the result array and perform whatever required
for \_, element := range allbooks {
book := \*element
fmt.Println(book)
}
- If you get empty results back, check your fields of type structures, you must export fields of structs, else they are ignored by the mongodb-go-driver package. (Each field should start with Capital letter, specifying go that it could be exported)
type users struct {
User stringbson:"user" json:"user"
Data stringbson:"data" json:"data"
}By default when a struct value is transformed/stored/retrieved from MongoDB, the field name is used. If you want to use different names, you may use tags to tell what names should the fields map to.
- If your documents have automatically generate id’s, at the moment you won’t be able to get those id’s through mongodb-go-server , this is because the id’s are strings wrapped around in custom objects, and at the moment, the driver does not support them. More detailed info can be found here.
- Update
The collection.UpdateOne() method allows you to update a single document. It requires a filter document to match documents in the database and an update document to describe the update operation. These can be built just how we made the filter object while reading.
/\*\*
\* Update
\* Collection has functions like UpdateOne and UpdateMany
\* Returns the Matched and Modified Count
\*/
filter := bson.D{{"name", "Book 1"}}
// Need to specify the mongodb output operator too
newName := bson.D{
{"$set", bson.D{
{"name", "Updated Name of Book 1"},
}},
}
res, err := BooksCollection.UpdateOne(ctx, filter, newName)
if err != nil {
log.Fatal(err)
}
updatedObject := \*res
fmt.Printf("The matched count is : %d, the modified count is : %d", updatedObject.MatchedCount, updatedObject.ModifiedCount)
- Delete
Finally, we can delete documents using collection.DeleteOne() or collection.DeleteMany(). Here we can pass nil as the filter argument, which will match all documents in the collection or any other specific argument. We could also use collection.Drop() to delete an entire collection.
filter = bson.D{{"name", "Updated Name of Book 2"}}
deleteResult, error := BooksCollection.DeleteOne(ctx, filter)
Next Steps
The source code for this tutorial can be found here.
Documentation for the MongoDB Go Driver is available on GoDoc. You may be particularly interested in the documentation about using aggregations or transactions.
Hope this tutorial made things a bit simpler for you, Happy coding everyone!👨🏽💻🍻
Posted on October 28, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.