索引(Indexes)
多字段索引
索引可配置于一个或多个字段,以提升数据检索速度或定义唯一性。
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{
// non-unique index.
index.Fields("field1", "field2"),
// unique index.
index.Fields("first_name", "last_name").
Unique(),
}
}
请注意,若要将单个字段设置为唯一,请在字段构建器上使用 Unique 方法,如下所示:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("phone").
Unique(),
}
}
边的索引
索引可配置于字段与边的关系组合上。其主要应用场景是为特定关系下的字段设置唯一性。举例说明:

在上面的示例中,City 有多条 Street,我们希望在不同的城市中街道的名称设置为唯一的。
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 {
// Unlike `Save`, `SaveX` panics if an error occurs.
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。
边字段的索引
目前 Edges 列始终添加在 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"),
}
}
方言支持
允许使用 注解 指定方言特有的功能。例如,要在 MySQL 中使用 索引前缀,请使用以下配置:
// Indexes of the User.
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("description").
Annotations(entsql.Prefix(128)),
index.Fields("c1", "c2", "c3").
Annotations(
entsql.PrefixColumn("c1", 100),
entsql.PrefixColumn("c2", 200),
)
}
}
上面的代码生成如下SQL语句:
CREATE INDEX `users_description` ON `users`(`description`(128))
CREATE INDEX `users_c1_c2_c3` ON `users`(`c1`(100), `c2`(200), `c3`)
Atlas支持
Ent 自 v0.10 版本开始用 Atlas 运行迁移。此选项提供了对索引的更多控制权限,例如配置索引类型或以反向顺序定义索引。
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("c1").
Annotations(entsql.Desc()),
index.Fields("c1", "c2", "c3").
Annotations(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",
}),
),
// For PostgreSQL, we can include in the index
// non-key columns.
index.Fields("workplace").
Annotations(
entsql.IncludeColumns("address"),
),
// Define a partial index on SQLite and PostgreSQL.
index.Fields("nickname").
Annotations(
entsql.IndexWhere("active"),
),
// Define a custom operator class.
index.Fields("phone").
Annotations(
entsql.OpClass("bpchar_pattern_ops"),
),
}
}
上面的代码生成如下SQL语句:
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")
-- Include index-only scan on PostgreSQL.
CREATE INDEX "users_workplace" ON "users" ("workplace") INCLUDE ("address")
-- Define partial index on SQLite and PostgreSQL.
CREATE INDEX "users_nickname" ON "users" ("nickname") WHERE "active"
-- PostgreSQL only.
CREATE INDEX "users_phone" ON "users" ("phone" bpchar_pattern_ops)
函数索引(Functional Indexes)
Ent模式支持在字段和边(外键)上定义索引,但不提供将索引部分定义为表达式(如函数调用)的API。若您使用 Atlas 管理模式迁移,可参照 本指南 所述方式定义函数索引。
存储名称(Storage Key)
与字段一样,可以使用 StorageKey 方法自定义索引名称。它将会被映射到SQL方言中的所以名称上。
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("field1", "field2").
StorageKey("custom_index"),
}
}