索引
多个字段
索引可以在一个或多个字段上配置以提高数据检索速度,也可以定义其唯一性。
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/index"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
func (User) Indexes() []ent.Index {
return []ent.Index{
// 非唯一约束索引
index.Fields("field1", "field2"),
// 唯一约束索引
index.Fields("first_name", "last_name").
Unique(),
}
}
请注意,如果要为单个字段设置唯一约束,请在字段生成器上使用 Unique
方法,如下:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("phone").
Unique(),
}
}
边上的索引
索引可以为字段和边的组合进行配置。 主要用法是在特定关系下设置字段的唯一性。 让我们来看一个例子:
在上面的示例中,我们有一个带了许多 Street
的 City
,并且我们想设置每个城市的街道名称都是唯一的。
ent/schema/city.go
// City holds the schema definition for the City entity.
type City struct {
ent.Schema
}
// Fields of the City.
func (City) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the City.
func (City) Edges() []ent.Edge {
return []ent.Edge{
edge.To("streets", Street.Type),
}
}
ent/schema/street.go
// Street holds the schema definition for the Street entity.
type Street struct {
ent.Schema
}
// Fields of the Street.
func (Street) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the Street.
func (Street) Edges() []ent.Edge {
return []ent.Edge{
edge.From("city", City.Type).
Ref("streets").
Unique(),
}
}
// Indexes of the Street.
func (Street) Indexes() []ent.Index {
return []ent.Index{
index.Fields("name").
Edges("city").
Unique(),
}
}
example.go
func Do(ctx context.Context, client *ent.Client) error {
// 和 `Save`不同,当出现错误是 `SaveX` 抛出panic。
tlv := client.City.
Create().
SetName("TLV").
SaveX(ctx)
nyc := client.City.
Create().
SetName("NYC").
SaveX(ctx)
// Add a street "ST" to "TLV".
client.Street.
Create().
SetName("ST").
SetCity(tlv).
SaveX(ctx)
// This operation fails because "ST"
// was already created under "TLV".
if err := client.Street.
Create().
SetName("ST").
SetCity(tlv).
Exec(ctx); err == nil {
return fmt.Errorf("expecting creation to fail")
}
// Add a street "ST" to "NYC".
client.Street.
Create().
SetName("ST").
SetCity(nyc).
SaveX(ctx)
return nil
}
完整示例请参阅 GitHub.
Index On Edge Fields
Currently Edges
columns are always added after Fields
columns. However, some indexes require these columns to come first in order to achieve specific optimizations. You can work around this problem by making use of Edge Fields.
// Card holds the schema definition for the Card entity.
type Card struct {
ent.Schema
}
// Fields of the Card.
func (Card) Fields() []ent.Field {
return []ent.Field{
field.String("number").
Optional(),
field.Int("owner_id").
Optional(),
}
}
// Edges of the Card.
func (Card) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type).
Ref("card").
Field("owner_id").
Unique(),
}
}
// Indexes of the Card.
func (Card) Indexes() []ent.Index {
return []ent.Index{
index.Fields("owner_id", "number"),
}
}
Dialect Support
Indexes currently support only SQL dialects, and do not support Gremlin. Dialect specific features are allowed using annotations. For example, in order to use index prefixes in MySQL, use the following configuration:
// Indexes of the User.
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("description").
Annotations(entsql.Prefix(128)),
index.Fields("c1", "c2", "c3").
Annotation(
entsql.PrefixColumn("c1", 100),
entsql.PrefixColumn("c2", 200),
)
}
}
The code above generates the following SQL statements:
CREATE INDEX `users_description` ON `users`(`description`(128))
CREATE INDEX `users_c1_c2_c3` ON `users`(`c1`(100), `c2`(200), `c3`)
Atlas Support
Starting with v0.10, Ent supports running migration with Atlas. This option provides more control on indexes such as, configuring their types or define indexes in a reverse order.
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("c1").
Annotations(entsql.Desc()),
index.Fields("c1", "c2", "c3").
Annotation(entsql.DescColumns("c1", "c2")),
index.Fields("c4").
Annotations(entsql.IndexType("HASH")),
// Enable FULLTEXT search on MySQL,
// and GIN on PostgreSQL.
index.Fields("c5").
Annotations(
entsql.IndexTypes(map[string]string{
dialect.MySQL: "FULLTEXT",
dialect.Postgres: "GIN",
}),
),
}
}
The code above generates the following SQL statements:
CREATE INDEX `users_c1` ON `users` (`c1` DESC)
CREATE INDEX `users_c1_c2_c3` ON `users` (`c1` DESC, `c2` DESC, `c3`)
CREATE INDEX `users_c4` ON `users` USING HASH (`c4`)
-- MySQL only.
CREATE FULLTEXT INDEX `users_c5` ON `users` (`c5`)
-- PostgreSQL only.
CREATE INDEX `users_c5` ON `users` USING GIN (`c5`)
Storage Key
Like Fields, custom index name can be configured using the StorageKey
method. It’s mapped to a index name in SQL dialects.
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("field1", "field2").
StorageKey("custom_index"),
}
}