mgo - MongoDB driver for Go

Web applications alway need a database, and most of the examples we found online always dealing with relational databases, so we are not using sql server here, we try a nosql database, MongoDB & a official mongodb driver - mgo (pronounced as "mango") , which is a rich mongodb driver for Go.

Setting up MongoDB and mgo

a) Download mongodb and install.

if you are using windows, create 2 folders, data and db (C:\data\db, your databases will be store here), then you are done. Start the server using

C:\Program Files\MongoDB\Server\3.0\bin> mongod

b) get mgo

C:\Users\moon>go get gopkg.in/mgo.v2

this will install all 3 packages mgo, mgo/bson, mgo/txn.

c) using mgo

paste the following code into your favorite ide and run (no need browser, just run the code).

package main

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

type Person struct {
        Name string
        Phone string
}

func main() {

        session, err := mgo.Dial("localhost")           // open an connection -> Dial function
        if err != nil {                                 //  if you have a 
                panic(err)
        }
        defer session.Close()                           // session must close at the end

        session.SetMode(mgo.Monotonic, true)            // Optional. Switch the session to a monotonic behavior.

        c := session.DB("moon").C("people")
        err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
                        &Person{"Cla", "+55 53 8402 8510"})
        if err != nil {
                log.Fatal(err)
        }

        result := Person{}
        err = c.Find(bson.M{"name": "Ale"}).One(&result)
        if err != nil {
                log.Fatal(err)
        }

        fmt.Println("Phone:", result.Phone)
}

you no need to do any configurations, it just work ! it set up a database "moon" for you and insert 2 people documents for you,

Notice that _id are added into the people documents automatically although we did not specify in the Person struct.

mgo's connection & session

Usage of the driver revolves around the concept of sessions (these sessions has nothing to do with the web sessions!). To get started, obtain a session using the Dial function (something like the db connection):

session, err := mgo.Dial(url)
  • This will establish one or more connections with the cluster of servers defined by the url parameter.
  • When you dial, you will get back a session which is basically handle the connection pool.
  • This will be the same interface for server , replica set or shard. This means that whether you are connected to a single server, replica set, or connecting to a shard deployment for a several machines, you will be using the same code above. The driver behind the scenes will be trying to figure out what's the actual servers that are part of that deployment (drivers discover and maintains topology) and if there is a failover, it will error out to let you know there is a error in your statement, and then it will try again , all this are happened behind the scenes.
  • For a cluster of servers, we do it this way :
      session, err := mgo.Dial("server1.example.com,server2.example.com")
    
    Check the Dial(..) api for more detail.

From then on, the cluster may be queried with multiple consistency rules (see SetMode) and documents retrieved with statements such as:

c := session.DB(database).C(collection)
err := c.Find(query).One(&result)

New sessions are typically created by calling session. Copy on the initial session obtained at dial time.

func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
    session := s.session.Copy()              // s - the initial session       
    defer session.Close()

    // Handle request
}

These new sessions will share the same cluster information and connection cache, and may be easily handed into other methods and functions for organizing logic. Every session created must have its Close method called at the end of its life time, so its resources may be put back in the pool or collected, depending on the case.

mgo's CRUD

a) Create

func (c *Collection) Insert(docs ...interface{}) error

// Insert(..) inserts one or more documents in the respective collection. In case the session  
// is in safe mode (see the SetSafe method) and an error happens while inserting the provided
// documents, the returned error will be of type *LastError.

Notice that inside the Insert(..) function, there is a "..." & empty interface,

  • empty interface - all types are satisfy the empty interface.
  • "..." means it is a variadic function - a variadic function will take an arbitrary number of ints as arguments.

b) Read

func (c *Collection) Find(query interface{}) *Query

// Find prepares a query using the provided document. The document may be a map or a 
// struct value capable of being marshalled with bson. The map may be a generic one 
// using interface{} for its key and/or values, such as bson.M, or it may be a properly
// typed map. Providing nil as the document is equivalent to providing an empty document 
// such as bson.M{}.

// Further details of the query may be tweaked using the resulting Query value, and then
// executed to retrieve results using methods such as One, For, Iter, or Tail.

// In case the resulting document includes a field named $err or errmsg, which are standard 
// ways for MongoDB to return query errors, the returned err will be set to a *QueryError 
// value including the Err message and the Code. In those cases, the result argument is still
// unmarshalled into with the received document so that any other custom values may be obtained
// if desired.

// Relevant documentation:
// http://www.mongodb.org/display/DOCS/Querying
// http://www.mongodb.org/display/DOCS/Advanced+Queries

c) Update

func (c *Collection) Update(selector interface{}, update interface{}) error

// Update finds a single document matching the provided selector document and modifies it 
// according to the update document. If the session is in safe mode (see SetSafe) a ErrNotFound
// error is returned if a document isn't found, or a value of type *LastError when some other 
// error is detected.

// Relevant documentation:

// http://www.mongodb.org/display/DOCS/Updating
// http://www.mongodb.org/display/DOCS/Atomic+Operations

d) Delete

func (c *Collection) Remove(selector interface{}) error

// Remove finds a single document matching the provided selector document and removes it 
// from the database. If the session is in safe mode (see SetSafe) a ErrNotFound error is 
// returned if a document isn't found, or a value of type *LastError when some other error 
// is detected.

// Relevant documentation:

// http://www.mongodb.org/display/DOCS/Removing

CRUD example :

package main

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

type Person struct {
    Id    bson.ObjectId `bson:"_id,omitempty"`      // see http://godoc.org/labix.org/v2/mgo/bson#Marshal
    Name  string        `bson:"name"`               // define field name in the database if you want 
    Phone string                                    // the name different from the struc, if not define,
}                                                   // it will use lowercase name.

var (
    IsDrop = true
)

func main() {
    session, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer session.Close()

    // optional. switch the session to a monotonic behavior.
    session.SetMode(mgo.Monotonic, true)

    // drop database
    if IsDrop {
        err = session.DB("moon").DropDatabase()
        if err != nil {
            panic(err)
        }
    }

    // collection People
    c := session.DB("moon").C("people")
    result := Person{}

    // Index
    index := mgo.Index{
        Key:        []string{"name", "phone"},
        Unique:     true,
        DropDups:   true,
        Background: true,
        Sparse:     true,
    }

    err = c.EnsureIndex(index)
    if err != nil {
        panic(err)
    }

    count1, err := c.Count()
    fmt.Println("<database drop | total peoples> :", count1)    

    // create
    err = c.Insert(&Person{Name: "Ale", Phone: "+11 11 1111 1111"},
                    &Person{Name: "Ale", Phone: "+22 22 2222 2222"},
                        &Person{Name: "Cla", Phone: "+33 33 3333 3333"})
    if err != nil {
        log.Fatal(err)
    }

    count2, err := c.Count()
    fmt.Println("<database created | insert 3 peoples | total peoples> :", count2)    

    // query one
    err = c.Find(bson.M{"name": "Ale"}).One(&result)        // see http://godoc.org/gopkg.in/mgo.v2#Collection.Find
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("<query one | name: Ale> Phone :", result.Phone)

    // query all [a]
    var results []Person
    err = c.Find(bson.M{"name": "Ale"}).Sort("-name").All(&results)    // see http://godoc.org/gopkg.in/mgo.v2#Query.All
     if err != nil {
        panic(err)
    }

    fmt.Println()
    fmt.Println("<query all [a] | results>")
    fmt.Println(results)

    // query all [b]
    iter := c.Find(nil).Limit(3).Iter()
    fmt.Println()
    fmt.Println("<query all [b] | results>")
    for iter.Next(&result) {
        fmt.Printf("results: %v\n", result.Name + " , " + result.Phone)
    }
    err = iter.Close()
    if err != nil {
        log.Fatal(err)
    }

    // update
    selector := bson.M{"name": "Cla"}
    updator := bson.M{"$set": bson.M{"phone": "+44 44 4444 4444"}}
    err = c.Update(selector, updator)
    if err != nil {
        panic(err)
    }

    err = c.Find(bson.M{"name": "Cla"}).One(&result)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println()
    fmt.Println("<updated | name : Ale> Phone :", result.Phone)

    // remove
    err = c.Remove(bson.M{"_id": result.Id})
    if err != nil {
        log.Fatal(err)
    }

    count3, err := c.Count()
    fmt.Println("<total peoples after delete> :", count3)    
}

[output]
<database drop | total peoples> : 0
<database created | insert 3 peoples | total peoples> : 3
<query one | name: Ale> Phone : +11 11 1111 1111

<query all [a] | results>
[{ObjectIdHex("555c6cc2598d31903f07f60d") Ale +22 22 2222 2222} {ObjectIdHex("555c6cc2598d31903f07f60c") Ale +11 11 1111 1111}]

<query all [b] | results>
results: Ale , +11 11 1111 1111
results: Ale , +22 22 2222 2222
results: Cla , +33 33 3333 3333

<updated | name : Ale> Phone : +44 44 4444 4444
<total peoples after delete> : 2

For more details, see the documentation for the types and methods,

Ref :

  1. mgo driver
  2. mgo video tutorial
  3. Running MongoDB Queries Concurrently With Go
  4. Using MongoDB With Go And Mgo
  5. A Simple Go Web App on Heroku with MongoDB on MongoHQ
  6. Moving MongoDB components to Go
  7. Building an Analytics Engine Using MongoDB and Go
  8. Building RESTful Services With Go and MongoDB

results matching ""

    No results matching ""