MongoDB Go Driver

High-performance MongoDB integration for Go applications

🐹 What is MongoDB Go Driver?

The MongoDB Go Driver provides idiomatic Go integration for MongoDB databases. It leverages Go's concurrency features, offers excellent performance, and follows Go conventions with context support, making it ideal for building scalable microservices and cloud-native applications.


// Simple Go connection
import "go.mongodb.org/mongo-driver/mongo"

client, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
fmt.Println("Connected to MongoDB!")
                                    

Key Features

High Performance

Optimized for Go's concurrency model

Goroutines Channels Fast
🎯

Context Support

Native context.Context integration

Timeouts Cancellation Deadlines
🔧

Idiomatic Go

Follows Go best practices

Structs Interfaces Error Handling
🔒

Type Safety

Strong typing with BSON tags

Struct Tags Marshaling Validation

🔹 Installation

Install the MongoDB Go Driver using Go modules. This package provides all necessary tools for connecting to MongoDB and performing database operations with full Go integration and concurrency support.

# Install using go get
go get go.mongodb.org/mongo-driver/mongo

# Or add to go.mod
go mod init myapp
go get go.mongodb.org/mongo-driver/mongo

🔹 Connecting to MongoDB

Establish a connection using mongo.Connect with context support. The driver manages connection pooling automatically and provides thread-safe access to databases and collections with proper error handling.

package main

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

func main() {
    // Create context with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    
    // Connect to MongoDB
    client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctx)
    
    // Ping the database
    err = client.Ping(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("Connected to MongoDB!")
    
    // Get database and collection
    database := client.Database("myDatabase")
    collection := database.Collection("users")
}

🔹 Define Structs

Create Go structs to represent MongoDB documents with BSON tags for field mapping. Use bson tags to control serialization and omitempty to exclude zero values, ensuring clean document storage.

import (
    "go.mongodb.org/mongo-driver/bson/primitive"
    "time"
)

type User struct {
    ID        primitive.ObjectID `bson:"_id,omitempty"`
    Name      string             `bson:"name"`
    Email     string             `bson:"email"`
    Age       int                `bson:"age"`
    Hobbies   []string           `bson:"hobbies,omitempty"`
    CreatedAt time.Time          `bson:"created_at"`
}

// Constructor function
func NewUser(name, email string, age int) *User {
    return &User{
        Name:      name,
        Email:     email,
        Age:       age,
        Hobbies:   []string{},
        CreatedAt: time.Now(),
    }
}

🔹 Insert Documents

Add documents to collections using Go structs. The driver provides InsertOne for single documents and InsertMany for bulk inserts, both returning detailed results including generated IDs.

import "go.mongodb.org/mongo-driver/bson/primitive"

// Insert one document
user := NewUser("John Doe", "[email protected]", 30)
user.Hobbies = []string{"reading", "coding"}

result, err := collection.InsertOne(ctx, user)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Inserted ID: %v\n", result.InsertedID)

// Insert multiple documents
users := []interface{}{
    NewUser("Alice", "[email protected]", 25),
    NewUser("Bob", "[email protected]", 35),
}

multiResult, err := collection.InsertMany(ctx, users)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Inserted %d documents\n", len(multiResult.InsertedIDs))

Output:

Inserted ID: ObjectID("507f1f77bcf86cd799439011")

Inserted 2 documents

🔹 Find Documents

Query collections using BSON filters for flexible data retrieval. Use Find for multiple documents with cursor iteration or FindOne for single documents, with full context support for timeouts and cancellation.

import "go.mongodb.org/mongo-driver/bson"

// Find all documents
cursor, err := collection.Find(ctx, bson.M{})
if err != nil {
    log.Fatal(err)
}
defer cursor.Close(ctx)

var users []User
if err = cursor.All(ctx, &users); err != nil {
    log.Fatal(err)
}

for _, user := range users {
    fmt.Println(user.Name)
}

// Find with filter
filter := bson.M{"age": bson.M{"$gte": 18}}
cursor, err = collection.Find(ctx, filter)
// ... process results

// Find one document
var user User
err = collection.FindOne(ctx, bson.M{"name": "John Doe"}).Decode(&user)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Found: %s\n", user.Name)

// Find with options (projection, sort, limit)
opts := options.Find().SetProjection(bson.M{"name": 1, "email": 1}).SetLimit(10)
cursor, err = collection.Find(ctx, bson.M{}, opts)

Output:

John Doe

Found: John Doe

🔹 Update Documents

Modify documents using BSON update operators. The driver supports UpdateOne for single documents and UpdateMany for bulk updates, returning detailed results about matched and modified documents.

import "go.mongodb.org/mongo-driver/bson"

// Update one document
filter := bson.M{"name": "John Doe"}
update := bson.M{
    "$set": bson.M{
        "age":   31,
        "email": "[email protected]",
    },
}

result, err := collection.UpdateOne(ctx, filter, update)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Modified: %d\n", result.ModifiedCount)

// Update many documents
filterMany := bson.M{"age": bson.M{"$lt": 30}}
updateMany := bson.M{"$set": bson.M{"status": "young"}}

multiResult, err := collection.UpdateMany(ctx, filterMany, updateMany)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Updated: %d\n", multiResult.ModifiedCount)

// Increment a value
incUpdate := bson.M{"$inc": bson.M{"age": 1}}
collection.UpdateOne(ctx, filter, incUpdate)

// Add to array
pushUpdate := bson.M{"$push": bson.M{"hobbies": "gaming"}}
collection.UpdateOne(ctx, filter, pushUpdate)

Output:

Modified: 1

Updated: 2

🔹 Delete Documents

Remove documents from collections using DeleteOne or DeleteMany. The driver returns detailed results including the count of deleted documents, allowing you to verify the operation's success.

import "go.mongodb.org/mongo-driver/bson"

// Delete one document
filter := bson.M{"name": "John Doe"}
result, err := collection.DeleteOne(ctx, filter)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Deleted: %d\n", result.DeletedCount)

// Delete many documents
filterMany := bson.M{"age": bson.M{"$lt": 18}}
multiResult, err := collection.DeleteMany(ctx, filterMany)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Deleted: %d\n", multiResult.DeletedCount)

// Delete all documents
deleteAll, err := collection.DeleteMany(ctx, bson.M{})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("All deleted: %d\n", deleteAll.DeletedCount)

Output:

Deleted: 1

Deleted: 3

🔹 Aggregation Pipeline

Build complex aggregation pipelines using BSON arrays and documents. Combine multiple stages like match, group, and sort to perform sophisticated data analysis directly in MongoDB with full Go type safety.

import "go.mongodb.org/mongo-driver/bson"

// Aggregation pipeline
pipeline := []bson.M{
    {"$match": bson.M{"age": bson.M{"$gte": 18}}},
    {"$group": bson.M{
        "_id":    "$city",
        "avgAge": bson.M{"$avg": "$age"},
        "count":  bson.M{"$sum": 1},
    }},
    {"$sort": bson.M{"avgAge": -1}},
}

cursor, err := collection.Aggregate(ctx, pipeline)
if err != nil {
    log.Fatal(err)
}
defer cursor.Close(ctx)

type Result struct {
    City   string  `bson:"_id"`
    AvgAge float64 `bson:"avgAge"`
    Count  int     `bson:"count"`
}

var results []Result
if err = cursor.All(ctx, &results); err != nil {
    log.Fatal(err)
}

for _, result := range results {
    fmt.Printf("%s: avg %.1f, count %d\n", result.City, result.AvgAge, result.Count)
}

Output:

New York: avg 32.0, count 5

Boston: avg 28.0, count 3

🔹 Transactions

Execute multi-document transactions for ACID guarantees. Use sessions to group operations that must succeed or fail together, ensuring data consistency across multiple collections or databases.

import "go.mongodb.org/mongo-driver/mongo"

// Start a session
session, err := client.StartSession()
if err != nil {
    log.Fatal(err)
}
defer session.EndSession(ctx)

// Execute transaction
err = mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
    // Start transaction
    if err := session.StartTransaction(); err != nil {
        return err
    }
    
    // Perform operations
    _, err := collection.InsertOne(sc, NewUser("Test", "[email protected]", 25))
    if err != nil {
        session.AbortTransaction(sc)
        return err
    }
    
    _, err = collection.UpdateOne(sc, bson.M{"name": "John"}, bson.M{"$inc": bson.M{"age": 1}})
    if err != nil {
        session.AbortTransaction(sc)
        return err
    }
    
    // Commit transaction
    return session.CommitTransaction(sc)
})

if err != nil {
    log.Fatal(err)
}
fmt.Println("Transaction completed successfully")

🔹 Error Handling

Implement robust error handling using Go's error interface and MongoDB-specific error types. Check for specific error conditions like duplicate keys, connection failures, or timeouts to build resilient applications.

import (
    "go.mongodb.org/mongo-driver/mongo"
    "errors"
)

func insertUser(ctx context.Context, collection *mongo.Collection, user *User) error {
    _, err := collection.InsertOne(ctx, user)
    if err != nil {
        // Check for duplicate key error
        if mongo.IsDuplicateKeyError(err) {
            return errors.New("user with this email already exists")
        }
        
        // Check for timeout
        if errors.Is(err, context.DeadlineExceeded) {
            return errors.New("operation timed out")
        }
        
        // Check for network error
        if mongo.IsNetworkError(err) {
            return errors.New("network connection failed")
        }
        
        return fmt.Errorf("failed to insert user: %w", err)
    }
    
    return nil
}

// Usage
err := insertUser(ctx, collection, user)
if err != nil {
    log.Printf("Error: %v\n", err)
}

🧠 Test Your Knowledge

What package is used for MongoDB operations in Go?