Uso de índices funcionales en esquemas Ent
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Un índice funcional es un índice cuyas partes clave se basan en valores de expresiones, no en valores de columnas. Este tipo de índice es útil para indexar resultados de funciones o expresiones que no están almacenadas en la tabla. Compatible con MySQL, MariaDB, PostgreSQL y SQLite.
Esta guía explica cómo extender tu esquema Ent con índices funcionales y configurar la migración de esquemas para gestionar tanto los índices funcionales como el esquema Ent como una única unidad de migración usando Atlas.
El soporte de Atlas para Esquema Compuesto utilizado en esta guía está disponible exclusivamente para usuarios Pro. Para usar esta función, ejecuta:
atlas login
Instalar Atlas
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Para instalar la última versión de Atlas, simplemente ejecuta uno de los siguientes comandos en tu terminal, o visita el sitio web de Atlas:
- macOS + Linux
- Homebrew
- Docker
- Windows
curl -sSf https://atlasgo.sh | sh
brew install ariga/tap/atlas
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
Iniciar sesión en Atlas
$ atlas login a8m
You are now connected to "a8m" on Atlas Cloud.
Esquema compuesto
El paquete ent/schema se usa principalmente para definir tipos Ent (objetos), sus campos, relaciones y lógica. Los índices funcionales
no tienen representación en el esquema Ent, ya que Ent solo permite definir índices en campos, relaciones (claves foráneas) y combinaciones
de ellos.
Para extender nuestra migración de esquema PostgreSQL con índices funcionales para nuestros tipos Ent (tablas), configuramos Atlas para leer el estado del esquema desde una fuente de datos de Esquema Compuesto. Sigue estos pasos para configurarlo en tu proyecto:
1. Definamos un esquema simple con un tipo (tabla): User (tabla users):
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Comment("A unique index is defined on lower(name) in schema.sql"),
}
}
2. Como siguiente paso, definimos un índice funcional en el campo name en el archivo schema.sql:
-- Create a functional (unique) index on the lowercased name column.
CREATE UNIQUE INDEX unique_name ON "users" ((lower("name")));
3. Crea un archivo de configuración atlas.hcl simple con un composite_schema que incluya tanto los índices funcionales definidos en
schema.sql como tu esquema Ent:
data "composite_schema" "app" {
# Load the ent schema first with all tables.
schema "public" {
url = "ent://ent/schema"
}
# Then, load the functional indexes.
schema "public" {
url = "file://schema.sql"
}
}
env "local" {
src = data.composite_schema.app.url
dev = "docker://postgres/15/dev?search_path=public"
}
Uso
Tras configurar nuestro esquema compuesto, podemos obtener su representación usando el comando atlas schema inspect, generar migraciones de esquema, aplicarlas a una base de datos y más. Aquí tienes algunos comandos para comenzar con Atlas:
Inspeccionar el esquema
El comando atlas schema inspect se usa comúnmente para inspeccionar bases de datos. Sin embargo, también podemos usarlo para inspeccionar nuestro composite_schema e imprimir su representación SQL:
atlas schema inspect \
--env local \
--url env://src \
--format '{{ sql . }}'
El comando anterior imprime el siguiente SQL:
-- Create "users" table
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, PRIMARY KEY ("id"));
-- Create index "unique_name" to table: "users"
CREATE UNIQUE INDEX "unique_name" ON "users" ((lower((name)::text)));
Observa que nuestro índice funcional está definido en el campo name de la tabla users.
Generar migraciones para el esquema
Para generar una migración para el esquema, ejecuta el siguiente comando:
atlas migrate diff \
--env local
Ten en cuenta que se crea un nuevo archivo de migración con el siguiente contenido:
-- Create "users" table
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, PRIMARY KEY ("id"));
-- Create index "unique_name" to table: "users"
CREATE UNIQUE INDEX "unique_name" ON "users" ((lower((name)::text)));
Aplicar las migraciones
Para aplicar la migración generada a una base de datos, ejecuta el siguiente comando:
atlas migrate apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
En ocasiones, es necesario aplicar el esquema directamente en la base de datos sin generar un archivo de migración. Por ejemplo, al experimentar con cambios de esquema, al crear bases de datos para pruebas, etc. En estos casos, puedes usar el siguiente comando para aplicar el esquema directamente en la base de datos:
atlas schema apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?sslmode=disable"
O utilizando el SDK de Atlas para Go:
ac, err := atlasexec.NewClient(".", "atlas")
if err != nil {
log.Fatalf("failed to initialize client: %w", err)
}
// Automatically update the database with the desired schema.
// Another option, is to use 'migrate apply' or 'schema apply' manually.
if _, err := ac.SchemaApply(ctx, &atlasexec.SchemaApplyParams{
Env: "local",
URL: "postgres://postgres:pass@localhost:5432/database?sslmode=disable",
AutoApprove: true,
}); err != nil {
log.Fatalf("failed to apply schema changes: %w", err)
}
Ejemplo de código
Después de configurar nuestro esquema Ent con índices funcionales, esperamos que la base de datos imponga la unicidad del campo name
en la tabla users:
// Test that the unique index is enforced.
client.User.Create().SetName("Ariel").SaveX(ctx)
err = client.User.Create().SetName("ariel").Exec(ctx)
require.EqualError(t, err, `ent: constraint failed: pq: duplicate key value violates unique constraint "unique_name"`)
// Type-assert returned error.
var pqerr *pq.Error
require.True(t, errors.As(err, &pqerr))
require.Equal(t, `duplicate key value violates unique constraint "unique_name"`, pqerr.Message)
require.Equal(t, user.Table, pqerr.Table)
require.Equal(t, "unique_name", pqerr.Constraint)
require.Equal(t, pq.ErrorCode("23505"), pqerr.Code, "unique violation")
El código de esta guía se puede encontrar en GitHub.