159 lines
3.6 KiB
Markdown
159 lines
3.6 KiB
Markdown
|
|
# Cassandra2 - 新一代 Cassandra 客戶端
|
|||
|
|
|
|||
|
|
Cassandra2 是重新設計的 Cassandra 客戶端,提供更簡潔的 API、更好的類型安全性和更清晰的架構。
|
|||
|
|
|
|||
|
|
## 特色
|
|||
|
|
|
|||
|
|
- ✅ **Repository 模式**:每個 Repository 綁定一個 keyspace,無需到處傳遞
|
|||
|
|
- ✅ **類型安全**:使用泛型,編譯期類型檢查
|
|||
|
|
- ✅ **簡潔的 API**:統一的查詢介面,流暢的鏈式調用
|
|||
|
|
- ✅ **符合 cursor.md 原則**:小介面、依賴注入、顯式錯誤處理
|
|||
|
|
|
|||
|
|
## 快速開始
|
|||
|
|
|
|||
|
|
### 1. 初始化
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import "your-module/pkg/library/cassandra"
|
|||
|
|
|
|||
|
|
// 創建 DB 連接
|
|||
|
|
db, err := cassandra2.New(
|
|||
|
|
cassandra2.WithHosts("localhost"),
|
|||
|
|
cassandra2.WithKeyspace("my_keyspace"),
|
|||
|
|
cassandra2.WithPort(9042),
|
|||
|
|
)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
defer db.Close()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 定義資料模型
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type User struct {
|
|||
|
|
ID gocql.UUID `db:"id" partition_key:"true"`
|
|||
|
|
Name string `db:"name"`
|
|||
|
|
Email string `db:"email"`
|
|||
|
|
CreatedAt time.Time `db:"created_at"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (u User) TableName() string {
|
|||
|
|
return "users"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 使用 Repository
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 獲取 Repository
|
|||
|
|
repo, err := db.Repository[User]("my_keyspace")
|
|||
|
|
|
|||
|
|
// 插入
|
|||
|
|
user := User{
|
|||
|
|
ID: gocql.TimeUUID(),
|
|||
|
|
Name: "Alice",
|
|||
|
|
Email: "alice@example.com",
|
|||
|
|
CreatedAt: time.Now(),
|
|||
|
|
}
|
|||
|
|
err = repo.Insert(ctx, user)
|
|||
|
|
|
|||
|
|
// 查詢
|
|||
|
|
var result User
|
|||
|
|
result, err = repo.Get(ctx, user.ID)
|
|||
|
|
|
|||
|
|
// 更新
|
|||
|
|
user.Email = "newemail@example.com"
|
|||
|
|
err = repo.Update(ctx, user)
|
|||
|
|
|
|||
|
|
// 刪除
|
|||
|
|
err = repo.Delete(ctx, user.ID)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 使用 Query Builder
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 條件查詢
|
|||
|
|
var users []User
|
|||
|
|
err = repo.Query().
|
|||
|
|
Where(cassandra2.Eq("status", "active")).
|
|||
|
|
OrderBy("created_at", cassandra2.DESC).
|
|||
|
|
Limit(10).
|
|||
|
|
Scan(ctx, &users)
|
|||
|
|
|
|||
|
|
// 單筆查詢
|
|||
|
|
user, err := repo.Query().
|
|||
|
|
Where(cassandra2.Eq("id", userID)).
|
|||
|
|
One(ctx)
|
|||
|
|
|
|||
|
|
// 計數
|
|||
|
|
count, err := repo.Query().
|
|||
|
|
Where(cassandra2.Eq("status", "active")).
|
|||
|
|
Count(ctx)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. Batch 操作
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
batch := repo.Batch(ctx)
|
|||
|
|
batch.Insert(user1).
|
|||
|
|
Insert(user2).
|
|||
|
|
Update(user3)
|
|||
|
|
err = batch.Commit(ctx)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6. Transaction 操作
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
tx := db.Begin(ctx, "my_keyspace")
|
|||
|
|
tx.Insert(user1)
|
|||
|
|
tx.Update(user2)
|
|||
|
|
if err := tx.Commit(ctx); err != nil {
|
|||
|
|
tx.Rollback(ctx)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## API 對比
|
|||
|
|
|
|||
|
|
### 舊 API (Cassandra 1)
|
|||
|
|
```go
|
|||
|
|
db.Insert(ctx, user, "keyspace")
|
|||
|
|
db.Model(ctx, &User{}, "keyspace").Where(...).Scan(&result)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 新 API (Cassandra 2)
|
|||
|
|
```go
|
|||
|
|
repo := db.Repository[User]("keyspace")
|
|||
|
|
repo.Insert(ctx, user)
|
|||
|
|
repo.Query().Where(...).Scan(ctx, &result)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 主要改進
|
|||
|
|
|
|||
|
|
1. **移除 keyspace 參數**:Repository 綁定 keyspace,無需重複傳遞
|
|||
|
|
2. **類型安全**:使用泛型,編譯期檢查
|
|||
|
|
3. **統一 API**:只有一套查詢介面
|
|||
|
|
4. **更好的錯誤處理**:統一的錯誤類型,支援 errors.Is/As
|
|||
|
|
|
|||
|
|
## 注意事項
|
|||
|
|
|
|||
|
|
1. **主鍵查詢**:`Get` 方法需要完整的 Primary Key。如果是多欄位主鍵,需要傳入包含所有主鍵欄位的 struct。
|
|||
|
|
2. **更新行為**:`Update` 預設只更新非零值欄位,使用 `UpdateAll` 可更新所有欄位。
|
|||
|
|
3. **Transaction**:這是補償式交易,不是真正的 ACID 交易,適用於最終一致性場景。
|
|||
|
|
|
|||
|
|
## 遷移指南
|
|||
|
|
|
|||
|
|
從 Cassandra 1 遷移到 Cassandra 2:
|
|||
|
|
|
|||
|
|
1. 將 `cassandra.NewCassandraDB` 改為 `cassandra2.New`
|
|||
|
|
2. 將 `db.Insert(ctx, doc, keyspace)` 改為 `repo.Insert(ctx, doc)`,其中 `repo = db.Repository[Type](keyspace)`
|
|||
|
|
3. 將 `db.Model(...)` 改為 `repo.Query()`
|
|||
|
|
4. 更新錯誤處理:使用 `cassandra2.IsNotFound` 等函數
|
|||
|
|
|
|||
|
|
## 文檔
|
|||
|
|
|
|||
|
|
詳細的技術設計請參考:
|
|||
|
|
- `REFACTORING_PLAN.md` - 重構計畫
|
|||
|
|
- `TECHNICAL_DESIGN.md` - 技術設計文檔
|
|||
|
|
|