Skip to content

MongoDB

MongoDB is a document database. Its basic data unit is documents, stored in BSON (Binary JSON) format, a structure similar to JSON. The loose structure allows storing different types of data, making it more flexible than relational databases. It also uses JavaScript as a scripting language, allowing script-based compound operations. This article mainly introduces how to use the official MongoDB Go driver to operate MongoDB databases. It is not a MongoDB tutorial; if you don't have MongoDB basics, please learn it first.

MongoDB documentation: Introduction to MongoDB — MongoDB Manual

Driver

There are relatively few libraries for MongoDB in Go. Some community-maintained libraries existed earlier but have since been discontinued. However, the official MongoDB driver is fully sufficient for use.

Open source repository: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)

Documentation: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)

Installation

To download the dependency, use the following:

sh
$ go get go.mongodb.org/mongo-driver/mongo

Connection

Below is a simple example of a MongoDB client establishing a connection with the server.

go
package main

import (
   "context"
   "fmt"
   "go.mongodb.org/mongo-driver/mongo"
   "go.mongodb.org/mongo-driver/mongo/options"
   "go.mongodb.org/mongo-driver/mongo/readpref"
   "log"
)

func main() {
   ctx := context.Background()
   // Establish connection using URI
   client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://admin:123456@192.168.48.138:27017/"))
   if err != nil {
      log.Panicln(err)
   }
   // Close connection
   defer client.Disconnect(ctx)
   // Ping to test if connection is available
   fmt.Println(client.Ping(ctx, readpref.Primary()))
}

BSON

MongoDB in Go uses the following types to map documents in the database, located in bson/bson.go:

go
// Ordered representation of a BSON document
type D = primitive.D

// A key-value pair, the basic unit of an ordered BSON document representation
type E = primitive.E

// Unordered representation of a BSON document
type M = primitive.M

// Ordered representation of BSON data
type A = primitive.A

Their actual types are as follows:

go
// Ordered representation of a BSON document
type D []E

// A key-value pair, the basic unit of an ordered BSON document representation
type E struct {
  Key   string
  Value interface{}
}

// Unordered representation of a BSON document
type M map[string]interface{}

// Ordered representation of BSON data
type A []interface{}

With these types, you can construct query SQL or map data.

TIP

The examples directory in the driver contains quite a few usage examples. The official documentation demonstrates in detail how to use the above four types.

Link: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

Querying Documents

Official query example: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

First, create the user database and insert the following data into the users collection:

sql
> use user
> db.users.insertMany([
    {
        name: "mike",
        age: 12,

    },
    {
        name: "jenny",
        age: 14,

    },
    {
        name: "jack",
        age: 18,
        address: "usa"
    }
])

Query Single

go
type User struct {
    Name    string `bson:"name"`
    Age     int    `bson:"age"`
    Address string `bson:"address"`
}

var user User

result := client.Database("user"). // Select database
                    Collection("users").                     // Select collection
                    FindOne(ctx, bson.D{{"address", "usa"}}) // Filter condition

// Deserialize
if err := result.Decode(&user); err != nil {
    log.Panicln(err)
}

fmt.Printf("%+v\n", user)

The above query code is equivalent to:

sql
db.users.findOne({
    address: "usa"
})

Output:

{Name:jack Age:18 Address:usa}

Query Multiple

go
type User struct {
   Name    string `bson:"name"`
   Age     int    `bson:"age"`
   Address string `bson:"address"`
}

var users []User

cursor, err := client.Database("user"). // Select database
               Collection("users"). // Select collection
               Find(ctx, bson.D{})  // Filter condition

if err != nil {
   log.Panicln(err)
}

if err := cursor.All(ctx, &users); err != nil {
   log.Panicln(err)
}

fmt.Printf("%+v\n", users)

Equivalent to:

sql
db.users.find({})

Output:

[{Name:jack Age:18 Address:usa} {Name:mike Age:12 Address:} {Name:jenny Age:14 Address:}]

When constructing query conditions, you can also use options:

go
type User struct {
    Name    string `bson:"name"`
    Age     int    `bson:"age"`
    Address string `bson:"address"`
}

var users []User

find := options.Find()
find.SetSort(bson.M{"age": 1})
find.SetLimit(1)

cursor, err := client.Database("user"). // Select database
                    Collection("users").      // Select collection
                    Find(ctx, bson.D{}, find) // Filter condition

if err != nil {
    log.Panicln(err)
}

if err := cursor.All(ctx, &users); err != nil {
    log.Panicln(err)
}

fmt.Printf("%+v\n", users)

Equivalent to:

db.users.find({}).sort({age:1}).limit(1)

Output:

[{Name:mike Age:12 Address:}]

Creating Documents

Official create example: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

Below is an example of creating a single document:

go
one, err := client.Database("user").Collection("users").InsertOne(ctx, User{
    Name:    "lili",
    Age:     20,
    Address: "china",
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(one.InsertedID)

After successful creation, the document's ObjectID is returned:

ObjectID("64c60fa01e2548d9e4de6cf4")

Below is an example of creating multiple documents:

go
users := []any{User{
    Name:    "john",
    Age:     10,
    Address: "usa",
}, User{
    Name:    "pop",
    Age:     30,
    Address: "uk",
}}

one, err := client.Database("user").Collection("users").InsertMany(ctx, users)
if err != nil {
    log.Panicln(err)
}
fmt.Println(one.InsertedIDs)

After successful creation, a list of ObjectIDs is returned:

[ObjectID("64c610d5aec2618d6ca0b515") ObjectID("64c610d5aec2618d6ca0b516")]

The above two code snippets are equivalent to db.users.insertOne and db.users.insertMany.

Updating Documents

Official update example: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

Below is an example of updating a single document, renaming a person named lili to mark:

go
upres, err := client.Database("user").Collection("users").UpdateOne(ctx, bson.D{
    {"name", "mark"},
},
    bson.D{
       {"$set", bson.D{
          {"name", "lili"},
       }},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

Equivalent to:

sql
db.users.updateOne({
    name: "lili"
}, {
    $set: {
        name: "mark"
    },
})

Output:

&{MatchedCount:1 ModifiedCount:1 UpsertedCount:0 UpsertedID:<nil>}

Below is an example of updating multiple documents, updating the address of people aged 10 to cn:

go
upres, err := client.Database("user").Collection("users").UpdateMany(ctx, bson.D{
    {"age", 10},
},
    bson.D{
        {"$set", bson.D{
            {"address", "cn"},
        }},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

Besides using Update, MongoDB also provides Replace. The difference between the two is that the former updates document fields, while the latter directly replaces the document. For example, in the code below, operators are no longer needed:

go
upres, err := client.Database("user").Collection("users").ReplaceOne(ctx, bson.D{
    {"age", 10},
},
    bson.D{
       {"address", "cn"},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

MongoDB also provides FindOneAndUpdate and FindOneAndReplace to retrieve and update documents. As follows:

go
res := client.Database("user").Collection("users").FindOneAndReplace(ctx, bson.D{
    {"address", "cn"},
},
    bson.D{
       {"address", "uk"},
    })
if err := res.Err(); err != nil {
    log.Panicln(err)
}

var user User

res.Decode(&user)

fmt.Printf("%+v", user)

Output:

Name: Age:0 Address:cn}

This operation queries the document first and then modifies it.

Deleting Documents

Official delete example: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

Below is an example of deleting a single document:

go
result, err := client.Database("user").Collection("users").DeleteOne(ctx, bson.D{
    {"name", "jack"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

Below is an example of deleting multiple documents:

go
result, err := client.Database("user").Collection("users").DeleteMany(ctx, bson.D{
    {"age", "10"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

Aggregation

Official aggregation example: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

Aggregation operations use the mongo.Pipeline type, which is essentially []bson.D:

go
type Pipeline []bson.D
go
pipline := mongo.Pipeline{
    {
       {"$match", bson.D{
          {"address", "uk"},
       }},
    },
    {
       {"$sort", bson.D{
          {"age", 1},
       }},
    },
}
aggregate, err := client.Database("user").Collection("users").Aggregate(ctx, pipline)
if err != nil {
    log.Panicln(err)
}
var users []User
if err := aggregate.All(ctx, &users); err != nil {
    log.Panicln(err)
}
log.Println(users)

Output:

[{lili 20 uk} {kak 30 uk}]

This aggregation operation matches all users with address "uk" and then sorts them by age.