github.com/GuanceCloud/cliutils@v1.1.21/pipeline/ptinput/refertable/table.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 // Package refertable for saving external data 7 package refertable 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "strings" 13 "sync" 14 15 "github.com/spf13/cast" 16 ) 17 18 const ( 19 columnTypeStr = "string" 20 columnTypeInt = "int" 21 columnTypeFloat = "float" 22 columnTypeBool = "bool" 23 ) 24 25 type PlReferTables interface { 26 Query(tableName string, colName []string, colValue []any, kGet []string) (map[string]any, bool) 27 updateAll(tables []referTable) (retErr error) 28 Stats() *ReferTableStats 29 } 30 31 type PlReferTablesInMemory struct { 32 tables map[string]*referTable 33 tablesName []string 34 updateMutex sync.Mutex 35 queryRWmutex sync.RWMutex 36 } 37 38 type ReferTableStats struct { 39 Name []string 40 Row []int 41 } 42 43 func (plrefer *PlReferTablesInMemory) Query(tableName string, colName []string, colValue []any, 44 kGet []string, 45 ) (map[string]any, bool) { 46 plrefer.queryRWmutex.RLock() 47 defer plrefer.queryRWmutex.RUnlock() 48 49 var table *referTable 50 51 if tableName == "" { 52 return nil, false 53 } 54 55 table = plrefer.tables[tableName] 56 if table == nil { 57 return nil, false 58 } 59 60 var row []any 61 if allRow, ok := query(table.index, colName, colValue, 1); ok { 62 row = table.RowData[allRow[0]] 63 } else { 64 return nil, false 65 } 66 67 result := map[string]any{} 68 69 if len(kGet) != 0 { 70 for _, key := range kGet { 71 cIdx, ok := table.colIndex[key] 72 if !ok { 73 continue 74 } 75 result[key] = row[cIdx] 76 } 77 } else { 78 for k, v := range table.colIndex { 79 result[k] = row[v] 80 } 81 } 82 83 return result, true 84 } 85 86 func (plrefer *PlReferTablesInMemory) updateAll(tables []referTable) (retErr error) { 87 defer func() { 88 if err := recover(); err != nil { 89 retErr = fmt.Errorf("run pl: %s", err) 90 } 91 }() 92 93 plrefer.updateMutex.Lock() 94 defer plrefer.updateMutex.Unlock() 95 96 refTableMap := map[string]*referTable{} 97 tablesName := []string{} 98 for idx := range tables { 99 table := tables[idx] 100 if err := table.buildTableIndex(); err != nil { 101 return err 102 } 103 if _, ok := refTableMap[table.TableName]; !ok { 104 refTableMap[table.TableName] = &table 105 tablesName = append(tablesName, table.TableName) 106 } 107 } 108 109 plrefer.queryRWmutex.Lock() 110 defer plrefer.queryRWmutex.Unlock() 111 plrefer.tables = refTableMap 112 plrefer.tablesName = tablesName 113 return nil 114 } 115 116 func (plrefer *PlReferTablesInMemory) Stats() *ReferTableStats { 117 plrefer.queryRWmutex.RLock() 118 defer plrefer.queryRWmutex.RUnlock() 119 120 tableStats := ReferTableStats{} 121 for _, name := range plrefer.tablesName { 122 tableStats.Name = append(tableStats.Name, name) 123 124 tableStats.Row = append(tableStats.Row, 125 len(plrefer.tables[name].RowData)) 126 } 127 128 return &tableStats 129 } 130 131 type referTable struct { 132 TableName string `json:"table_name"` 133 ColumnName []string `json:"column_name"` 134 ColumnType []string `json:"column_type"` 135 RowData [][]any `json:"row_data"` 136 137 // 要求 []int 中的行号递增 138 index map[string]map[any][]int 139 140 colIndex map[string]int 141 } 142 143 func (table *referTable) check() error { 144 if table.TableName == "" { 145 return fmt.Errorf("table: \"%s\", error: empty table name", table.TableName) 146 } 147 if len(table.ColumnName) != len(table.ColumnType) { 148 return fmt.Errorf("table: %s, error: len(table.ColumnName) != len(table.ColumnType)", 149 table.TableName) 150 } 151 152 for idx, row := range table.RowData { 153 if len(table.ColumnName) != len(row) { 154 return fmt.Errorf("table: %s, col: %d, error: len(table.ColumnName) != len(table.RowData[%d])", 155 table.TableName, idx, idx) 156 } 157 } 158 159 for idx, columnName := range table.ColumnName { 160 if columnName == "" { 161 return fmt.Errorf("table: %s, column: %v, index: %d, value: \"\"", 162 table.TableName, table.ColumnName, idx) 163 } 164 } 165 166 for idx, columnType := range table.ColumnType { 167 switch columnType { 168 case columnTypeInt, columnTypeFloat, 169 columnTypeBool, columnTypeStr: 170 default: 171 return fmt.Errorf("table: %s, unsupported column type: %s -> %s", 172 table.TableName, table.ColumnName[idx], columnType) 173 } 174 } 175 176 return nil 177 } 178 179 func (table *referTable) buildTableIndex() error { 180 if err := table.check(); err != nil { 181 return err 182 } 183 184 table.index = map[string]map[any][]int{} 185 table.colIndex = map[string]int{} 186 187 // 遍历行 188 for rowIdx, row := range table.RowData { 189 // 遍历列,建立索引: colName -> colValue -> []rowIndex 190 for colIdx := 0; colIdx < len(table.ColumnName); colIdx++ { 191 colName := table.ColumnName[colIdx] 192 193 if _, ok := table.index[colName]; !ok { 194 table.colIndex[colName] = colIdx 195 table.index[colName] = map[any][]int{} 196 } 197 198 // 列数据转换为指定类型 199 v, err := conv(row[colIdx], table.ColumnType[colIdx]) 200 if err != nil { 201 return fmt.Errorf("table: %s, row: %d, col: %d, cast error: %w", 202 table.TableName, rowIdx, colIdx, err) 203 } 204 row[colIdx] = v 205 // column 反向索引: colValue -> []rowIndex 206 table.index[colName][v] = append(table.index[colName][v], 207 rowIdx) 208 } 209 } 210 211 return nil 212 } 213 214 func conv(col any, dtype string) (any, error) { 215 switch strings.ToLower(dtype) { 216 case columnTypeBool: 217 return cast.ToBoolE(col) 218 case columnTypeInt: 219 return cast.ToInt64E(col) 220 case columnTypeFloat: 221 return cast.ToFloat64E(col) 222 case columnTypeStr: 223 return cast.ToStringE(col) 224 default: 225 return nil, fmt.Errorf("unsupported type: %s", dtype) 226 } 227 } 228 229 func decodeJSONData(data []byte) ([]referTable, error) { 230 var tables []referTable 231 if err := json.Unmarshal(data, &tables); err != nil { 232 return nil, err 233 } else { 234 return tables, nil 235 } 236 }