Create Indexes with AutoMigrate

This commit is contained in:
Jinzhu 2015-03-09 17:22:16 +08:00
parent 42f934d60e
commit 6fed43162f
3 changed files with 90 additions and 53 deletions

View File

@ -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 hasnt 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

View File

@ -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() {

View File

@ -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
} }