Skip to content

MongoDB ​

Before You Start

  • Applies to: Go 1.21+ (examples verified with Go 1.22) · Prerequisites: Std: encode · Read time: about 15 min

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.

Complete Example ​

The program below runs as-is: copy it into a file and execute go run main.go, or paste it into the Go Playground.

go
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	filter := map[string]any{
		"status": "active",
		"age":    map[string]any{"$gte": 18},
	}

	data, err := json.Marshal(filter)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(data))
}

Expected output:

text
{"age":{"$gte":18},"status":"active"}

Common Pitfalls ​

  • Build filters with bson.M / structs, not string concatenation. The driver serialises values; hand-built JSON breaks on dates and ObjectIDs.
  • Pass a context to every operation. Find, InsertOne and friends block until the server answers; a timeout keeps latency bounded.
  • Indexes decide performance. Check with explain before blaming the driver.
  • Mind the connection pool. Tune maxPoolSize and reuse one client for the whole process; Connect is lazy and safe to keep.
  • ObjectID conversions are explicit. Accept hex strings at the API edge and convert with primitive.ObjectIDFromHex.

Change Log ​

  • 2026-10-14: Added "Before You Start" notes, a complete runnable example with expected output, common pitfalls, and this change log.

Golang by www.golangdev.cn · Adapted in part from Golang 中文学习文档 (MIT, © 寒江蓑笠翁)