blockchain/internal/lib/cassandra/metadata.go

70 lines
1.8 KiB
Go

package cassandra
import (
"fmt"
"reflect"
"github.com/scylladb/gocqlx/v3/table"
)
// GenerateTableMetadata 根據傳入的 struct 產生 table.Metadata
func GenerateTableMetadata(document any, keyspace string) (table.Metadata, error) {
// 取得型別資訊,若是指標則取 Elem
t := reflect.TypeOf(document)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 取得表名稱:若 model 有實作 TableName() 則使用該方法,否則轉換型別名稱為 snake_case
var tableName string
if tm, ok := document.(interface{ TableName() string }); ok {
tableName = fmt.Sprintf("%s.%s", keyspace, tm.TableName())
} else {
return table.Metadata{}, fmt.Errorf("failed to get table func")
}
columns := make([]string, 0, t.NumField())
partKeys := make([]string, 0, t.NumField())
sortKeys := make([]string, 0, t.NumField())
// 遍歷所有 exported 欄位
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// 跳過 unexported 欄位
if field.PkgPath != "" {
continue
}
// 如果欄位有標記 db:"-" 則跳過
if tag := field.Tag.Get("db"); tag == "-" {
continue
}
// 取得欄位名稱
colName := field.Tag.Get("db")
if colName == "" {
colName = toSnakeCase(field.Name)
}
columns = append(columns, colName)
// 若有 partition:"true" 標記,加入 PartKey
if field.Tag.Get("partition_key") == "true" {
partKeys = append(partKeys, colName)
}
// 若有 sort:"true" 標記,加入 SortKey
if field.Tag.Get("clustering_key") == "true" {
sortKeys = append(sortKeys, colName)
}
}
if len(partKeys) == 0 {
return table.Metadata{}, fmt.Errorf("no partition key defined in struct")
}
// 組合 Metadata
meta := table.Metadata{
Name: tableName,
Columns: columns,
PartKey: partKeys,
SortKey: sortKeys,
}
return meta, nil
}