Section 5: Programming

🐹 MongoDB with Go (Golang)

Complete tutorial on using MongoDB with Go - CRUD operations, aggregation, best practices

8 Complete Examples
CRUD Full Operations
100% Production Ready
Go 1.18+ Modern Go
βš™οΈ

Setup & Installation

Get started with MongoDB Go driver

Install Go Driver

Install the official MongoDB Go driver:

Terminal
go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/bson
πŸ’‘ Driver Version

This tutorial uses the official MongoDB Go driver v1.12+. Make sure you have Go 1.18 or later installed.

Project Structure

Project Layout
myproject/
β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
β”œβ”€β”€ main.go
└── models/
    └── user.go
πŸ”Œ

Connecting to MongoDB

Establish database connection

Basic Connection

main.go
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()

    // Connection string
    // Local: "mongodb://localhost:27017"
    // Atlas: "mongodb+srv://user:pass@cluster0.mongodb.net/"
    uri := "mongodb://localhost:27017"

    // Create client
    client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = client.Disconnect(ctx); err != nil {
            log.Fatal(err)
        }
    }()

    // 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("myapp")
    collection := database.Collection("users")

    fmt.Println("Collection:", collection.Name())
}
βœ… Connection Established!

Run with: go run main.go

Define Data Model

models/user.go
package models

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

type User struct {
    ID        primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
    Name      string             `json:"name" bson:"name"`
    Email     string             `json:"email" bson:"email"`
    Age       int                `json:"age" bson:"age"`
    Active    bool               `json:"active" bson:"active"`
    CreatedAt time.Time          `json:"created_at" bson:"created_at"`
    UpdatedAt time.Time          `json:"updated_at" bson:"updated_at"`
}
πŸ’‘ BSON Tags
  • bson:"_id,omitempty" - MongoDB's _id field
  • bson:"name" - Maps Go field to BSON field
  • json:"name" - JSON serialization
  • omitempty - Omit if empty
βž•

Insert Operations

Create documents

Insert One Document

Insert One
func insertOne(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Create user
    user := models.User{
        Name:      "Alice Johnson",
        Email:     "alice@example.com",
        Age:       28,
        Active:    true,
        CreatedAt: time.Now(),
        UpdatedAt: time.Now(),
    }

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

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

Insert Many Documents

Insert Many
func insertMany(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Create users
    users := []interface{}{
        models.User{
            Name: "Bob Smith", Email: "bob@example.com",
            Age: 35, Active: true,
            CreatedAt: time.Now(), UpdatedAt: time.Now(),
        },
        models.User{
            Name: "Carol White", Email: "carol@example.com",
            Age: 42, Active: false,
            CreatedAt: time.Now(), UpdatedAt: time.Now(),
        },
        models.User{
            Name: "David Brown", Email: "david@example.com",
            Age: 29, Active: true,
            CreatedAt: time.Now(), UpdatedAt: time.Now(),
        },
    }

    // Insert documents
    result, err := collection.InsertMany(ctx, users)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Inserted %d documents\n", len(result.InsertedIDs))
}
πŸ”

Find Operations

Query documents

Find One Document

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

func findOne(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Create filter
    filter := bson.M{"email": "alice@example.com"}

    // Find one document
    var user models.User
    err := collection.FindOne(ctx, filter).Decode(&user)
    if err != nil {
        if err == mongo.ErrNoDocuments {
            fmt.Println("No document found")
            return
        }
        log.Fatal(err)
    }

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

Find Many Documents

Find Many
func findMany(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Find active users
    filter := bson.M{"active": true}

    // Options: Sort by name, limit to 10
    opts := options.Find().SetSort(bson.D{{"name", 1}}).SetLimit(10)

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

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

    fmt.Printf("Found %d users\n", len(users))
    for _, user := range users {
        fmt.Printf("  - %s (%s)\n", user.Name, user.Email)
    }
}

Query Operators

Complex Queries
func complexQuery(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Find users with age >= 25 and age <= 40
    filter := bson.M{
        "age": bson.M{
            "$gte": 25,
            "$lte": 40,
        },
        "active": true,
    }

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

    var users []models.User
    cursor.All(ctx, &users)
    
    fmt.Printf("Found %d users between 25-40\n", len(users))
}
πŸ’‘ Common Query Operators
  • $eq - Equal to
  • $gt, $gte - Greater than (or equal)
  • $lt, $lte - Less than (or equal)
  • $in - Matches any value in array
  • $and, $or - Logical operators
  • $regex - Regular expression match
✏️

Update Operations

Modify documents

Update One Document

Update One
func updateOne(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Filter
    filter := bson.M{"email": "alice@example.com"}

    // Update
    update := bson.M{
        "$set": bson.M{
            "age":        29,
            "updated_at": time.Now(),
        },
    }

    // Update one document
    result, err := collection.UpdateOne(ctx, filter, update)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Matched %d, Modified %d\n", 
        result.MatchedCount, result.ModifiedCount)
}

Update Many Documents

Update Many
func updateMany(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Update all inactive users
    filter := bson.M{"active": false}

    update := bson.M{
        "$set": bson.M{
            "active":     true,
            "updated_at": time.Now(),
        },
    }

    // Update many documents
    result, err := collection.UpdateMany(ctx, filter, update)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Updated %d documents\n", result.ModifiedCount)
}
πŸ’‘ Update Operators
  • $set - Set field value
  • $unset - Remove field
  • $inc - Increment numeric value
  • $push - Add to array
  • $pull - Remove from array
πŸ—‘οΈ

Delete Operations

Remove documents

Delete One & Many

Delete Operations
func deleteOne(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Delete by email
    filter := bson.M{"email": "alice@example.com"}

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

    fmt.Printf("Deleted %d document\n", result.DeletedCount)
}

func deleteMany(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Delete users older than 50
    filter := bson.M{
        "age": bson.M{"$gt": 50},
    }

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

    fmt.Printf("Deleted %d documents\n", result.DeletedCount)
}
πŸ“Š

Aggregation Pipeline

Complex data operations

Basic Aggregation

Aggregation Example
func aggregation(collection *mongo.Collection) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // Pipeline: Group by active status and count
    pipeline := mongo.Pipeline{
        bson.D{{"$match", bson.D{{"age", bson.D{{"$gte", 25}}}}}},
        bson.D{{"$group", bson.D{
            {"_id", "$active"},
            {"count", bson.D{{"$sum", 1}}},
            {"avgAge", bson.D{{"$avg", "$age"}}},
        }}},
        bson.D{{"$sort", bson.D{{"count", -1}}}},
    }

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

    // Process results
    type Result struct {
        ID     bool    `bson:"_id"`
        Count  int     `bson:"count"`
        AvgAge float64 `bson:"avgAge"`
    }

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

    for _, result := range results {
        fmt.Printf("Active: %v, Count: %d, Avg Age: %.1f\n", 
            result.ID, result.Count, result.AvgAge)
    }
}
⭐

Best Practices

Production-ready code

Connection Pool & Context

⚠️ Important Tips
  • Reuse Client: Create one client, reuse across app
  • Use Context: Always use context with timeout
  • Defer Close: Always close cursors and disconnect
  • Error Handling: Check all errors properly
  • Connection Pool: Driver handles pooling automatically

Complete Example

Production-Ready Code
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type MongoDB struct {
    Client *mongo.Client
    DB     *mongo.Database
}

func NewMongoDB(uri, dbName string) (*MongoDB, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
    if err != nil {
        return nil, err
    }

    if err := client.Ping(ctx, nil); err != nil {
        return nil, err
    }

    return &MongoDB{
        Client: client,
        DB:     client.Database(dbName),
    }, nil
}

func (m *MongoDB) Close() error {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    return m.Client.Disconnect(ctx)
}

func main() {
    // Initialize
    db, err := NewMongoDB("mongodb://localhost:27017", "myapp")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    fmt.Println("βœ… Connected to MongoDB!")

    // Use collections
    collection := db.DB.Collection("users")
    
    // Your operations here...
}