Índices
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Múltiples campos
Los índices pueden configurarse en uno o varios campos para mejorar la velocidad de recuperación de datos o definir unicidad.
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(),
}
}
Nota: Para establecer un único campo como único, utiliza el método Unique
en el constructor de campos de la siguiente manera:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("phone").
Unique(),
}
}
Índices en relaciones (edges)
Los índices pueden configurarse en composiciones de campos y relaciones. El principal caso de uso es establecer unicidad en campos bajo una relación específica. Veamos un ejemplo:

En este ejemplo, tenemos una City con múltiples Streets, y queremos que el nombre
de la calle sea único dentro de cada ciudad.
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
}
El ejemplo completo está disponible en GitHub.
Índices en campos de relación
Actualmente las columnas de Edges siempre se añaden después de las columnas de Fields. Sin embargo, algunos índices requieren que estas columnas vayan primero para lograr optimizaciones específicas. Puedes solucionar esto usando Campos de Relación.
// 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"),
}
}
Soporte para dialectos
Las características específicas de dialecto se permiten mediante anotaciones. Por ejemplo, para usar prefijos de índice en MySQL, utiliza esta configuración:
// 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),
)
}
}
El código anterior genera estas sentencias SQL:
CREATE INDEX `users_description` ON `users`(`description`(128))
CREATE INDEX `users_c1_c2_c3` ON `users`(`c1`(100), `c2`(200), `c3`)
Soporte para Atlas
Desde la versión v0.10, Ent ejecuta migraciones con Atlas. Esta opción proporciona mayor control sobre índices, como configurar sus tipos o definirlos en orden inverso.
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"),
),
}
}
El código anterior genera estas sentencias 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)
Índices funcionales
El esquema de Ent permite definir índices en campos y relaciones (claves foráneas), pero no incluye API para definir partes de índice como expresiones (ej. llamadas a funciones). Si usas Atlas para gestionar migraciones de esquema, puedes definir índices funcionales como se describe en esta guía.
Clave de Almacenamiento
Al igual que los campos, el nombre personalizado de un índice puede configurarse mediante el método StorageKey.
Se asigna a un nombre de índice en dialectos SQL.
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("field1", "field2").
StorageKey("custom_index"),
}
}