API CRUD
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Como se mencionó en la sección de introducción, al ejecutar ent en los esquemas,
se generarán los siguientes recursos:
Objetos
ClientyTxpara interactuar con el grafo.Constructores CRUD para cada tipo de esquema.
Objeto entidad (struct de Go) para cada tipo de esquema.
Paquete con constantes y predicados para interactuar con los constructores.
Un paquete
migratepara dialectos SQL. Consulta Migración para más información.
Crear un nuevo cliente
- SQLite
- PostgreSQL
- MySQL
- Gremlin (AWS Neptune)
package main
import (
"context"
"log"
"entdemo/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.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
package main
import (
"context"
"log"
"entdemo/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.Fatalf("failed opening connection to postgres: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
package main
import (
"context"
"log"
"entdemo/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.Fatalf("failed opening connection to mysql: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
package main
import (
"log"
"entdemo/ent"
)
func main() {
client, err := ent.Open("gremlin", "http://localhost:8182")
if err != nil {
log.Fatal(err)
}
}
Crear una entidad
Guardar un usuario.
a8m, err := client.User. // UserClient.
Create(). // User create builder.
SetName("a8m"). // Set field value.
SetNillableAge(age). // Avoid nil checks.
AddGroups(g1, g2). // Add many edges.
SetSpouse(nati). // Set unique edge.
Save(ctx) // Create and return.
GuardarX una mascota; A diferencia de Guardar, GuardarX entra en pánico si ocurre un error.
pedro := client.Pet. // PetClient.
Create(). // Pet create builder.
SetName("pedro"). // Set field value.
SetOwner(a8m). // Set owner (unique edge).
SaveX(ctx) // Create and return.
Crear múltiples
Guardar un grupo de mascotas.
pets, err := client.Pet.CreateBulk(
client.Pet.Create().SetName("pedro").SetOwner(a8m),
client.Pet.Create().SetName("xabi").SetOwner(a8m),
client.Pet.Create().SetName("layla").SetOwner(a8m),
).Save(ctx)
names := []string{"pedro", "xabi", "layla"}
pets, err := client.Pet.MapCreateBulk(names, func(c *ent.PetCreate, i int) {
c.SetName(names[i]).SetOwner(a8m)
}).Save(ctx)
Actualizar uno
Actualizar una entidad devuelta desde la base de datos.
a8m, err = a8m.Update(). // User update builder.
RemoveGroup(g2). // Remove a specific edge.
ClearCard(). // Clear a unique edge.
SetAge(30). // Set a field value.
AddRank(10). // Increment a field value.
AppendInts([]int{1}). // Append values to a JSON array.
Save(ctx) // Save and return.
Actualizar por ID
pedro, err := client.Pet. // PetClient.
UpdateOneID(id). // Pet update builder.
SetName("pedro"). // Set field name.
SetOwnerID(owner). // Set unique edge, using id.
Save(ctx) // Save and return.
Actualizar uno con condición
En algunos proyectos, la operación "actualizar múltiples" no está permitida y se bloquea mediante hooks. Sin embargo, existe la necesidad
de actualizar una sola entidad por su ID asegurando que cumple una condición específica. En este caso, puedes usar la opción Where
de la siguiente manera:
- By ID
- By Entity
- Update Directly
err := client.Todo.
UpdateOneID(id).
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
todo.Version(currentVersion),
).
Exec(ctx)
switch {
// If the entity does not meet a specific condition,
// the operation will return an "ent.NotFoundError".
case ent.IsNotFound(err):
fmt.Println("todo item was not found")
// Any other error.
case err != nil:
fmt.Println("update error:", err)
}
err := client.Todo.
UpdateOne(node).
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
todo.Version(currentVersion),
).
Exec(ctx)
switch {
// If the entity does not meet a specific condition,
// the operation will return an "ent.NotFoundError".
case ent.IsNotFound(err):
fmt.Println("todo item was not found")
// Any other error.
case err != nil:
fmt.Println("update error:", err)
}
firstTodo, err = firstTodo.
Update().
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
// Ensure the current version matches the one in the database.
todo.Version(firstTodo.Version),
).
Save(ctx)
switch {
// If the entity does not meet a specific condition,
// the operation will return an "ent.NotFoundError".
case ent.IsNotFound(err):
fmt.Println("todo item was not found")
// Any other error.
case err != nil:
fmt.Println("update error:", err)
}
Actualizar múltiples
Filtrar usando predicados.
n, err := client.User. // UserClient.
Update(). // User update builder.
Where( //
user.Or( // (age >= 30 OR name = "bar")
user.AgeGT(30), //
user.Name("bar"), // AND
), //
user.HasFollowers(), // UserHasFollowers()
). //
SetName("foo"). // Set field name.
Save(ctx) // exec and return.
Consultar predicados de aristas (edges).
n, err := client.User. // UserClient.
Update(). // User update builder.
Where( //
user.HasFriendsWith( // UserHasFriendsWith (
user.Or( // age = 20
user.Age(20), // OR
user.Age(30), // age = 30
) // )
), //
). //
SetName("a8m"). // Set field name.
Save(ctx) // exec and return.
Upsert (insertar o actualizar) uno
Ent admite operaciones upsert usando la bandera de característica
sql/upsert.
err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
// Use the new values that were set on create.
UpdateNewValues().
Exec(ctx)
id, err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
// Use the "age" that was set on create.
UpdateAge().
// Set a different "name" in case of conflict.
SetName("Mashraki").
ID(ctx)
// Customize the UPDATE clause.
err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
UpdateNewValues().
// Override some of the fields with a custom update.
Update(func(u *ent.UserUpsert) {
u.SetAddress("localhost")
u.AddCount(1)
u.ClearPhone()
}).
Exec(ctx)
En PostgreSQL, se requiere el objetivo de conflicto:
// Setting the column names using the fluent API.
err := client.User.
Create().
SetName("Ariel").
OnConflictColumns(user.FieldName).
UpdateNewValues().
Exec(ctx)
// Setting the column names using the SQL API.
err := client.User.
Create().
SetName("Ariel").
OnConflict(
sql.ConflictColumns(user.FieldName),
).
UpdateNewValues().
Exec(ctx)
// Setting the constraint name using the SQL API.
err := client.User.
Create().
SetName("Ariel").
OnConflict(
sql.ConflictConstraint(constraint),
).
UpdateNewValues().
Exec(ctx)
Para personalizar la sentencia ejecutada, usa la API SQL:
id, err := client.User.
Create().
OnConflict(
sql.ConflictColumns(...),
sql.ConflictWhere(...),
sql.UpdateWhere(...),
).
Update(func(u *ent.UserUpsert) {
u.SetAge(30)
u.UpdateName()
}).
ID(ctx)
// INSERT INTO "users" (...) VALUES (...) ON CONFLICT WHERE ... DO UPDATE SET ... WHERE ...
Dado que la API upsert se implementa mediante la cláusula ON CONFLICT (y ON DUPLICATE KEY en MySQL),
Ent ejecuta solo una sentencia en la base de datos, por lo que solo se aplican los hooks de creación
para estas operaciones.
Upsert múltiples
err := client.User. // UserClient
CreateBulk(builders...). // User bulk create.
OnConflict(). // User bulk upsert.
UpdateNewValues(). // Use the values that were set on create in case of conflict.
Exec(ctx) // Execute the statement.
Consultar el grafo
Obtener todos los usuarios con seguidores.
users, err := client.User. // UserClient.
Query(). // User query builder.
Where(user.HasFollowers()). // filter only users with followers.
All(ctx) // query and return.
Obtener todos los seguidores de un usuario específico; iniciar el recorrido desde un nodo del grafo.
users, err := a8m.
QueryFollowers().
All(ctx)
Obtener todas las mascotas de los seguidores de un usuario.
users, err := a8m.
QueryFollowers().
QueryPets().
All(ctx)
Contar el número de publicaciones sin comentarios.
n, err := client.Post.
Query().
Where(
post.Not(
post.HasComments(),
)
).
Count(ctx)
Puedes encontrar recorridos más avanzados en la siguiente sección.
Selección de campos
Obtener todos los nombres de mascotas.
names, err := client.Pet.
Query().
Select(pet.FieldName).
Strings(ctx)
Obtener todos los nombres únicos de mascotas.
names, err := client.Pet.
Query().
Unique(true).
Select(pet.FieldName).
Strings(ctx)
Contar el número de nombres únicos de mascotas.
n, err := client.Pet.
Query().
Unique(true).
Select(pet.FieldName).
Count(ctx)
Seleccionar objetos parciales y asociaciones parciales.
Obtener todas las mascotas y sus dueños, pero seleccionar y completar solo los campos ID y Name.
pets, err := client.Pet.
Query().
Select(pet.FieldName).
WithOwner(func (q *ent.UserQuery) {
q.Select(user.FieldName)
}).
All(ctx)
Escanear todos los nombres y edades de mascotas en una estructura personalizada.
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)
}
Actualizar una entidad y devolver una versión parcial de la misma.
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)
Eliminar uno
Eliminar una entidad:
err := client.User.
DeleteOne(a8m).
Exec(ctx)
Eliminar por ID:
err := client.User.
DeleteOneID(id).
Exec(ctx)
Eliminar uno con condición
En algunos proyectos, la operación "eliminar múltiples" no está permitida y se bloquea mediante hooks. Sin embargo, existe la necesidad
de eliminar una sola entidad por su ID asegurando que cumple una condición específica. En este caso, puedes usar la opción Where
de la siguiente manera:
err := client.Todo.
DeleteOneID(id).
Where(
// Allow deleting only expired todos.
todo.ExpireLT(time.Now()),
).
Exec(ctx)
switch {
// If the entity does not meet a specific condition,
// the operation will return an "ent.NotFoundError".
case ent.IsNotFound(err):
fmt.Println("todo item was not found")
// Any other error.
case err != nil:
fmt.Println("deletion error:", err)
}
Eliminar varios
Eliminar mediante predicados:
affected, err := client.File.
Delete().
Where(file.UpdatedAtLT(date)).
Exec(ctx)
Mutación
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.
Por ejemplo, para escribir código genérico que aplique un conjunto de métodos tanto en ent.UserCreate
como en ent.UserUpdate, utiliza el objeto UserMutation:
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")
}
En algunos casos, querrás aplicar un conjunto de métodos en varios tipos.
Para casos como este, puedes usar la interfaz genérica ent.Mutation,
o crear tu propia interfaz.
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)
}
}