谓词
字段谓词
- 布尔值:
- =, !=
- 数值:
- =, !=, >, <, >=, <=,
- IN, NOT IN
- 时间:
- =, !=, >, <, >=, <=
- IN, NOT IN
- 字符串:
- =, !=, >, <, >=, <=
- IN, NOT IN
- Contains, HasPrefix, HasSuffix
- ContainsFold, EqualFold (SQL 特有)
- JSON
- =, !=
- =, !=, >, <, >=, <= (作用于嵌套值,使用 JSON 路径)。
- Contains (作用于嵌套值,使用 JSON 路径)。
- HasKey, Len<P>
- 对嵌套值进行
null检查 (JSON 路径)。
- 可选 字段:
- IsNil, NotNil
边(Edge)谓词
HasEdge. 例如,对于类型为
Pet、名为owner的边,使用:client.Pet.
Query().
Where(pet.HasOwner()).
All(ctx)HasEdgeWith. 为边谓词添加谓词列表。
client.Pet.
Query().
Where(pet.HasOwnerWith(user.Name("a8m"))).
All(ctx)
取反 (NOT)
client.Pet.
Query().
Where(pet.Not(pet.NameHasPrefix("Ari"))).
All(ctx)
析取 (OR)
client.Pet.
Query().
Where(
pet.Or(
pet.HasOwner(),
pet.Not(pet.HasFriends()),
)
).
All(ctx)
合取 (AND)
client.Pet.
Query().
Where(
pet.And(
pet.HasOwner(),
pet.Not(pet.HasFriends()),
)
).
All(ctx)
自定义谓词
如果你想编写自己的特定方言逻辑或控制执行的查询,自定义谓词会非常有用。
获取用户 1、2、3 的所有宠物
pets := client.Pet.
Query().
Where(func(s *sql.Selector) {
s.Where(sql.InInts(pet.FieldOwnerID, 1, 2, 3))
}).
AllX(ctx)
上述代码将产生以下 SQL 查询:
SELECT DISTINCT `pets`.`id`, `pets`.`owner_id` FROM `pets` WHERE `owner_id` IN (1, 2, 3)
统计名为 URL 的 JSON 字段包含 Scheme 键的用户数量
count := client.User.
Query().
Where(func(s *sql.Selector) {
s.Where(sqljson.HasKey(user.FieldURL, sqljson.Path("Scheme")))
}).
CountX(ctx)
上述代码将产生以下 SQL 查询:
-- PostgreSQL
SELECT COUNT(DISTINCT "users"."id") FROM "users" WHERE "url"->'Scheme' IS NOT NULL
-- SQLite 和 MySQL
SELECT COUNT(DISTINCT `users`.`id`) FROM `users` WHERE JSON_EXTRACT(`url`, "$.Scheme") IS NOT NULL
获取所有拥有 "Tesla" 汽车的用户
考虑如下一个 ent 查询:
users := client.User.Query().
Where(user.HasCarWith(car.Model("Tesla"))).
AllX(ctx)
这个查询可以用 3 种不同的形式重写:IN、EXISTS 和 JOIN。
// `IN` 版本。
users := client.User.Query().
Where(func(s *sql.Selector) {
t := sql.Table(car.Table)
s.Where(
sql.In(
s.C(user.FieldID),
sql.Select(t.C(user.FieldID)).From(t).Where(sql.EQ(t.C(car.FieldModel), "Tesla")),
),
)
}).
AllX(ctx)
// `JOIN` 版本。
users := client.User.Query().
Where(func(s *sql.Selector) {
t := sql.Table(car.Table)
s.Join(t).On(s.C(user.FieldID), t.C(car.FieldOwnerID))
s.Where(sql.EQ(t.C(car.FieldModel), "Tesla"))
}).
AllX(ctx)
// `EXISTS` 版本。
users := client.User.Query().
Where(func(s *sql.Selector) {
t := sql.Table(car.Table)
p := sql.And(
sql.EQ(t.C(car.FieldModel), "Tesla"),
sql.ColumnsEQ(s.C(user.FieldID), t.C(car.FieldOwnerID)),
)
s.Where(sql.Exists(sql.Select().From(t).Where(p)))
}).
AllX(ctx)
上述代码将产生以下 SQL 查询:
-- `IN` 版本。
SELECT DISTINCT `users`.`id`, `users`.`age`, `users`.`name` FROM `users` WHERE `users`.`id` IN (SELECT `cars`.`owner_id` FROM `cars` WHERE `cars`.`model` = 'Tesla')
-- `JOIN` 版本。
SELECT DISTINCT `users`.`id`, `users`.`age`, `users`.`name` FROM `users` JOIN `cars` ON `users`.`id` = `cars`.`owner_id` WHERE `cars`.`model` = 'Tesla'
-- `EXISTS` 版本。
SELECT DISTINCT `users`.`id`, `users`.`age`, `users`.`name` FROM `users` WHERE EXISTS (SELECT * FROM `cars` WHERE `cars`.`model` = 'Tesla' AND `users`.`id` = `cars`.`owner_id`)
获取所有宠物名称包含特定模式的宠物
生成的代码为模式匹配提供了 HasPrefix、HasSuffix、Contains 和 ContainsFold 谓词。
但是,为了将 LIKE 操作符与自定义模式一起使用,请使用以下示例。
pets := client.Pet.Query().
Where(func(s *sql.Selector){
s.Where(sql.Like(pet.Name,"_B%"))
}).
AllX(ctx)
上述代码将产生以下 SQL 查询:
SELECT DISTINCT `pets`.`id`, `pets`.`owner_id`, `pets`.`name`, `pets`.`age`, `pets`.`species` FROM `pets` WHERE `name` LIKE '_B%'
自定义 SQL 函数
为了使用内置的 SQL 函数,例如 DATE(),请使用以下选项之一:
1. 使用 sql.P 选项传递一个支持方言的谓词函数:
users := client.User.Query().
Select(user.FieldID).
Where(func(s *sql.Selector) {
s.Where(sql.P(func(b *sql.Builder) {
b.WriteString("DATE(").Ident("last_login_at").WriteByte(')').WriteOp(OpGTE).Arg(value)
}))
}).
AllX(ctx)
上述代码将产生以下 SQL 查询:
SELECT `id` FROM `users` WHERE DATE(`last_login_at`) >= ?
2. 使用 ExprP() 选项内联一个谓词表达式:
users := client.User.Query().
Select(user.FieldID).
Where(func(s *sql.Selector) {
s.Where(sql.ExprP("DATE(last_login_at) >= ?", value))
}).
AllX(ctx)
上述代码将产生相同的 SQL 查询:
SELECT `id` FROM `users` WHERE DATE(`last_login_at`) >= ?
JSON 谓词
JSON 谓词默认不会作为代码生成的一部分生成。但是,ent 提供了一个名为 sqljson 的官方包,用于使用自定义谓词选项对 JSON 列应用谓词。
比较 JSON 值
sqljson.ValueEQ(user.FieldData, data)
sqljson.ValueEQ(user.FieldURL, "https", sqljson.Path("Scheme"))
sqljson.ValueNEQ(user.FieldData, content, sqljson.DotPath("attributes[1].body.content"))
sqljson.ValueGTE(user.FieldData, status.StatusBadRequest, sqljson.Path("response", "status"))
检查 JSON 键是否存在
sqljson.HasKey(user.FieldData, sqljson.Path("attributes", "[1]", "body"))
sqljson.HasKey(user.FieldData, sqljson.DotPath("attributes[1].body"))
注意,值为 null 字面量的键也匹配此操作。
检查 JSON null 字面量
sqljson.ValueIsNull(user.FieldData)
sqljson.ValueIsNull(user.FieldData, sqljson.Path("attributes"))
sqljson.ValueIsNull(user.FieldData, sqljson.DotPath("attributes[1].body"))
注意,ValueIsNull 在值为 JSON null 时返回 true,
而不是数据库 NULL。
比较 JSON 数组的长度
sqljson.LenEQ(user.FieldAttrs, 2)
sql.Or(
sqljson.LenGT(user.FieldData, 10, sqljson.Path("attributes")),
sqljson.LenLT(user.FieldData, 20, sqljson.Path("attributes")),
)
检查一个 JSON 值是否包含另一个值
sqljson.ValueContains(user.FieldData, data)
sqljson.ValueContains(user.FieldData, attrs, sqljson.Path("attributes"))
sqljson.ValueContains(user.FieldData, code, sqljson.DotPath("attributes[0].status_code"))
检查 JSON 字符串值是否包含给定子字符串,或是否有给定后缀或前缀
sqljson.StringContains(user.FieldURL, "github", sqljson.Path("host"))
sqljson.StringHasSuffix(user.FieldURL, ".com", sqljson.Path("host"))
sqljson.StringHasPrefix(user.FieldData, "20", sqljson.DotPath("attributes[0].status_code"))
检查 JSON 值是否等于列表中的任一值
sqljson.ValueIn(user.FieldURL, []any{"https", "ftp"}, sqljson.Path("Scheme"))
sqljson.ValueNotIn(user.FieldURL, []any{"github", "gitlab"}, sqljson.Path("Host"))
比较字段
dialect/sql 包提供了一组比较函数,可用于在查询中比较字段。
client.Order.Query().
Where(
sql.FieldsEQ(order.FieldTotal, order.FieldTax),
sql.FieldsNEQ(order.FieldTotal, order.FieldDiscount),
).
All(ctx)
client.Order.Query().
Where(
order.Or(
sql.FieldsGT(order.FieldTotal, order.FieldTax),
sql.FieldsLT(order.FieldTotal, order.FieldDiscount),
),
).
All(ctx)