Go Mongo

MongoDB 是一个流行的 NoSQL 文档数据库,使用 BSON(Binary JSON)格式存储数据。与关系型数据库不同,MongoDB 具有灵活的模式设计,适合存储非结构化或半结构化数据。

官方提供了 Go 语言 MongoDB 驱动程序 go.mongodb.org/mongo-driver。

下面是一个示例:

package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "os"
    "time"

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

// 1. 数据模型定义
type User struct {
    ID        primitive.ObjectID `bson:"_id,omitempty" json:"id"`
    Name      string             `bson:"name" json:"name"`
    Email     string             `bson:"email" json:"email"`
    Age       int                `bson:"age" json:"age"`
    Address   Address            `bson:"address" json:"address"`
    Tags      []string           `bson:"tags" json:"tags"`
    CreatedAt time.Time          `bson:"created_at" json:"created_at"`
    UpdatedAt time.Time          `bson:"updated_at" json:"updated_at"`
}

type Address struct {
    Street  string `bson:"street" json:"street"`
    City    string `bson:"city" json:"city"`
    Country string `bson:"country" json:"country"`
}

type Product struct {
    ID          primitive.ObjectID `bson:"_id,omitempty" json:"id"`
    Name        string             `bson:"name" json:"name"`
    Price       float64            `bson:"price" json:"price"`
    Description string             `bson:"description" json:"description"`
    Category    string             `bson:"category" json:"category"`
    Stock       int                `bson:"stock" json:"stock"`
    IsActive    bool               `bson:"is_active" json:"is_active"`
    Attributes  map[string]interface{} `bson:"attributes" json:"attributes"`
    CreatedAt   time.Time          `bson:"created_at" json:"created_at"`
}

type Order struct {
    ID        primitive.ObjectID `bson:"_id,omitempty" json:"id"`
    UserID    primitive.ObjectID `bson:"user_id" json:"user_id"`
    Items     []OrderItem        `bson:"items" json:"items"`
    Total     float64            `bson:"total" json:"total"`
    Status    string             `bson:"status" json:"status"`
    CreatedAt time.Time          `bson:"created_at" json:"created_at"`
}

type OrderItem struct {
    ProductID primitive.ObjectID `bson:"product_id" json:"product_id"`
    Name      string             `bson:"name" json:"name"`
    Quantity  int                `bson:"quantity" json:"quantity"`
    Price     float64            `bson:"price" json:"price"`
}

// 2. MongoDB 服务
type MongoDB struct {
    Client   *mongo.Client
    Database *mongo.Database
}

func NewMongoDB(uri, dbName string) (*MongoDB, error) {
    // 设置连接选项
    clientOptions := options.Client().
        ApplyURI(uri).
        SetMaxPoolSize(100).
        SetMinPoolSize(10).
        SetMaxConnIdleTime(5 * time.Minute)

    // 连接 MongoDB
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(ctx, clientOptions)
    if err != nil {
        return nil, fmt.Errorf("连接 MongoDB 失败: %v", err)
    }

    // 测试连接
    err = client.Ping(ctx, nil)
    if err != nil {
        return nil, fmt.Errorf("MongoDB 连接测试失败: %v", err)
    }

    database := client.Database(dbName)

    fmt.Println("✅ MongoDB 连接成功")
    return &MongoDB{
        Client:   client,
        Database: database,
    }, nil
}

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

func (m *MongoDB) GetCollection(name string) *mongo.Collection {
    return m.Database.Collection(name)
}

// 3. 基本 CRUD 操作示例
func demonstrateBasicCRUD(mongoDB *MongoDB) {
    fmt.Println("\n=== 基本 CRUD 操作 ===")

    usersCollection := mongoDB.GetCollection("users")
    ctx := context.Background()

    // 创建用户
    fmt.Println("\n1. 插入文档:")
    user := User{
        Name:      "张三",
        Email:     "zhang@example.com",
        Age:       25,
        Address:   Address{Street: "人民路123号", City: "北京", Country: "中国"},
        Tags:      []string{"vip", "active"},
        CreatedAt: time.Now(),
        UpdatedAt: time.Now(),
    }

    result, err := usersCollection.InsertOne(ctx, user)
    if err != nil {
        log.Printf("插入用户失败: %v", err)
        return
    }
    userID := result.InsertedID.(primitive.ObjectID)
    fmt.Printf("创建用户成功,ID: %s\n", userID.Hex())

    // 查询单个文档
    fmt.Println("\n2. 查询单个文档:")
    var fetchedUser User
    err = usersCollection.FindOne(ctx, bson.M{"email": "zhang@example.com"}).Decode(&fetchedUser)
    if err != nil {
        log.Printf("查询用户失败: %v", err)
        return
    }
    fmt.Printf("查询结果: %s (%s), 年龄: %d\n", fetchedUser.Name, fetchedUser.Email, fetchedUser.Age)

    // 查询多个文档
    fmt.Println("\n3. 查询多个文档:")
    cursor, err := usersCollection.Find(ctx, bson.M{})
    if err != nil {
        log.Printf("查询用户列表失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    var users []User
    if err = cursor.All(ctx, &users); err != nil {
        log.Printf("解析用户列表失败: %v", err)
        return
    }

    fmt.Println("所有用户:")
    for _, u := range users {
        fmt.Printf("  - ID: %s, 姓名: %s, 邮箱: %s\n", u.ID.Hex(), u.Name, u.Email)
    }

    // 更新文档
    fmt.Println("\n4. 更新文档:")
    update := bson.M{
        "$set": bson.M{
            "age":        26,
            "updated_at": time.Now(),
        },
    }
    updateResult, err := usersCollection.UpdateOne(
        ctx,
        bson.M{"_id": userID},
        update,
    )
    if err != nil {
        log.Printf("更新用户失败: %v", err)
        return
    }
    fmt.Printf("更新成功,影响文档数: %d\n", updateResult.ModifiedCount)

    // 删除文档
    fmt.Println("\n5. 删除文档:")
    deleteResult, err := usersCollection.DeleteOne(ctx, bson.M{"_id": userID})
    if err != nil {
        log.Printf("删除用户失败: %v", err)
        return
    }
    fmt.Printf("删除成功,删除文档数: %d\n", deleteResult.DeletedCount)
}

// 4. 高级查询示例
func demonstrateAdvancedQueries(mongoDB *MongoDB) {
    fmt.Println("\n=== 高级查询示例 ===")

    productsCollection := mongoDB.GetCollection("products")
    ctx := context.Background()

    // 创建测试数据
    products := []interface{}{
        Product{
            Name:        "MacBook Pro",
            Price:       12999.99,
            Description: "苹果笔记本电脑",
            Category:    "electronics",
            Stock:       50,
            IsActive:    true,
            Attributes:  map[string]interface{}{"brand": "Apple", "color": "Silver"},
            CreatedAt:   time.Now(),
        },
        Product{
            Name:        "iPhone 14",
            Price:       5999.99,
            Description: "苹果手机",
            Category:    "electronics",
            Stock:       100,
            IsActive:    true,
            Attributes:  map[string]interface{}{"brand": "Apple", "storage": "128GB"},
            CreatedAt:   time.Now(),
        },
        Product{
            Name:        "咖啡机",
            Price:       899.99,
            Description: "家用咖啡机",
            Category:    "appliances",
            Stock:       30,
            IsActive:    true,
            Attributes:  map[string]interface{}{"brand": "Nespresso", "type": "automatic"},
            CreatedAt:   time.Now(),
        },
    }

    _, err := productsCollection.InsertMany(ctx, products)
    if err != nil {
        log.Printf("插入产品失败: %v", err)
        return
    }

    // 条件查询
    fmt.Println("\n1. 条件查询:")
    var expensiveProducts []Product
    cursor, err := productsCollection.Find(
        ctx,
        bson.M{"price": bson.M{"$gt": 5000}},
        options.Find().SetSort(bson.M{"price": -1}),
    )
    if err != nil {
        log.Printf("查询高价产品失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    if err = cursor.All(ctx, &expensiveProducts); err != nil {
        log.Printf("解析产品失败: %v", err)
        return
    }

    fmt.Println("价格大于5000的产品:")
    for _, p := range expensiveProducts {
        fmt.Printf("  - %s: ¥%.2f\n", p.Name, p.Price)
    }

    // 数组查询
    fmt.Println("\n2. 数组查询:")
    var usersWithTag []User
    usersCollection := mongoDB.GetCollection("users")
    cursor, err = usersCollection.Find(
        ctx,
        bson.M{"tags": bson.M{"$in": []string{"vip", "premium"}}},
    )
    if err != nil {
        log.Printf("查询标签用户失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    if err = cursor.All(ctx, &usersWithTag); err != nil {
        log.Printf("解析用户失败: %v", err)
        return
    }

    fmt.Println("VIP 或 Premium 用户:")
    for _, u := range usersWithTag {
        fmt.Printf("  - %s: %v\n", u.Name, u.Tags)
    }

    // 投影查询(只返回指定字段)
    fmt.Println("\n3. 投影查询:")
    type ProductNameOnly struct {
        Name  string  `bson:"name"`
        Price float64 `bson:"price"`
    }
    var productNames []ProductNameOnly
    cursor, err = productsCollection.Find(
        ctx,
        bson.M{},
        options.Find().SetProjection(bson.M{"name": 1, "price": 1}),
    )
    if err != nil {
        log.Printf("投影查询失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    if err = cursor.All(ctx, &productNames); err != nil {
        log.Printf("解析投影结果失败: %v", err)
        return
    }

    fmt.Println("产品名称和价格:")
    for _, p := range productNames {
        fmt.Printf("  - %s: ¥%.2f\n", p.Name, p.Price)
    }
}

// 5. 聚合管道示例
func demonstrateAggregation(mongoDB *MongoDB) {
    fmt.Println("\n=== 聚合管道示例 ===")

    ordersCollection := mongoDB.GetCollection("orders")
    ctx := context.Background()

    // 创建测试订单数据
    orders := []interface{}{
        Order{
            UserID: primitive.NewObjectID(),
            Items: []OrderItem{
                {ProductID: primitive.NewObjectID(), Name: "MacBook Pro", Quantity: 1, Price: 12999.99},
                {ProductID: primitive.NewObjectID(), Name: "iPhone", Quantity: 2, Price: 5999.99},
            },
            Total:     24999.97,
            Status:    "completed",
            CreatedAt: time.Now().AddDate(0, 0, -5),
        },
        Order{
            UserID: primitive.NewObjectID(),
            Items: []OrderItem{
                {ProductID: primitive.NewObjectID(), Name: "iPad", Quantity: 1, Price: 4399.99},
            },
            Total:     4399.99,
            Status:    "completed",
            CreatedAt: time.Now().AddDate(0, 0, -3),
        },
        Order{
            UserID: primitive.NewObjectID(),
            Items: []OrderItem{
                {ProductID: primitive.NewObjectID(), Name: "MacBook Pro", Quantity: 1, Price: 12999.99},
            },
            Total:     12999.99,
            Status:    "pending",
            CreatedAt: time.Now().AddDate(0, 0, -1),
        },
    }

    _, err := ordersCollection.InsertMany(ctx, orders)
    if err != nil {
        log.Printf("插入订单失败: %v", err)
        return
    }

    // 聚合查询:按状态统计订单数量和总金额
    fmt.Println("\n1. 按状态统计订单:")
    pipeline := bson.A{
        bson.M{
            "$group": bson.M{
                "_id":   "$status",
                "count": bson.M{"$sum": 1},
                "total_amount": bson.M{"$sum": "$total"},
                "avg_amount": bson.M{"$avg": "$total"},
            },
        },
        bson.M{
            "$sort": bson.M{"total_amount": -1},
        },
    }

    cursor, err := ordersCollection.Aggregate(ctx, pipeline)
    if err != nil {
        log.Printf("聚合查询失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    type OrderStats struct {
        Status      string  `bson:"_id"`
        Count       int     `bson:"count"`
        TotalAmount float64 `bson:"total_amount"`
        AvgAmount   float64 `bson:"avg_amount"`
    }

    var stats []OrderStats
    if err = cursor.All(ctx, &stats); err != nil {
        log.Printf("解析聚合结果失败: %v", err)
        return
    }

    fmt.Println("订单统计:")
    for _, s := range stats {
        fmt.Printf("  - 状态: %s, 数量: %d, 总金额: ¥%.2f, 平均金额: ¥%.2f\n",
            s.Status, s.Count, s.TotalAmount, s.AvgAmount)
    }

    // 聚合查询:展开订单项统计产品销量
    fmt.Println("\n2. 产品销量统计:")
    salesPipeline := bson.A{
        bson.M{"$unwind": "$items"},
        bson.M{
            "$group": bson.M{
                "_id":   "$items.name",
                "total_sold": bson.M{"$sum": "$items.quantity"},
                "total_revenue": bson.M{"$sum": bson.M{"$multiply": []interface{}{"$items.quantity", "$items.price"}}},
            },
        },
        bson.M{"$sort": bson.M{"total_sold": -1}},
    }

    cursor, err = ordersCollection.Aggregate(ctx, salesPipeline)
    if err != nil {
        log.Printf("销量统计失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    type SalesStats struct {
        ProductName   string  `bson:"_id"`
        TotalSold    int     `bson:"total_sold"`
        TotalRevenue float64 `bson:"total_revenue"`
    }

    var salesStats []SalesStats
    if err = cursor.All(ctx, &salesStats); err != nil {
        log.Printf("解析销量统计失败: %v", err)
        return
    }

    fmt.Println("产品销量统计:")
    for _, s := range salesStats {
        fmt.Printf("  - %s: 销量 %d, 收入 ¥%.2f\n", s.ProductName, s.TotalSold, s.TotalRevenue)
    }
}

// 6. 索引管理示例
func demonstrateIndexes(mongoDB *MongoDB) {
    fmt.Println("\n=== 索引管理示例 ===")

    usersCollection := mongoDB.GetCollection("users")
    ctx := context.Background()

    // 创建索引
    fmt.Println("\n1. 创建索引:")
    indexModels := []mongo.IndexModel{
        {
            Keys: bson.M{"email": 1},
            Options: options.Index().SetUnique(true),
        },
        {
            Keys: bson.M{"created_at": -1},
        },
        {
            Keys: bson.M{"age": 1, "city": 1}, // 复合索引
        },
    }

    indexNames, err := usersCollection.Indexes().CreateMany(ctx, indexModels)
    if err != nil {
        log.Printf("创建索引失败: %v", err)
        return
    }

    fmt.Printf("创建索引成功: %v\n", indexNames)

    // 列出索引
    fmt.Println("\n2. 列出索引:")
    cursor, err := usersCollection.Indexes().List(ctx)
    if err != nil {
        log.Printf("列出索引失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    var indexes []bson.M
    if err = cursor.All(ctx, &indexes); err != nil {
        log.Printf("解析索引失败: %v", err)
        return
    }

    fmt.Println("集合索引:")
    for _, index := range indexes {
        fmt.Printf("  - 名称: %v, 键: %v\n", index["name"], index["key"])
    }
}

// 7. 事务处理示例
func demonstrateTransactions(mongoDB *MongoDB) {
    fmt.Println("\n=== 事务处理示例 ===")

    ctx := context.Background()

    // 开始会话
    session, err := mongoDB.Client.StartSession()
    if err != nil {
        log.Printf("开始会话失败: %v", err)
        return
    }
    defer session.EndSession(ctx)

    // 执行事务
    err = session.StartTransaction()
    if err != nil {
        log.Printf("开始事务失败: %v", err)
        return
    }

    callback := func(sessionContext mongo.SessionContext) (interface{}, error) {
        usersCollection := mongoDB.GetCollection("users")
        ordersCollection := mongoDB.GetCollection("orders")

        // 创建用户
        user := User{
            Name:      "事务用户",
            Email:     fmt.Sprintf("transaction%d@example.com", rand.Intn(1000)),
            Age:       30,
            CreatedAt: time.Now(),
            UpdatedAt: time.Now(),
        }

        userResult, err := usersCollection.InsertOne(sessionContext, user)
        if err != nil {
            return nil, err
        }
        userID := userResult.InsertedID.(primitive.ObjectID)

        // 创建订单
        order := Order{
            UserID:    userID,
            Items:     []OrderItem{{Name: "测试产品", Quantity: 1, Price: 100}},
            Total:     100,
            Status:    "completed",
            CreatedAt: time.Now(),
        }

        _, err = ordersCollection.InsertOne(sessionContext, order)
        if err != nil {
            return nil, err
        }

        fmt.Printf("✅ 事务执行成功,创建用户 ID: %s\n", userID.Hex())
        return userID, nil
    }

    // 提交事务
    result, err := session.WithTransaction(ctx, callback)
    if err != nil {
        log.Printf("事务执行失败: %v", err)
        session.AbortTransaction(ctx)
        return
    }

    fmt.Printf("事务结果: %v\n", result)
}

// 8. 批量操作示例
func demonstrateBulkOperations(mongoDB *MongoDB) {
    fmt.Println("\n=== 批量操作示例 ===")

    usersCollection := mongoDB.GetCollection("users")
    ctx := context.Background()

    // 批量写入
    fmt.Println("\n1. 批量插入:")
    users := []interface{}{
        User{Name: "批量用户1", Email: "batch1@example.com", Age: 20},
        User{Name: "批量用户2", Email: "batch2@example.com", Age: 25},
        User{Name: "批量用户3", Email: "batch3@example.com", Age: 30},
    }

    insertResult, err := usersCollection.InsertMany(ctx, users)
    if err != nil {
        log.Printf("批量插入失败: %v", err)
        return
    }
    fmt.Printf("批量插入成功,插入 %d 个文档\n", len(insertResult.InsertedIDs))

    // 批量更新
    fmt.Println("\n2. 批量更新:")
    filter := bson.M{"age": bson.M{"$lt": 30}}
    update := bson.M{"$inc": bson.M{"age": 1}}

    updateResult, err := usersCollection.UpdateMany(ctx, filter, update)
    if err != nil {
        log.Printf("批量更新失败: %v", err)
        return
    }
    fmt.Printf("批量更新成功,更新 %d 个文档\n", updateResult.ModifiedCount)

    // 批量删除
    fmt.Println("\n3. 批量删除:")
    deleteResult, err := usersCollection.DeleteMany(ctx, bson.M{"email": bson.M{"$regex": "batch"}})
    if err != nil {
        log.Printf("批量删除失败: %v", err)
        return
    }
    fmt.Printf("批量删除成功,删除 %d 个文档\n", deleteResult.DeletedCount)
}

// 9. 监控和性能示例
func demonstrateMonitoring(mongoDB *MongoDB) {
    fmt.Println("\n=== 数据库监控 ===")

    ctx := context.Background()

    // 获取数据库统计信息
    statsCommand := bson.D{{Key: "dbStats", Value: 1}}
    var stats bson.M
    err := mongoDB.Database.RunCommand(ctx, statsCommand).Decode(&stats)
    if err != nil {
        log.Printf("获取数据库统计失败: %v", err)
        return
    }

    fmt.Printf("数据库统计:\n")
    fmt.Printf("  数据库名: %v\n", stats["db"])
    fmt.Printf("  集合数: %v\n", stats["collections"])
    fmt.Printf("  文档数: %v\n", stats["objects"])
    fmt.Printf("  数据大小: %.2f MB\n", stats["dataSize"].(float64)/1024/1024)
    fmt.Printf("  存储大小: %.2f MB\n", stats["storageSize"].(float64)/1024/1024)
}

// 10. 文本搜索示例
func demonstrateTextSearch(mongoDB *MongoDB) {
    fmt.Println("\n=== 文本搜索示例 ===")

    productsCollection := mongoDB.GetCollection("products")
    ctx := context.Background()

    // 创建文本索引
    fmt.Println("\n1. 创建文本索引:")
    model := mongo.IndexModel{
        Keys: bson.M{
            "name":        "text",
            "description": "text",
        },
    }
    _, err := productsCollection.Indexes().CreateOne(ctx, model)
    if err != nil {
        log.Printf("创建文本索引失败: %v", err)
        return
    }

    // 文本搜索
    fmt.Println("\n2. 文本搜索:")
    cursor, err := productsCollection.Find(
        ctx,
        bson.M{"$text": bson.M{"$search": "苹果 电脑"}},
        options.Find().SetProjection(bson.M{"score": bson.M{"$meta": "textScore"}}).
            SetSort(bson.M{"score": bson.M{"$meta": "textScore"}}),
    )
    if err != nil {
        log.Printf("文本搜索失败: %v", err)
        return
    }
    defer cursor.Close(ctx)

    var results []bson.M
    if err = cursor.All(ctx, &results); err != nil {
        log.Printf("解析搜索结果失败: %v", err)
        return
    }

    fmt.Println("文本搜索结果:")
    for _, result := range results {
        fmt.Printf("  - %s (相关度: %.2f)\n", result["name"], result["score"])
    }
}

func main() {
    // MongoDB 连接字符串
    // 本地 MongoDB: mongodb://localhost:27017
    // MongoDB Atlas: mongodb+srv://username:password@cluster.mongodb.net/database
    uri := "mongodb://localhost:27017"
    if os.Getenv("MONGODB_URI") != "" {
        uri = os.Getenv("MONGODB_URI")
    }

    dbName := "go_mongo_demo"

    // 连接 MongoDB
    mongoDB, err := NewMongoDB(uri, dbName)
    if err != nil {
        log.Fatal(err)
    }
    defer mongoDB.Close()

    // 清空测试数据(可选)
    ctx := context.Background()
    collections := []string{"users", "products", "orders"}
    for _, coll := range collections {
        mongoDB.GetCollection(coll).Drop(ctx)
    }

    // 运行各种示例
    demonstrateBasicCRUD(mongoDB)
    demonstrateAdvancedQueries(mongoDB)
    demonstrateAggregation(mongoDB)
    demonstrateIndexes(mongoDB)
    demonstrateTransactions(mongoDB)
    demonstrateBulkOperations(mongoDB)
    demonstrateMonitoring(mongoDB)
    demonstrateTextSearch(mongoDB)

    fmt.Println("\n🎉 所有 MongoDB 示例执行完成!")
}

MongoDB 关键特性

  1. ​ 核心概念 ​

​ 文档 ​:BSON 格式的数据记录

​ 集合 ​:文档的容器

​ 数据库 ​:集合的容器

​ObjectID​:12 字节的唯一标识符

  1. ​ 主要操作 ​

​ 插入 ​:InsertOne(), InsertMany()

​ 查询 ​:FindOne(), Find()

​ 更新 ​:UpdateOne(), UpdateMany()

​ 删除 ​:DeleteOne(), DeleteMany()

  1. ​ 查询操作符 ​

​ 比较 ​:$eq, $gt, $lt, $in

​ 逻辑 ​:$and, $or, $not

​ 数组 ​:$all, $elemMatch, $size

​ 元素 ​:$exists, $type

  1. ​ 聚合管道 ​

​$match​:过滤文档

​$group​:分组统计

​$sort​:排序

​$project​:投影字段

​$unwind​:展开数组

  1. ​ 最佳实践 ​

​ 连接池管理 ​:合理配置连接池参数

​ 索引优化 ​:为常用查询字段创建索引

​ 文档设计 ​:合理设计文档结构

​ 错误处理 ​:正确处理 MongoDB 错误

​ 事务管理 ​:复杂操作使用事务

  1. ​ 适用场景 ​

​ 内容管理系统 ​:文章、评论、标签

​ 物联网应用 ​:设备数据、传感器读数

​ 实时分析 ​:日志分析、用户行为

​ 电商平台 ​:商品目录、订单管理

​ 社交网络 ​:用户资料、关系网络

MongoDB 的灵活性和高性能使其成为现代应用开发的理想选择,特别适合处理非结构化数据和需要快速迭代的项目。

通关密语:mongo