forked from mirror/gorm
Create Indexes with AutoMigrate
This commit is contained in:
parent
42f934d60e
commit
6fed43162f
88
README.md
88
README.md
|
@ -35,7 +35,7 @@ type User struct {
|
||||||
ID int
|
ID int
|
||||||
Birthday time.Time
|
Birthday time.Time
|
||||||
Age int
|
Age int
|
||||||
Name string `sql:"size:255"`
|
Name string `sql:"size:255"` // Default size for string is 255, you could reset it with this tag
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
DeletedAt time.Time
|
DeletedAt time.Time
|
||||||
|
@ -51,8 +51,8 @@ type User struct {
|
||||||
|
|
||||||
type Email struct {
|
type Email struct {
|
||||||
ID int
|
ID int
|
||||||
UserID int // Foreign key for User (belongs to)
|
UserID int `sql:"index"` // Foreign key (belongs to), tag `index` will create index for this field when using AutoMigrate
|
||||||
Email string `sql:"type:varchar(100);"` // Set field's type
|
Email string `sql:"type:varchar(100);unique_index"` // Set field's sql type, tag `unique_index` will create unique index
|
||||||
Subscribed bool
|
Subscribed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,8 @@ type Address struct {
|
||||||
|
|
||||||
type Language struct {
|
type Language struct {
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string `sql:"index:idx_name_code"` // Create index with name, and will create combined index if find other fields defined same name
|
||||||
|
Code string `sql:"index:idx_name_code"` // `unique_index` also works
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -74,13 +75,7 @@ type Language struct {
|
||||||
* Table name is the plural of struct name's snake case, you can disable pluralization with `db.SingularTable(true)`, or [Specifying The Table Name For A Struct Permanently With TableName](#specifying-the-table-name-for-a-struct-permanently-with-tablename)
|
* Table name is the plural of struct name's snake case, you can disable pluralization with `db.SingularTable(true)`, or [Specifying The Table Name For A Struct Permanently With TableName](#specifying-the-table-name-for-a-struct-permanently-with-tablename)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// E.g finding an existing User
|
type User struct{} // struct User's database table name is "users" by default, will be "user" if you disabled pluralisation
|
||||||
var user User
|
|
||||||
// Gorm will know to use table "users" ("user" if pluralisation has been disabled) for all operations.
|
|
||||||
db.First(&user)
|
|
||||||
|
|
||||||
// creating a new User
|
|
||||||
db.Save(&User{Name: "xxx"}) // table "users"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
* Column name is the snake case of field's name
|
* Column name is the snake case of field's name
|
||||||
|
@ -92,7 +87,6 @@ db.Save(&User{Name: "xxx"}) // table "users"
|
||||||
## Initialize Database
|
## Initialize Database
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
@ -101,7 +95,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
||||||
// db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True")
|
// db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
|
||||||
// db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
|
// db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
|
||||||
|
|
||||||
// You can also use an existing database connection handle
|
// You can also use an existing database connection handle
|
||||||
|
@ -129,38 +123,13 @@ db.CreateTable(&User{})
|
||||||
// Drop table
|
// Drop table
|
||||||
db.DropTable(&User{})
|
db.DropTable(&User{})
|
||||||
|
|
||||||
// Drop table if exists
|
|
||||||
db.DropTableIfExists(&User{})
|
|
||||||
|
|
||||||
// Automating Migration
|
// Automating Migration
|
||||||
db.AutoMigrate(&User{})
|
db.AutoMigrate(&User{})
|
||||||
db.AutoMigrate(&User{}, &Product{}, &Order{})
|
db.AutoMigrate(&User{}, &Product{}, &Order{})
|
||||||
|
|
||||||
// Feel free to change your struct, AutoMigrate will keep your database up-to-date.
|
// Feel free to change your struct, AutoMigrate will keep your database up-to-date.
|
||||||
// Fyi, AutoMigrate will only *add new columns*, it won't update column's type or delete unused columns, to make sure your data is safe.
|
// AutoMigrate will ONLY add *new columns* and *new indexes*,
|
||||||
|
// WON'T update current column's type or delete unused columns, to protect your data.
|
||||||
// If the table is not existing, AutoMigrate will create the table automatically.
|
// If the table is not existing, AutoMigrate will create the table automatically.
|
||||||
|
|
||||||
// Add index
|
|
||||||
db.Model(&User{}).AddIndex("idx_user_name", "name")
|
|
||||||
|
|
||||||
// Add foreign key
|
|
||||||
// 1st param : foreignkey field
|
|
||||||
// 2nd param : destination table(id)
|
|
||||||
// 3rd param : ONDELETE
|
|
||||||
// 4th param : ONUPDATE
|
|
||||||
db.Model(&User{}).AddForeignKey("user_id", "destination_table(id)", "CASCADE", "RESTRICT")
|
|
||||||
|
|
||||||
// Multiple column index
|
|
||||||
db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
|
|
||||||
|
|
||||||
// Add unique index
|
|
||||||
db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")
|
|
||||||
|
|
||||||
// Multiple column unique index
|
|
||||||
db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
|
|
||||||
|
|
||||||
// Remove index
|
|
||||||
db.Model(&User{}).RemoveIndex("idx_user_name")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Basic CRUD
|
# Basic CRUD
|
||||||
|
@ -170,18 +139,13 @@ db.Model(&User{}).RemoveIndex("idx_user_name")
|
||||||
```go
|
```go
|
||||||
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
|
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
|
||||||
|
|
||||||
// returns true if record hasn’t been saved (primary key `Id` is blank)
|
db.NewRecord(user) // => returns `true` if primary key is blank
|
||||||
db.NewRecord(user) // => true
|
|
||||||
|
|
||||||
db.Create(&user)
|
db.Create(&user)
|
||||||
|
|
||||||
// will return false after `user` created
|
db.NewRecord(user) // => return `false` after `user` created
|
||||||
db.NewRecord(user) // => false
|
|
||||||
|
|
||||||
// You could use `Save` to create record also if its primary key is null
|
// Associations will be inserted automatically when save the record
|
||||||
db.Save(&user)
|
|
||||||
|
|
||||||
// Associations will be saved automatically when insert the record
|
|
||||||
user := User{
|
user := User{
|
||||||
Name: "jinzhu",
|
Name: "jinzhu",
|
||||||
BillingAddress: Address{Address1: "Billing Address - Address 1"},
|
BillingAddress: Address{Address1: "Billing Address - Address 1"},
|
||||||
|
@ -204,7 +168,7 @@ db.Create(&user)
|
||||||
//// COMMIT;
|
//// COMMIT;
|
||||||
```
|
```
|
||||||
|
|
||||||
Refer [Associations](#associations) for how to work with associations
|
Refer [Associations](#associations) for more details
|
||||||
|
|
||||||
## Query
|
## Query
|
||||||
|
|
||||||
|
@ -1095,6 +1059,32 @@ INSERT INTO animals("age") values('99');
|
||||||
|
|
||||||
The same thing occurs in update statements.
|
The same thing occurs in update statements.
|
||||||
|
|
||||||
|
## Database Indexes & Foreign Key
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Add foreign key
|
||||||
|
// 1st param : foreignkey field
|
||||||
|
// 2nd param : destination table(id)
|
||||||
|
// 3rd param : ONDELETE
|
||||||
|
// 4th param : ONUPDATE
|
||||||
|
db.Model(&User{}).AddForeignKey("user_id", "destination_table(id)", "CASCADE", "RESTRICT")
|
||||||
|
|
||||||
|
// Add index
|
||||||
|
db.Model(&User{}).AddIndex("idx_user_name", "name")
|
||||||
|
|
||||||
|
// Multiple column index
|
||||||
|
db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
|
||||||
|
|
||||||
|
// Add unique index
|
||||||
|
db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")
|
||||||
|
|
||||||
|
// Multiple column unique index
|
||||||
|
db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
|
||||||
|
|
||||||
|
// Remove index
|
||||||
|
db.Model(&User{}).RemoveIndex("idx_user_name")
|
||||||
|
```
|
||||||
|
|
||||||
## More examples with query chain
|
## More examples with query chain
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
|
@ -87,9 +87,9 @@ func TestIndexes(t *testing.T) {
|
||||||
type BigEmail struct {
|
type BigEmail struct {
|
||||||
Id int64
|
Id int64
|
||||||
UserId int64
|
UserId int64
|
||||||
Email string
|
Email string `sql:"index:idx_email_agent"`
|
||||||
UserAgent string
|
UserAgent string `sql:"index:idx_email_agent"`
|
||||||
RegisteredAt time.Time
|
RegisteredAt time.Time `sql:"unique_index"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,15 @@ func TestAutoMigration(t *testing.T) {
|
||||||
|
|
||||||
DB.Save(&BigEmail{Email: "jinzhu@example.org", UserAgent: "pc", RegisteredAt: time.Now()})
|
DB.Save(&BigEmail{Email: "jinzhu@example.org", UserAgent: "pc", RegisteredAt: time.Now()})
|
||||||
|
|
||||||
|
scope := DB.NewScope(&BigEmail{})
|
||||||
|
if !scope.Dialect().HasIndex(scope, scope.TableName(), "idx_email_agent") {
|
||||||
|
t.Errorf("Failed to create index")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !scope.Dialect().HasIndex(scope, scope.TableName(), "uix_emails_registered_at") {
|
||||||
|
t.Errorf("Failed to create index")
|
||||||
|
}
|
||||||
|
|
||||||
var bigemail BigEmail
|
var bigemail BigEmail
|
||||||
DB.First(&bigemail, "user_agent = ?", "pc")
|
DB.First(&bigemail, "user_agent = ?", "pc")
|
||||||
if bigemail.Email != "jinzhu@example.org" || bigemail.UserAgent != "pc" || bigemail.RegisteredAt.IsZero() {
|
if bigemail.Email != "jinzhu@example.org" || bigemail.UserAgent != "pc" || bigemail.RegisteredAt.IsZero() {
|
||||||
|
|
|
@ -532,5 +532,43 @@ func (scope *Scope) autoMigrate() *Scope {
|
||||||
scope.createJoinTable(field)
|
scope.createJoinTable(field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.autoIndex()
|
||||||
|
return scope
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scope *Scope) autoIndex() *Scope {
|
||||||
|
var indexes = map[string][]string{}
|
||||||
|
var uniqueIndexes = map[string][]string{}
|
||||||
|
|
||||||
|
for _, field := range scope.GetStructFields() {
|
||||||
|
sqlSettings := parseTagSetting(field.Tag.Get("sql"))
|
||||||
|
if name, ok := sqlSettings["INDEX"]; ok {
|
||||||
|
if name == "INDEX" {
|
||||||
|
name = fmt.Sprintf("idx_%v_%v", scope.TableName(), field.DBName)
|
||||||
|
}
|
||||||
|
indexes[name] = append(indexes[name], field.DBName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := sqlSettings["UNIQUE_INDEX"]; ok {
|
||||||
|
if name == "UNIQUE_INDEX" {
|
||||||
|
name = fmt.Sprintf("uix_%v_%v", scope.TableName(), field.DBName)
|
||||||
|
}
|
||||||
|
uniqueIndexes[name] = append(indexes[name], field.DBName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, columns := range indexes {
|
||||||
|
if !scope.Dialect().HasIndex(scope, scope.TableName(), name) {
|
||||||
|
scope.addIndex(false, name, columns...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, columns := range uniqueIndexes {
|
||||||
|
if !scope.Dialect().HasIndex(scope, scope.TableName(), name) {
|
||||||
|
scope.addIndex(true, name, columns...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue