MongoDB Go Driver Tutorial

Published on December 20, 2018

MongoDB Go Driver Tutorial

Good news! The official go driver for mongoDB is now in beta. After some thought, I decided to translate the article from the official mongoDB site instead of writing the material myself. Here is what will happen in this tutorial:


  • Install mongo-go-driver
  • Connecting to mongoDB with mongo-go-driver
  • Using BSON Objects
  • Using CRUD methods
    image
    The source code for this tutorial is in [this GitHub repository] (https://github.com/tfogo/mongodb-go-tutorial). To work with this guide, you must have version 3.2 MongoDB installed and Golang 1.10 or later installed.

Install mongo-go-driver


The driver consists of several packages.
You can install the driver with go get :


go get github.com/mongodb/mongo-go-driver

The output may contain the following warning:


package github.com/mongodb/mongo-go-driver: no Go files in (...)


This is the expected result.


approx. translator: I personally use modules and everything is fine there.


If you are using dep as a manager for dependency management , then you have to install the main mongo package , also the bson package and mongo / options package :


dep ensure --add github.com/mongodb/mongo-go-driver/mongo \
github.com/mongodb/mongo-go-driver/bson \
github.com/mongodb/mongo-go-driver/mongo/options

Laying the foundation


Create a project and add a main.go file with the following content:


package main
import (
    "context"
    "fmt"
    "log"
    "github.com/mongodb/mongo-go-driver/bson"
    "github.com/mongodb/mongo-go-driver/mongo"
    "github.com/mongodb/mongo-go-driver/mongo/options"
)
// You will be using this Trainer type later in the program
type Trainer struct {
    Name string
    Age  int
    City string
}
func main() {
    // Rest of the code will go here
}

In this code, we import mongo-go-driver, some standard libraries, and define the type Trainer.


Connecting to mongoDB with mongo-go-driver


After we imported mongo-go-driver, we had the opportunity to create a connection with mongoDB using the mongo.Connect () function . We have the opportunity to pass into this function the context arguments and the string containing the connection address to mongodb as arguments. If desired, we can also pass in as the third argument an options.ClientOptions object to fine-tune the driver options. The documentation describes in detail the available parmeters.


Let's add the following code to the body of the main function:


client, err := mongo.Connect(context.TODO(), "mongodb://localhost:27017")
if err != nil {
    log.Fatal(err)
}
// Check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Connected to MongoDB!")

After successfully connecting to mongoDB, we can access the trainers collection , which is in the database called test by adding the following code to the end of the main function:


collection := client.Database("test").Collection("trainers")

It is recommended to keep the connection with mongoDB open so that you do not have to open and close the connection for each request. However, if the application no longer requires a connection, it can be closed using the client.Disconnect () function as follows:


err = client.Disconnect(context.TODO())
if err != nil {
    log.Fatal(err)
}
fmt.Println("Connection to MongoDB closed.")

Comment out the unused bson and mongo / options packages .


Run our code ( go run main.go ) to check the connection to the mongoDB server.


Using BSON facilities


JSON documents in mongoDB are stored in a binary format called BSON. Unlike other databases in which JSON data is stored as strings and numbers, BSON encoding adds new types such as int , long , date , float and decimal128 .


This greatly simplifies the processing, sorting and comparison of data by applications. The Go driver has two types of families to represent BSON data: Types D and RAW types.


Family D consists of four types:


  • D : BSON document. This type should be used in situations where the order matters, for example, MongoDB commands.
  • M : Unordered dictionary (associative array, map). It is the same as D , except that it does not preserve order.
  • A : BSON array.
  • E : single element inside d .

Here is an example of a filter built using D-types that searches for documents in which the name field corresponds to Alice or Bob values :


The Raw type family is used to check byte slices. You can extract single elements from Raw types using Lookup () .


This can be useful when you need to get rid of the extra load when converting BSON to another type. In this tutorial, only the D type family will be used.


Using CRUD methods


After successfully connecting to the database, we can begin to add and modify data in our collection. The Collection type contains methods that allow you to send requests to the database.


Insert (create) documents


First, you need to create several new Trainer structures to insert into the database:


ash := Trainer{"Ash", 10, "Pallet Town"}
misty := Trainer{"Misty", 10, "Cerulean City"}
brock := Trainer{"Brock", 15, "Pewter City"}

In order to pop a single document, use the collection.InsertOne () method :


insertResult, err := collection.InsertOne(context.TODO(), ash)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Inserted a single document: ", insertResult.InsertedID)

To insert multiple documents, the collection.InsertMany () method simultaneously exists :


trainers := []interface{}{misty, brock}
insertManyResult, err := collection.InsertMany(context.TODO(), trainers)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs)

Document update


The collection.UpdateOne () method allows you to update a single document. You need to create a filter to search for a document in the database and a document for the update operation. You can create them using bson.D types :


filter := bson.D{{"name", "Ash"}}
update := bson.D{
    {"$inc", bson.D{
        {"age", 1},
    }},
}

The following code will find a document in which the name field coincides with the value of Ash and increases the value of age by 1.


updateResult, err := collection.UpdateOne(context.TODO(), filter, update)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Matched %v documents and updated %v documents.\n", updateResult.MatchedCount, updateResult.ModifiedCount)

Document search


To find a document, you will need a filter, as well as a pointer to a variable into which the result can be decoded.


To find a single document, use collection.FindOne () . This method returns a single value that can be decoded into a variable.


We will use the same filter variable that was used in the update request.


// create a value into which the result can be decoded
var result Trainer
err = collection.FindOne(context.TODO(), filter).Decode(&result)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)

To find multiple documents, use collection.Find () .


This method returns a Cursor . Cursor provides a stream of documents with which you can loop through and decode one document at a time.


When the Cursor documents are exhausted, the Cursor should be closed . Also Cursor can be fine-tuned using the options package .


In our example, we set a limit on the issuance of two documents.


// Pass these options to the Find method
options := options.Find()
options.SetLimit(2)
// Here's an array in which you can store the decoded documents
var results []*Trainer
// Passing nil as the filter matches all documents in the collection
cur, err := collection.Find(context.TODO(), nil, options)
if err != nil {
    log.Fatal(err)
}
// Finding multiple documents returns a cursor
// Iterating through the cursor allows us to decode documents one at a time
for cur.Next(context.TODO()) {
    // create a value into which the single document can be decoded
    var elem Trainer
    err := cur.Decode(&elem)
    if err != nil {
        log.Fatal(err)
    }
    results = append(results, &elem)
}
if err := cur.Err(); err != nil {
    log.Fatal(err)
}
// Close the cursor once finished
cur.Close(context.TODO())
fmt.Printf("Found multiple documents (array of pointers): %+v\n", results)

Deleting documents


You can delete documents using collection.DeleteOne () or collection.DeleteMany () .
We pass nil as a filter argument that will match all the documents in the collection. You can also use collection.Drop () to remove the entire collection.


deleteResult, err := collection.DeleteMany(context.TODO(), nil)
if err := cur.Err(); err != nil {
    log.Fatal(err)
}
fmt.Printf("Deleted %v documents in the trainers collection\n", deleteResult.DeletedCount)

The furthest steps


→ The final code for this tutorial is in the GitHub repository
→ Driver documentation is available on GoDoc


If you have any questions, please contact us in the Google mongo-go-driver group .
Please submit bug reports to MongoDB JIRA .
We will be happy to receive your feedback on Go Driver.