| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- package DB
- import (
- "TsDB2/DB/core/page"
- "TsDB2/DB/core/types"
- "fmt"
- "reflect"
- )
- // FieldDef ORM 字段定义
- type FieldDef struct {
- Name string // Go 字段名
- ColName string // 数据库列名 (来自 tsdb tag 或字段名)
- FieldType types.FieldType // 存储类型
- GoKind reflect.Kind // Go 原始类型
- Index int // 在结构体中的字段索引
- }
- // ModelInfo 模型元信息
- type ModelInfo struct {
- Fields []FieldDef // 用户定义字段 (不含自动主键)
- AllFields []FieldDef // 全部字段 (含自动主键,按存储顺序)
- PKField string // 主键列名 ("_id" 或 "_ts")
- PKIndex int // 主键在 AllFields 中的索引
- IsTimeSeries bool // 是否时序表
- CoreFields []types.FieldInfo // 传给 core 的字段列表 (含自动主键)
- CorePKField byte // 主键在 CoreFields 中的索引 (用于 CreateIndex)
- StructType reflect.Type // 结构体类型 (用于反射)
- PKInStruct bool // 主键字段是否在结构体中 (嵌入基类时有)
- }
- // goKindToFieldType Go 类型到数据库字段类型的默认映射
- var goKindToFieldType = map[reflect.Kind]types.FieldType{
- reflect.Int8: types.FieldType_1byte,
- reflect.Int16: types.FieldType_2byte,
- reflect.Int32: types.FieldType_4byte,
- reflect.Int64: types.FieldType_8byte,
- reflect.Int: types.FieldType_8byte,
- reflect.Uint8: types.FieldType_1byte,
- reflect.Uint16: types.FieldType_2byte,
- reflect.Uint32: types.FieldType_4byte,
- reflect.Uint64: types.FieldType_8byte,
- reflect.Float32: types.FieldType_4byte,
- reflect.Float64: types.FieldType_8byte,
- reflect.Bool: types.FieldType_1byte,
- }
- // tagToFieldType 结构体标签到数据库字段类型的映射
- var tagToFieldType = map[string]types.FieldType{
- "1byte": types.FieldType_1byte,
- "2byte": types.FieldType_2byte,
- "4byte": types.FieldType_4byte,
- "8byte": types.FieldType_8byte,
- "16byte": types.FieldType_16byte,
- "32byte": types.FieldType_32byte,
- "64byte": types.FieldType_64byte,
- "128byte": types.FieldType_128byte,
- "256byte": types.FieldType_256byte,
- "dynamic": types.FieldType_Dynamicbyte,
- }
- var (
- baseModelType = reflect.TypeOf(BaseModel{})
- timeSeriesModelType = reflect.TypeOf(TimeSeriesModel{})
- )
- // ParseModel 解析结构体,提取模型信息
- // obj 必须是指向结构体的指针 (可以是零值)
- func ParseModel(obj interface{}) (*ModelInfo, error) {
- t := reflect.TypeOf(obj)
- if t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
- if t.Kind() != reflect.Struct {
- return nil, fmt.Errorf("tsdb: obj must be a struct, got %s", t.Kind())
- }
- info := &ModelInfo{
- StructType: t,
- PKField: "_id",
- }
- // 扫描字段,检测嵌入的基类
- fieldIdx := 0
- for i := 0; i < t.NumField(); i++ {
- sf := t.Field(i)
- // 处理嵌入结构体
- if sf.Anonymous {
- if sf.Type == baseModelType {
- info.IsTimeSeries = false
- info.PKField = "_id"
- info.PKInStruct = true
- // 添加 _id 主键列
- info.AllFields = append(info.AllFields, FieldDef{
- Name: "ID",
- ColName: "_id",
- FieldType: types.FieldType_8byte,
- GoKind: reflect.Uint64,
- Index: len(info.AllFields),
- })
- info.PKIndex = len(info.AllFields) - 1
- continue
- }
- if sf.Type == timeSeriesModelType {
- info.IsTimeSeries = true
- info.PKField = "_ts"
- info.PKInStruct = true
- // 添加 _ts 主键列
- info.AllFields = append(info.AllFields, FieldDef{
- Name: "TS",
- ColName: "_ts",
- FieldType: types.FieldType_8byte,
- GoKind: reflect.Uint64,
- Index: len(info.AllFields),
- })
- info.PKIndex = len(info.AllFields) - 1
- continue
- }
- // 其他嵌入结构体: 递归展开
- if sf.Type.Kind() == reflect.Struct {
- if err := parseEmbeddedFields(sf.Type, info, &fieldIdx); err != nil {
- return nil, err
- }
- continue
- }
- continue
- }
- // 普通字段
- fd, err := parseField(sf, fieldIdx)
- if err != nil {
- return nil, fmt.Errorf("field %s: %w", sf.Name, err)
- }
- info.Fields = append(info.Fields, fd)
- info.AllFields = append(info.AllFields, fd)
- fieldIdx++
- }
- // 如果没有嵌入任何基类,默认添加 _id 主键
- if len(info.AllFields) == 0 || (info.PKField != "_id" && info.PKField != "_ts") {
- info.PKField = "_id"
- info.IsTimeSeries = false
- info.PKInStruct = false
- pk := FieldDef{
- Name: "ID",
- ColName: "_id",
- FieldType: types.FieldType_8byte,
- GoKind: reflect.Uint64,
- Index: 0,
- }
- // 将 PK 插入到 AllFields 开头
- newAll := make([]FieldDef, 0, len(info.AllFields)+1)
- newAll = append(newAll, pk)
- for j, f := range info.AllFields {
- f.Index = j + 1
- newAll = append(newAll, f)
- }
- for j := range info.Fields {
- info.Fields[j].Index++
- }
- info.AllFields = newAll
- info.PKIndex = 0
- }
- // 构建 CoreFields
- info.CoreFields = make([]types.FieldInfo, len(info.AllFields))
- for i, f := range info.AllFields {
- info.CoreFields[i] = types.FieldInfo{
- FieldType: f.FieldType,
- FieldName: []byte(f.ColName),
- }
- }
- info.CorePKField = byte(info.PKIndex)
- return info, nil
- }
- // parseEmbeddedFields 递归解析嵌入结构体的字段
- func parseEmbeddedFields(t reflect.Type, info *ModelInfo, fieldIdx *int) error {
- for i := 0; i < t.NumField(); i++ {
- sf := t.Field(i)
- if sf.Anonymous && sf.Type.Kind() == reflect.Struct {
- if err := parseEmbeddedFields(sf.Type, info, fieldIdx); err != nil {
- return err
- }
- continue
- }
- fd, err := parseField(sf, *fieldIdx)
- if err != nil {
- return err
- }
- info.Fields = append(info.Fields, fd)
- info.AllFields = append(info.AllFields, fd)
- *fieldIdx++
- }
- return nil
- }
- // parseField 解析单个结构体字段
- func parseField(sf reflect.StructField, idx int) (FieldDef, error) {
- fd := FieldDef{
- Name: sf.Name,
- GoKind: sf.Type.Kind(),
- Index: idx,
- }
- // 列名: 优先使用 tsdb tag 中的列名
- fd.ColName = sf.Name
- if tag, ok := sf.Tag.Lookup("tsdb"); ok && tag != "" {
- // tag 格式: "列名" 或 "列名,类型" 或 "类型"
- parts := parseTag(tag)
- if len(parts) >= 1 && parts[0] != "" {
- // 如果第一部分不是已知类型名,则视为列名
- if _, isType := tagToFieldType[parts[0]]; !isType {
- fd.ColName = parts[0]
- }
- }
- }
- // 字段类型: 优先使用 tsdb tag
- if tag, ok := sf.Tag.Lookup("tsdb"); ok && tag != "" {
- parts := parseTag(tag)
- for _, p := range parts {
- if ft, isType := tagToFieldType[p]; isType {
- fd.FieldType = ft
- return fd, nil
- }
- }
- }
- // 自动推断
- if ft, ok := goKindToFieldType[sf.Type.Kind()]; ok {
- fd.FieldType = ft
- return fd, nil
- }
- // string → 256byte
- if sf.Type.Kind() == reflect.String {
- fd.FieldType = types.FieldType_256byte
- return fd, nil
- }
- // []byte → dynamic
- if sf.Type.Kind() == reflect.Slice && sf.Type.Elem().Kind() == reflect.Uint8 {
- fd.FieldType = types.FieldType_Dynamicbyte
- return fd, nil
- }
- return fd, fmt.Errorf("unsupported type: %s", sf.Type.String())
- }
- // parseTag 解析 tsdb tag: "col_name,type" 或 "type" 或 "col_name"
- func parseTag(tag string) []string {
- var parts []string
- current := ""
- for _, c := range tag {
- if c == ',' {
- parts = append(parts, current)
- current = ""
- } else {
- current += string(c)
- }
- }
- if current != "" {
- parts = append(parts, current)
- }
- return parts
- }
- // GetIndexType 返回该模型应创建的一级索引类型
- func (m *ModelInfo) GetIndexType() page.IndexType {
- if m.IsTimeSeries {
- return page.IndexTypeTime
- }
- return page.IndexTypePrimary
- }
|