跳到主要内容

在 Ent 模式中使用函数索引

函数索引是一种索引类型,其键部分基于表达式值而非列值。这种索引类型对于索引未存储在表中的函数或表达式结果非常有用。MySQL、MariaDBPostgreSQLSQLite 均支持此功能。

本指南介绍了如何通过函数索引扩展您的 Ent 模式,并使用 Atlas 配置模式迁移,以将函数索引和 Ent 模式作为单个迁移单元进行管理。

本指南中使用的 复合模式 (Composite Schema) 的 Atlas 支持功能为 Pro 用户专享。要使用此功能,请运行:

atlas login

安装 Atlas

To install the latest release of Atlas, simply run one of the following commands in your terminal, or check out the Atlas website:

curl -sSf https://atlasgo.sh | sh

登录 Atlas

$ atlas login a8m
您现已连接到 Atlas Cloud 上的 "a8m" 账户。

复合模式

ent/schema 包主要用于定义 Ent 类型(对象)、其字段、边和逻辑。函数索引在 Ent 模式中没有表示形式,因为 Ent 支持在字段、边(外键)及其组合上定义索引。

为了将带有函数索引的 PostgreSQL 模式迁移扩展到我们的 Ent 类型(表),我们配置 Atlas 以从 复合模式 (Composite Schema) 数据源读取模式状态。请按照以下步骤为您的项目进行配置:

  1. 定义一个简单的模式,包含一个类型(表):User(表 users):
ent/schema/user.go
// User 持有 User 实体的模式定义。
type User struct {
ent.Schema
}

// User 的字段。
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Comment("在 schema.sql 中为 lower(name) 定义了唯一索引"),
}
}
  1. 接下来,在 schema.sql 文件中为 name 字段定义一个函数索引:
schema.sql
-- 在 name 列的小写值上创建一个函数(唯一)索引。
CREATE UNIQUE INDEX unique_name ON "users" ((lower("name")));
  1. 创建一个简单的 atlas.hcl 配置文件,其中包含一个 composite_schema,该配置同时包含在 schema.sql 中定义的函数索引和您的 Ent 模式:
atlas.hcl
data "composite_schema" "app" {
# 首先加载包含所有表的 ent 模式。
schema "public" {
url = "ent://ent/schema"
}
# 然后,加载函数索引。
schema "public" {
url = "file://schema.sql"
}
}

env "local" {
src = data.composite_schema.app.url
dev = "docker://postgres/15/dev?search_path=public"
}

使用

设置好复合模式后,我们可以使用 atlas schema inspect 命令获取其表示形式,为其生成模式迁移,将其应用到数据库等。以下是一些命令,帮助您开始使用 Atlas:

检查模式

atlas schema inspect 命令通常用于检查数据库。但是,我们也可以使用它来检查我们的 composite_schema 并打印其 SQL 表示形式:

atlas schema inspect \
--env local \
--url env://src \
--format '{{ sql . }}'

上述命令打印以下 SQL。

-- 创建 "users" 表
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, PRIMARY KEY ("id"));
-- 为表 "users" 创建索引 "unique_name"
CREATE UNIQUE INDEX "unique_name" ON "users" ((lower((name)::text)));

请注意,我们的函数索引是在 users 表的 name 字段上定义的。

为模式生成迁移

要生成模式的迁移,请运行以下命令:

atlas migrate diff \
--env local

请注意,会创建一个新的迁移文件,其内容如下:

migrations/20240712090543.sql
-- 创建 "users" 表
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, PRIMARY KEY ("id"));
-- 为表 "users" 创建索引 "unique_name"
CREATE UNIQUE INDEX "unique_name" ON "users" ((lower((name)::text)));

应用迁移

要将上面生成的迁移应用到数据库,请运行以下命令:

atlas migrate apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
直接在数据库上应用模式

有时候,需要在不生成迁移文件的情况下直接将模式应用到数据库。例如,在试验模式更改、启动测试数据库等情况下。在这种情况下,您可以使用以下命令直接将模式应用到数据库:

atlas schema apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?sslmode=disable"

或者,使用 Atlas Go SDK

ac, err := atlasexec.NewClient(".", "atlas")
if err != nil {
log.Fatalf("failed to initialize client: %w", err)
}
// 自动使用所需模式更新数据库。
// 另一个选项是手动使用 'migrate apply' 或 'schema apply'。
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)
}

代码示例

在设置了带有函数索引的 Ent 模式后,我们希望数据库能强制 users 表中 name 字段的唯一性:

// 测试唯一索引是否被强制执行。
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"`)

// 类型断言返回的错误。
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")

本指南的代码可以在 GitHub 上找到。