👏

[ent][Go]M2M Edgesで自動生成されるjoin table にindex追加する

2021/08/15に公開

TL;DR

  • GoのORM, entでM2MのEdgesを定義した場合、自動的にjoin用の中間テーブル(join tableと呼ばれている)が生成される
  • 自動生成されるテーブルはschemaファイルが存在しないため、通常のようにindexesを定義できない
  • 現状はmigration hooksでschema定義を修正する必要があるとのこと。将来的にはoptionで指定できるようになるらしい(2021/8現在)

Example

※PostgreSQLを想定

  • 下記のようなUser, Group schemaを定義する
    • これによりUserとGroupを関連付ける group_users というテーブルが自動生成される
type User struct {
	ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.String("user_name"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.From("groups", Group.Type).Ref("users"),
	}
}
type Group struct {
	ent.Schema
}

// Fields of the Group.
func (Group) Fields() []ent.Field {
	return []ent.Field{
		field.String("group_name"),
	}
}

// Edges of the Group.
func (Group) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("users", User.Type),
	}
}
  • group_users DDL
-- public.group_users definition

CREATE TABLE public.group_users (
	group_id int8 NOT NULL,
	user_id int8 NOT NULL,
	CONSTRAINT group_users_pkey PRIMARY KEY (group_id, user_id)
);

-- public.group_users foreign keys

ALTER TABLE public.group_users ADD CONSTRAINT group_users_group_id FOREIGN KEY (group_id) REFERENCES public."groups"(id) ON DELETE CASCADE;
ALTER TABLE public.group_users ADD CONSTRAINT group_users_user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
  • そして下記のようなcodeを実行するとする
client.User.Query().
    Where(user.IDEQ(u.ID)).
    WithGroups().
    All(ctx)
  • その時に実行されるSQLは下記になる
    • driver.Query: query=SELECT "user_id", "group_id" FROM "group_users" WHERE "user_id" IN ($1) args=[1]
    • group_users_pkey PRIMARY KEY (group_id, user_id) しかないためindexが存在しない
driver.Query: query=SELECT DISTINCT "users"."id", "users"."user_name" FROM "users" WHERE "users"."id" = $1 args=[1]
driver.Query: query=SELECT "user_id", "group_id" FROM "group_users" WHERE "user_id" IN ($1) args=[1]
driver.Query: query=SELECT DISTINCT "groups"."id", "groups"."group_name" FROM "groups" WHERE "groups"."id" IN ($1) args=[1]
  • group_users は自動生成されshemaファイルが存在しないため、通常のようにindexesを定義できない
  • 現状はmigration hooksでschema定義を修正する必要があるとのこと。将来的にはoptionで指定できるようになるとのこと(2021/8現在)
  • 下記でindexを追加できることを確認
    • 他にいい方法あったら教えて下さいmm
db.Schema.Create(context.Background(),
	schema.WithHooks(func(next schema.Creator) schema.Creator {
		return schema.CreateFunc(func(ctx context.Context, tables ...*schema.Table) error {
			// [NOTE]
			// Add index to group_users schema.
			for _, t := range tables {
				if t.Name == "group_users" {
					t.Indexes = append(t.Indexes, &schema.Index{
						Name:    "group_users_user_id",
						Unique:  false,
						Columns: []*schema.Column{t.Columns[1]}, // user_id
					})
				}
			}
			return next.Create(ctx, tables...)
		})
	}),
)

Discussion