github.com/godaddy-x/freego@v1.0.156/ormx/sqld/model_index.go (about) 1 package sqld 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 "github.com/godaddy-x/freego/ormx/sqlc" 8 "github.com/godaddy-x/freego/utils" 9 "github.com/godaddy-x/freego/zlog" 10 "go.mongodb.org/mongo-driver/bson" 11 "go.mongodb.org/mongo-driver/mongo" 12 "go.mongodb.org/mongo-driver/mongo/options" 13 "reflect" 14 "sort" 15 ) 16 17 type IndexInfo struct { 18 Table string // 索引所属的表名 19 NonUnique int // 索引是否是唯一索引。如果值为 0,则表示索引是唯一索引;如果值为 1,则表示索引是普通索引 20 KeyName string // 索引的名称 21 SeqInIndex int // 索引中的列的顺序号 22 ColumnName string // 索引的列名 23 Collation string // 索引列的排序规则 24 Cardinality interface{} // 索引列的基数,即不重复的索引值数量 25 SubPart sql.NullString // 索引的子部分长度。通常用于前缀索引,以指示索引的前缀长度 26 Packed sql.NullString // 索引存储的方式 27 Null interface{} // 索引列是否可以包含 NULL 值 28 IndexType string // 索引的类型,如 BTREE、HASH 等 29 Comment string // 索引的注释信息 30 IndexComment string // 索引的额外注释信息 31 } 32 33 func readyCollection(object sqlc.Object) { 34 db, err := NewMongo(Option{Timeout: 120000}) 35 if err != nil { 36 panic(err) 37 } 38 defer db.Close() 39 if err := db.Save(object); err != nil { 40 panic(err) 41 } 42 if err := db.Delete(object); err != nil { 43 panic(err) 44 } 45 } 46 47 func dropMongoIndex(object sqlc.Object, index []sqlc.Index) bool { 48 readyCollection(object) 49 db, err := NewMongo(Option{Timeout: 120000}) 50 if err != nil { 51 panic(err) 52 } 53 defer db.Close() 54 coll, err := db.GetDatabase(object.GetTable()) 55 if err != nil { 56 panic(err) 57 } 58 cur, err := coll.Indexes().List(context.Background()) 59 if err != nil { 60 panic(err) 61 } 62 var list []map[string]interface{} 63 if err := cur.All(context.Background(), &list); err != nil { 64 panic(err) 65 } 66 oldKey := "" 67 for _, v := range list { 68 key := v["name"].(string) 69 if key == "_id_" { 70 continue 71 } 72 oldKey += key 73 } 74 newKey := "" 75 for _, v := range index { 76 newKey += v.Name 77 } 78 if oldKey == newKey { 79 return false 80 } 81 if _, err := coll.Indexes().DropAll(context.Background()); err != nil { 82 panic(err) 83 } 84 return true 85 } 86 87 func dropMysqlIndex(object sqlc.Object, index []sqlc.Index) bool { 88 db, err := NewMysql(Option{Timeout: 120000}) 89 if err != nil { 90 panic(err) 91 } 92 defer db.Close() 93 // 执行查询获取索引信息 94 rows, err := db.Db.Query("SHOW INDEX FROM " + object.GetTable()) 95 if err != nil { 96 panic(err) 97 } 98 defer rows.Close() 99 100 // 获取查询结果的字段名称 101 columns, err := rows.Columns() 102 if err != nil { 103 panic(err) 104 } 105 106 // 创建一个动态映射,用于存储字段名和对应的值 107 result := make(map[string]interface{}) 108 values := make([]interface{}, len(columns)) 109 for i := range columns { 110 values[i] = new(sql.RawBytes) 111 } 112 113 var indexes []IndexInfo 114 for rows.Next() { 115 if err := rows.Scan(values...); err != nil { 116 panic(err) 117 } 118 for i, column := range columns { 119 if values[i] == nil { 120 result[column] = nil // 或者设置为其他默认值 121 continue 122 } 123 result[column] = values[i] 124 } 125 var index IndexInfo 126 index.Table = string(*result["Table"].(*sql.RawBytes)) 127 index.KeyName = string(*result["Key_name"].(*sql.RawBytes)) 128 index.ColumnName = string(*result["Column_name"].(*sql.RawBytes)) 129 index.IndexType = string(*result["Index_type"].(*sql.RawBytes)) 130 nonUnique, err := utils.StrToInt(string(*result["Non_unique"].(*sql.RawBytes))) 131 if err != nil { 132 panic(err) 133 } 134 index.NonUnique = nonUnique 135 indexes = append(indexes, index) 136 } 137 138 check := map[string][]string{} 139 for _, v := range indexes { 140 key := v.KeyName 141 if key == "PRIMARY" { 142 continue 143 } 144 m, b := check[key] 145 if b { 146 check[v.KeyName] = append(m, v.ColumnName) 147 } else { 148 check[v.KeyName] = []string{v.ColumnName} 149 } 150 } 151 var drop bool 152 for _, v := range index { 153 if len(v.Name) == 0 || len(v.Key) == 0 { 154 panic("table index name/key invalid: " + object.GetTable()) 155 } 156 key, b := check[v.Name] 157 if b { 158 sort.Strings(key) 159 sort.Strings(v.Key) 160 if reflect.DeepEqual(key, v.Key) { 161 continue 162 } 163 } 164 drop = true 165 break 166 } 167 if !drop { 168 return false 169 } 170 for k, _ := range check { // 确定删除表所有索引 171 if _, err := db.Db.Exec("DROP INDEX `" + k + "` ON " + object.GetTable()); err != nil { 172 panic(err) 173 } 174 } 175 return true 176 } 177 178 func addMongoIndex(object sqlc.Object, index sqlc.Index) error { 179 db, err := NewMongo(Option{Timeout: 120000}) 180 if err != nil { 181 panic(err) 182 } 183 defer db.Close() 184 coll, err := db.GetDatabase(object.GetTable()) 185 if err != nil { 186 panic(err) 187 } 188 bsonD := bson.D{} 189 for _, v := range index.Key { 190 bsonD = append(bsonD, bson.E{Key: v, Value: 1}) 191 } 192 modelIndex := mongo.IndexModel{ 193 Keys: bsonD, Options: &options.IndexOptions{Name: &index.Name, Unique: &index.Unique}, 194 } 195 if _, err := coll.Indexes().CreateOne(context.Background(), modelIndex); err != nil { 196 panic(err) 197 } 198 return nil 199 } 200 201 func addMysqlIndex(object sqlc.Object, index sqlc.Index) error { 202 if len(index.Key) == 0 { 203 zlog.Warn("addMysqlIndex keys is nil", 0, zlog.Any("object", object)) 204 return nil 205 } 206 if len(index.Name) == 0 { 207 panic("index key name is nil: " + object.GetTable()) 208 } 209 var columns string 210 for _, v := range index.Key { 211 if len(v) == 0 { 212 panic("index key field is nil: " + object.GetTable()) 213 } 214 columns += utils.AddStr(",`", v, "`") 215 } 216 sql := "CREATE" 217 if index.Unique { 218 sql = utils.AddStr(sql, " UNIQUE ") 219 } 220 sql = utils.AddStr(sql, " INDEX ") 221 sql = utils.AddStr(sql, "`", index.Name, "`") 222 sql = utils.AddStr(sql, " ON ", object.GetTable(), " (") 223 sql = utils.AddStr(sql, columns[1:], ")") 224 225 db, err := NewMysql(Option{Timeout: 120000}) 226 if err != nil { 227 panic(err) 228 } 229 defer db.Close() 230 if _, err := db.Db.Exec(sql); err != nil { 231 panic(err) 232 } 233 return nil 234 } 235 236 // RebuildMongoDBIndex 先删除所有表索引,再按配置新建(线上慎用功能) 237 func RebuildMongoDBIndex() error { 238 for _, model := range modelDrivers { 239 index := model.Object.NewIndex() 240 if index == nil { 241 continue 242 } 243 if !dropMongoIndex(model.Object, index) { 244 fmt.Println(fmt.Sprintf("********* [%s] index consistent, skipping *********", model.Object.GetTable())) 245 continue 246 } 247 fmt.Println(fmt.Sprintf("********* [%s] delete all index *********", model.Object.GetTable())) 248 for _, v := range index { 249 addMongoIndex(model.Object, v) 250 fmt.Println(fmt.Sprintf("********* [%s] add index [%s] *********", model.Object.GetTable(), v.Name)) 251 } 252 } 253 return nil 254 } 255 256 func checkMysqlTable(tableName string) (bool, error) { 257 db, err := NewMysql(Option{Timeout: 120000}) 258 if err != nil { 259 panic(err) 260 } 261 defer db.Close() 262 var result string 263 if err := db.Db.QueryRow("SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ? LIMIT 1", tableName).Scan(&result); err != nil { 264 if err == sql.ErrNoRows { 265 return false, nil // 表不存在 266 } 267 return false, err // 查询出错 268 } 269 return true, nil // 表存在 270 } 271 272 func isInt(s string) bool { 273 if s == "int64" || s == "int" { 274 return true 275 } 276 return false 277 } 278 279 func createTable(model *MdlDriver) error { 280 sql := utils.AddStr("CREATE TABLE ", model.TableName, "( ") 281 var fields string 282 for _, v := range model.FieldElem { 283 if len(v.FieldDBType) == 0 { 284 if isInt(v.FieldType) { 285 fields = utils.AddStr(fields, ",`", v.FieldJsonName, "` ", "BIGINT") 286 } else { 287 fields = utils.AddStr(fields, ",`", v.FieldJsonName, "` ", "VARCHAR(255)") 288 } 289 } else { 290 fields = utils.AddStr(fields, ",`", v.FieldJsonName, "` ", v.FieldDBType) 291 } 292 if v.Primary { 293 fields = utils.AddStr(fields, " NOT NULL PRIMARY KEY") 294 } 295 if len(v.FieldComment) > 0 { 296 fields = utils.AddStr(fields, " COMMENT '", v.FieldComment, "'") 297 } 298 } 299 sql = utils.AddStr(sql, fields[1:], ")") 300 sql = utils.AddStr(sql, " ENGINE=InnoDB DEFAULT CHARSET=", model.Charset, " COLLATE=", model.Collate, ";") 301 db, err := NewMysql(Option{Timeout: 120000}) 302 if err != nil { 303 panic(err) 304 } 305 defer db.Close() 306 if _, err := db.Db.Exec(sql); err != nil { 307 return err 308 } 309 zlog.Info("create table success", 0, zlog.String("table", model.TableName)) 310 return nil 311 } 312 313 // RebuildMysqlDBIndex 先删除所有表索引,再按配置新建(线上慎用功能) 314 func RebuildMysqlDBIndex() error { 315 for _, model := range modelDrivers { 316 index := model.Object.NewIndex() 317 if len(index) == 0 { 318 continue 319 } 320 exist, err := checkMysqlTable(model.Object.GetTable()) 321 if err != nil { 322 panic(err) 323 } 324 if !exist { 325 zlog.Warn("mysql table not exist", 0, zlog.String("table", model.Object.GetTable())) 326 if err := createTable(model); err != nil { 327 panic(err) 328 } 329 } 330 if !dropMysqlIndex(model.Object, index) { 331 fmt.Println(fmt.Sprintf("********* [%s] index consistent, skipping *********", model.Object.GetTable())) 332 continue 333 } 334 fmt.Println(fmt.Sprintf("********* [%s] delete all index *********", model.Object.GetTable())) 335 for _, v := range index { 336 addMysqlIndex(model.Object, v) 337 fmt.Println(fmt.Sprintf("********* [%s] add index [%s] *********", model.Object.GetTable(), v.Name)) 338 } 339 } 340 return nil 341 }