# README

MongoDB Client Library

MongoDB Go Version GoDoc License: MIT

A production-ready MongoDB client wrapper for Go applications with built-in support for connection pooling, transactions, telemetry, and testing.

Table of Contents

Features

Core Features

  • ๐Ÿ”„ Automatic connection management and pooling
  • ๐Ÿ“Š Comprehensive transaction support
  • ๐Ÿ“ˆ Built-in telemetry and monitoring
  • ๐Ÿ” Configurable retry mechanisms
  • โฑ๏ธ Query timeout handling
  • ๐Ÿงช In-memory testing capabilities
  • ๐Ÿ” Detailed logging and debugging
  • ๐Ÿ›ก๏ธ Connection security options

Performance Features

  • Connection pooling optimization
  • Automatic retry with exponential backoff
  • Query timeout management
  • Efficient resource cleanup

Installation

go get -u github.com/SolomonAIEngineering/backend-core-library/database/mongo

Quick Start

package main

import (
    "context"
    "time"
    
    "github.com/SolomonAIEngineering/backend-core-library/database/mongo"
    "github.com/SolomonAIEngineering/backend-core-library/instrumentation"
    "go.uber.org/zap"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
    // Initialize client
    client, err := mongo.New(
        mongo.WithDatabaseName("myapp"),
        mongo.WithConnectionURI("mongodb://localhost:27017"),
        mongo.WithQueryTimeout(30 * time.Second),
        mongo.WithLogger(zap.L()),
    )
    if err != nil {
        panic(err)
    }
    defer client.Close()

    // Basic CRUD operations
    collection, err := client.GetCollection("users")
    if err != nil {
        panic(err)
    }

    // Insert a document
    ctx := context.Background()
    user := map[string]interface{}{
        "name": "John Doe",
        "email": "[email protected]",
        "created_at": time.Now(),
    }
    
    result, err := collection.InsertOne(ctx, user)
    if err != nil {
        panic(err)
    }
}

Configuration

Available Options

type Config struct {
    // Client configuration
    opts := []mongo.Option{
        // Database Configuration
        mongo.WithDatabaseName("mydb"),
        mongo.WithConnectionURI("mongodb://localhost:27017"),
        
        // Timeout Settings
        mongo.WithQueryTimeout(60 * time.Second),
        mongo.WithRetryTimeOut(30 * time.Second),
        
        // Retry Configuration
        mongo.WithMaxConnectionAttempts(3),
        mongo.WithMaxRetriesPerOperation(3),
        mongo.WithOperationSleepInterval(5 * time.Second),
        
        // Monitoring & Logging
        mongo.WithTelemetry(&instrumentation.Client{}),
        mongo.WithLogger(zap.L()),
        
        // Collection Management
        mongo.WithCollectionNames([]string{"users", "products", "orders"}),
        
        // Advanced Options
        mongo.WithClientOptions(options.Client().
            ApplyURI("mongodb://localhost:27017").
            SetMaxPoolSize(100).
            SetMinPoolSize(10)),
    }
}

Security Configuration

// TLS Configuration
tlsConfig := &tls.Config{
    // ... your TLS configuration
}

opts := []mongo.Option{
    mongo.WithClientOptions(options.Client().
        ApplyURI("mongodb://localhost:27017").
        SetTLSConfig(tlsConfig)),
    mongo.WithCredentials(options.Credential{
        Username: "user",
        Password: "pass",
        AuthSource: "admin",
    }),
}

Working with Collections

Basic CRUD Operations

// Get collection
collection, err := client.GetCollection("users")
if err != nil {
    return err
}

// Insert
doc := map[string]interface{}{
    "name": "Alice",
    "age":  30,
}
result, err := collection.InsertOne(ctx, doc)

// Find
var user map[string]interface{}
err = collection.FindOne(ctx, bson.M{"name": "Alice"}).Decode(&user)

// Update
update := bson.M{"$set": bson.M{"age": 31}}
_, err = collection.UpdateOne(
    ctx,
    bson.M{"name": "Alice"},
    update,
)

// Delete
_, err = collection.DeleteOne(ctx, bson.M{"name": "Alice"})

Bulk Operations

// Bulk Insert
documents := []interface{}{
    bson.D{{"name", "User1"}, {"age", 25}},
    bson.D{{"name", "User2"}, {"age", 30}},
}
result, err := collection.InsertMany(ctx, documents)

// Bulk Update
updates := []mongo.WriteModel{
    mongo.NewUpdateOneModel().
        SetFilter(bson.M{"name": "User1"}).
        SetUpdate(bson.M{"$set": bson.M{"age": 26}}),
    mongo.NewUpdateOneModel().
        SetFilter(bson.M{"name": "User2"}).
        SetUpdate(bson.M{"$set": bson.M{"age": 31}}),
}
_, err = collection.BulkWrite(ctx, updates)

Transactions

Standard Transaction Example

err := client.StandardTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
    // Get collections
    users, err := client.GetCollection("users")
    if err != nil {
        return nil, err
    }
    orders, err := client.GetCollection("orders")
    if err != nil {
        return nil, err
    }

    // Perform operations
    _, err = users.InsertOne(sessCtx, userDoc)
    if err != nil {
        return nil, err
    }
    
    _, err = orders.InsertOne(sessCtx, orderDoc)
    if err != nil {
        return nil, err
    }

    return nil, nil
})

Complex Transaction with Return Value

type TransactionResult struct {
    UserID  string
    OrderID string
}

result, err := client.ComplexTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
    // Your transaction logic here
    userResult, err := users.InsertOne(sessCtx, userDoc)
    if err != nil {
        return nil, err
    }
    
    orderResult, err := orders.InsertOne(sessCtx, orderDoc)
    if err != nil {
        return nil, err
    }

    return &TransactionResult{
        UserID:  userResult.InsertedID.(string),
        OrderID: orderResult.InsertedID.(string),
    }, nil
})

Error Handling

// Custom error types
var (
    ErrConnectionFailed = errors.New("failed to connect to mongodb")
    ErrQueryTimeout     = errors.New("query timeout")
)

// Error handling example
collection, err := client.GetCollection("users")
if err != nil {
    switch {
    case errors.Is(err, mongo.ErrCollectionNotFound):
        // Handle collection not found
    case errors.Is(err, mongo.ErrDatabaseNotFound):
        // Handle database not found
    default:
        // Handle other errors
    }
    return err
}

Telemetry & Monitoring

// Initialize with telemetry
telemetryClient := &instrumentation.Client{
    // Configure your telemetry client
}

client, err := mongo.New(
    mongo.WithTelemetry(telemetryClient),
    mongo.WithMetricsEnabled(true),
)

// Access metrics
metrics := client.GetMetrics()
fmt.Printf("Total Queries: %d\n", metrics.TotalQueries)
fmt.Printf("Failed Queries: %d\n", metrics.FailedQueries)
fmt.Printf("Average Query Time: %v\n", metrics.AverageQueryTime)

Testing

In-Memory Testing

func TestUserRepository(t *testing.T) {
    // Create test client
    testClient, err := mongo.NewInMemoryTestDbClient(
        []string{"users", "orders"},
    )
    if err != nil {
        t.Fatal(err)
    }
    defer testClient.Teardown()

    // Run tests
    t.Run("InsertUser", func(t *testing.T) {
        collection, err := testClient.GetCollection("users")
        if err != nil {
            t.Fatal(err)
        }

        user := bson.M{"name": "Test User"}
        _, err = collection.InsertOne(context.Background(), user)
        assert.NoError(t, err)
    })
}

Mocking

type MockMongoClient struct {
    mongo.Client
    MockInsertOne func(context.Context, interface{}) (*mongo.InsertOneResult, error)
}

func (m *MockMongoClient) InsertOne(ctx context.Context, document interface{}) (*mongo.InsertOneResult, error) {
    return m.MockInsertOne(ctx, document)
}

Best Practices

Connection Management

// Initialize once at application startup
client, err := mongo.New(opts...)
if err != nil {
    log.Fatal(err)
}
defer client.Close()

// Reuse the client throughout your application

Query Optimization

// Use appropriate indexes
collection.CreateIndex(ctx, mongo.IndexModel{
    Keys: bson.D{{"field", 1}},
    Options: options.Index().SetUnique(true),
})

// Use projection to limit returned fields
collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{
    "needed_field": 1,
    "_id": 0,
}))

Resource Management

// Use context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

// Use cursor properly
cursor, err := collection.Find(ctx, filter)
if err != nil {
    return err
}
defer cursor.Close(ctx)

Migration Guide

Upgrading from v1.x to v2.x

// Old v1.x configuration
oldClient := mongo.NewClient(
    "mongodb://localhost:27017",
    "mydb",
)

// New v2.x configuration
newClient, err := mongo.New(
    mongo.WithConnectionURI("mongodb://localhost:27017"),
    mongo.WithDatabaseName("mydb"),
)

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

# Clone the repository
git clone https://github.com/SolomonAIEngineering/backend-core-library.git

# Install dependencies
go mod download

# Run tests
go test -v ./...

# Run linting
golangci-lint run

License

This MongoDB client is released under the MIT License. See LICENSE for details.


Support

# Functions

New creates a new instance of the mongo client.
NewInMemoryTestDbClient creates a new in-memory MongoDB database and returns a client to it.
ObjectIDFromHexIDString takes a pointer to a string as input and returns a primitive.ObjectID and an error.
WithClientOptions sets the client options.
WithCollectionNames sets the collection names.
WithConnectionURI sets the connection URI.
WithDatabaseName sets the database name.
WithLogger sets the logging utility used by this object.
WithMaxConnectionAttempts sets the maximum connection attempts to initiate against the database.
WithMaxRetriesPerOperation sets the maximum retries to attempt per failed database connection attempt.
WithOperationSleepInterval sets the amount of time between retry operations that the system sleeps.
WithQueryTimeout sets the maximal amount of time a query can execute before being cancelled.
WithRetryTimeOut sets the maximum time until a retry operation is observed as a timed out operation.
WithTelemetry sets the object by which we will emit metrics, trace requests, and database operations.

# Structs

No description provided by the author
No description provided by the author

# Interfaces

No description provided by the author

# Type aliases

MongoTx is a type alias for the `WithTransaction` method of the `mongo.Session` object.
No description provided by the author