Golang中使用Couchbase框架实现单桶数据库方案

Golang中使用Couchbase框架实现单桶数据库方案 我们的目标是寻找一种解决方案,能够将不同的结构体存储在一个Couchbase桶中,并提供引用其他文档而非嵌入JSON的可能性。

您的评审将对我们非常有帮助,请给我们一些反馈!

GitHub

PumpkinSeed/bucket

专为单桶使用优化的Couchbase数据结构框架 - PumpkinSeed/bucket


更多关于Golang中使用Couchbase框架实现单桶数据库方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中使用Couchbase框架实现单桶数据库方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Gouchbase中使用单桶存储方案时,通过引用而非嵌入JSON来关联不同结构体是一个常见的需求。以下是一个基于Couchbase Go SDK的实现方案,展示如何存储具有引用关系的文档:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/couchbase/gocb/v2"
)

// 定义用户结构体
type User struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"created_at"`
}

// 定义订单结构体,包含用户引用
type Order struct {
	ID        string    `json:"id"`
	UserID    string    `json:"user_id"` // 引用用户文档
	Amount    float64   `json:"amount"`
	CreatedAt time.Time `json:"created_at"`
}

const (
	bucketName = "my_bucket"
)

func main() {
	// 连接Couchbase集群
	cluster, err := gocb.Connect("couchbase://localhost", gocb.ClusterOptions{
		Authenticator: gocb.PasswordAuthenticator{
			Username: "Administrator",
			Password: "password",
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	// 获取桶
	bucket := cluster.Bucket(bucketName)
	
	// 等待桶就绪
	err = bucket.WaitUntilReady(5*time.Second, nil)
	if err != nil {
		log.Fatal(err)
	}

	// 获取集合(默认集合)
	collection := bucket.DefaultCollection()

	// 创建用户文档
	user := User{
		ID:        "user_001",
		Name:      "John Doe",
		Email:     "john@example.com",
		CreatedAt: time.Now(),
	}

	// 存储用户文档
	_, err = collection.Upsert("user_001", user, &gocb.UpsertOptions{})
	if err != nil {
		log.Fatal(err)
	}

	// 创建订单文档,引用用户ID
	order := Order{
		ID:        "order_001",
		UserID:    "user_001", // 引用用户文档
		Amount:    99.99,
		CreatedAt: time.Now(),
	}

	// 存储订单文档
	_, err = collection.Upsert("order_001", order, &gocb.UpsertOptions{})
	if err != nil {
		log.Fatal(err)
	}

	// 查询订单并获取关联的用户信息
	orderResult, err := collection.Get("order_001", &gocb.GetOptions{})
	if err != nil {
		log.Fatal(err)
	}

	var retrievedOrder Order
	err = orderResult.Content(&retrievedOrder)
	if err != nil {
		log.Fatal(err)
	}

	// 通过引用获取用户信息
	userResult, err := collection.Get(retrievedOrder.UserID, &gocb.GetOptions{})
	if err != nil {
		log.Fatal(err)
	}

	var retrievedUser User
	err = userResult.Content(&retrievedUser)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("订单金额: $%.2f\n", retrievedOrder.Amount)
	fmt.Printf("用户姓名: %s\n", retrievedUser.Name)
	fmt.Printf("用户邮箱: %s\n", retrievedUser.Email)
}

对于更复杂的引用关系,可以使用N1QL查询来连接相关文档:

// 使用N1QL查询连接订单和用户
func queryOrdersWithUsers(cluster *gocb.Cluster) {
	query := `
		SELECT o.*, u.name as user_name, u.email as user_email
		FROM ` + bucketName + ` o
		JOIN ` + bucketName + ` u ON KEYS o.user_id
		WHERE o._type = "order"
	`

	rows, err := cluster.Query(query, &gocb.QueryOptions{})
	if err != nil {
		log.Fatal(err)
	}

	for rows.Next() {
		var result map[string]interface{}
		err := rows.Row(&result)
		if err != nil {
			log.Fatal(err)
		}
		
		fmt.Printf("订单ID: %s, 金额: $%.2f, 用户: %s\n", 
			result["id"], result["amount"], result["user_name"])
	}
}

这种方案的优势:

  • 避免文档嵌套过深导致的性能问题
  • 支持文档间的灵活关联
  • 便于单独更新关联文档
  • 符合关系型数据建模的思维习惯

关键实现要点:

  1. 使用字符串字段存储引用文档的ID
  2. 通过多次Get操作或N1QL JOIN查询获取关联数据
  3. 保持文档结构的扁平化
  4. 使用合适的文档键命名策略
回到顶部