メインコンテンツへスキップする

CRUD API

前書き セクションで述べたように、スキーマで ent を実行すると、 以下のアセットを生成します。

  • グラフとのやり取りに使用される ClientTx オブジェクト。
  • 各スキーマタイプの CRUD ビルダー。 詳細は CRUD を参照してください。
  • 各スキーマタイプのエンティティオブジェクト (Go構造体)。
  • ビルダーとのやりとりに使われる定数や述語を含むパッケージ。
  • SQL方言用の migrate パッケージ。 詳細は 移行 を参照してください。

新しいクライアントを作成する#

MySQL

package main
import (
"log"
"<project>/ent"
_ "github.com/go-sql-driver/mysql"
)
func main() {
client, err := ent.Open("mysql", "<user>:<pass>@tcp(<host>:<port>)/<database>?parseTime=True")
if err != nil {
log.Fatal(err)
}
defer client.Close()
}

PostgreSQL

package main
import (
"log"
"<project>/ent"
_ "github.com/lib/pq"
)
func main() {
client, err := ent.Open("postgres","host=<host> port=<port> user=<user> dbname=<database> password=<pass>")
if err != nil {
log.Fatal(err)
}
defer client.Close()
}

SQLite

package main
import (
"log"
"<project>/ent"
_ "github.com/mattn/go-sqlite3"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatal(err)
}
defer client.Close()
}

Gremlin (AWS Neptune)

package main
import (
"log"
"<project>/ent"
)
func main() {
client, err := ent.Open("gremlin", "http://localhost:8182")
if err != nil {
log.Fatal(err)
}
}

エンティティを作成する#

Save でユーザーを保存します。

a8m, err := client.User. // ユーザークライアント
Create(). // ユーザー作成用ビルダー
SetName("a8m"). // フィールド値を設定する
SetNillableAge(age). // 必須チェックを無効化する
AddGroups(g1, g2). // 複数のエッジを追加する
SetSpouse(nati). // 単一のエッジを設定する
Save(ctx) // 作成して返却する

SaveX でペットを保存します。 Save とは異なり、 SaveX はエラーが発生した場合パニックになります。

pedro := client.Pet. // ペットクライアント
Create(). // ペット作成用ビルダー
SetName("pedro"). // フィールド値を設定する
SetOwner(a8m). // 飼い主を設定する (単一エッジ).
SaveX(ctx) // 作成して返却する

複数作成する#

Save で複数のペットを一括保存します。

names := []string{"pedro", "xabi", "layla"}
bulk := make([]*ent.PetCreate, len(names))
for i, name := range names {
bulk[i] = client.Pet.Create().SetName(name).SetOwner(a8m)
}
pets, err := client.Pet.CreateBulk(bulk...).Save(ctx)

1つを更新する#

データベースから返されたエンティティを更新します。

a8m, err = a8m.Update(). // ユーザー更新用ビルダー
RemoveGroup(g2). // 特定のエッジを削除する
ClearCard(). // 単一のエッジをクリアする
SetAge(30). // フィールド値を設定する
Save(ctx) // 保存して返却する

ID指定で更新する#

pedro, err := client.Pet. // ペットクライアント
UpdateOneID(id). // ペット更新用ビルダー
SetName("pedro"). // 名前のフィールド値を設定する
SetOwnerID(owner). // IDを用いて、単一のエッジを設定する
Save(ctx) // 保存して返却する

複数更新#

述語を使用してフィルタリングします。

n, err := client.User. // ユーザークライアント
Update(). // ペット更新用ビルダー
Where( //
user.Or( // (age >= 30 OR name = "bar")
user.AgeGT(30), //
user.Name("bar"), // AND
), //
user.HasFollowers(), // UserHasFollowers()
). //
SetName("foo"). // 名前のフィールド値を設定する
Save(ctx) // 保存して返却する

エッジ述語を使用して問い合わせます。

n, err := client.User. // ユーザークライアント
Update(). // ペット更新用ビルダー
Where( //
user.HasFriendsWith( // 条件にマッチする友人を持つユーザーである (
user.Or( // 年齢 = 20
user.Age(20), // OR
user.Age(30), // 年齢 = 30
) // )
), //
). //
SetName("a8m"). // 名前のフィールド値を設定する
Save(ctx) // 保存して返却する

1つをUpsert#

Ent は upsert レコードを sql/upsert機能フラグによってサポートしています

err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
// 作成時に設定された新しい値を使用します。
UpdateNewValues().
Exec(ctx)
id, err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
// 作成時に設定された"age"の値を使用します。
UpdateAge().
// コンフリクトの場合に、違う "name"の値を設定します。
SetName("Mashraki").
ID(ctx)
// UPDATE 句をカスタマイズします
err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
UpdateNewValues().
// カスタムアップデートでいくつかのフィールドを上書きします
Update(func(u *ent.UserUpsert) {
u.SetAddress("localhost")
u.AddCount(1)
u.ClearPhone()
}).
Exec(ctx)

PostgreSQLでは、 conflict target が必要です。

// fluent API を使用してカラム名を設定します
err := client.User.
Create().
SetName("Ariel").
OnConflictColumns(user.FieldName).
UpdateNewValues().
Exec(ctx)
// SQL APIを使用してカラム名を設定します。
err := client.User.
Create().
SetName("Ariel").
OnConflict(
sql.ConflictColumns(user.FieldName),
).
UpdateNewValues().
Exec(ctx)
// SQL APIを使用して制約名を設定します。
err := client.User.
Create().
SetName("Ariel").
OnConflict(
sql.ConflictConstraint(constraint),
).
UpdateNewValues().
Exec(ctx)

実行されたステートメントをカスタマイズするには、SQL API を使用します。

id, err := client.User.
Create().
OnConflict(
sql.ConflictColumns(...),
sql.ConflictWhere(...),
sql.UpdateWhere(...),
).
Update(func(u *ent.UserUpsert) {
u.SetAge(30)
u.UpadteName()
}).
ID(ctx)
// INSERT INTO "users" (...) VALUES (...) ON CONFLICT WHERE ... DO UPDATE SET ... WHERE ...
upsert APIはON CONFLICT句(MySQLではON DUPLICATE KEY) を使って実装されています。Entはデータベースに対して1つのステートメントしか実行しないため、このような操作にはcreateフックしか適用されません。 :::

複数をUpsert#

err := client.User. // UserClient
CreateBulk(builders...). // ユーザーの一括作成
OnConflict(). // ユーザーの一括Upsert
UpdateNewValues(). // コンフリクトの発生時に設定された値を使用します。
Exec(ctx) // ステートメントを実行します。

グラフのQuery#

フォロワーを持つすべてのユーザーを取得します。

users, err := client.User. // UserClient.
Query(). // User query builder.
Where(user.HasFollowers()). // filter only users with followers.
All(ctx) // query and return.

特定のユーザーのすべてのフォロワーを取得します。グラフ内のノードからトラバーサルを開始します。

users, err := a8m.
QueryFollowers().
All(ctx)

ユーザーのフォロワーのすべてのペットを取得します。

users, err := a8m.
QueryFollowers().
QueryPets().
All(ctx)

Count the number of posts without comments.

n, err := client.Post.
Query().
Where(
post.Not(
post.HasComments(),
)
).
Count(ctx)

More advance traversals can be found in the next section.

Fieldの選択#

Get all pet names.

names, err := client.Pet.
Query().
Select(pet.FieldName).
Strings(ctx)

Get all unique pet names.

names, err := client.Pet.
Query().
Unique(true).
Select(pet.FieldName).
Strings(ctx)

Count the number of unique pet names.

n, err := client.Pet.
Query().
Unique(true).
Select(pet.FieldName).
Count(ctx)

Select partial objects and partial associations.gs Get all pets and their owners, but select and fill only the ID and Name fields.

pets, err := client.Pet.
Query().
Select(pet.FieldName).
WithOwner(func (q *ent.UserQuery) {
q.Select(user.FieldName)
}).
All(ctx)

Scan all pet names and ages to custom struct.

var v []struct {
Age int `json:"age"`
Name string `json:"name"`
}
err := client.Pet.
Query().
Select(pet.FieldAge, pet.FieldName).
Scan(ctx, &v)
if err != nil {
log.Fatal(err)
}

Update an entity and return a partial of it.

pedro, err := client.Pet.
UpdateOneID(id).
SetAge(9).
SetName("pedro").
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
Select(pet.FieldName).
Save(ctx)

1つを削除#

Delete an entity.

err := client.User.
DeleteOne(a8m).
Exec(ctx)

Delete by ID.

err := client.User.
DeleteOneID(id).
Exec(ctx)

複数削除#

Delete using predicates.

_, err := client.File.
Delete().
Where(file.UpdatedAtLT(date)).
Exec(ctx)

Mutation#

Each generated node type has its own type of mutation. For example, all User builders, share the same generated UserMutation object. However, all builder types implement the generic ent.Mutation interface.

For example, in order to write a generic code that apply a set of methods on both ent.UserCreate and ent.UserUpdate, use the UserMutation object:

func Do() {
creator := client.User.Create()
SetAgeName(creator.Mutation())
updater := client.User.UpdateOneID(id)
SetAgeName(updater.Mutation())
}
// SetAgeName sets the age and the name for any mutation.
func SetAgeName(m *ent.UserMutation) {
m.SetAge(32)
m.SetName("Ariel")
}

In some cases, you want to apply a set of methods on multiple types. For cases like this, either use the generic ent.Mutation interface, or create your own interface.

func Do() {
creator1 := client.User.Create()
SetName(creator1.Mutation(), "a8m")
creator2 := client.Pet.Create()
SetName(creator2.Mutation(), "pedro")
}
// SetNamer wraps the 2 methods for getting
// and setting the "name" field in mutations.
type SetNamer interface {
SetName(string)
Name() (string, bool)
}
func SetName(m SetNamer, name string) {
if _, exist := m.Name(); !exist {
m.SetName(name)
}
}