跳到主要内容

谓词(Predicates)

字段谓词

  • 布尔型 Bool:
    • =, !=
  • 数字型 Numeric:
    • =, !=, >, <, >=, <=,
    • IN, NOT IN
  • 时间型 Time:
    • =, !=, >, <, >=, <=
    • IN, NOT IN
  • 字符串型 String:
    • =, !=, >, <, >=, <=
    • IN, NOT IN
    • Contains, HasPrefix, HasSuffix
    • ContainsFold, EqualFold (SQL specific)
  • JSON
    • =, !=
    • =, !=, >, <, >=, <= on nested values (JSON path).
    • Contains on nested values (JSON path).
    • HasKey, Len<P>
    • null checks for nested values (JSON path).
  • 可选 Optional 字段:
    • IsNil, NotNil

边谓词

  • HasEdge. 例如 Pet 类型的名为 owner 的边可以这样:

     client.Pet.
    Query().
    Where(pet.HasOwner()).
    All(ctx)
  • HasEdgeWith. 边谓词的谓词列表.

     client.Pet.
    Query().
    Where(pet.HasOwnerWith(user.Name("a8m"))).
    All(ctx)

否定 Negation (NOT)

client.Pet.
Query().
Where(pet.Not(pet.NameHasPrefix("Ari"))).
All(ctx)

析取 Disjunction (OR)

client.Pet.
Query().
Where(
pet.Or(
pet.HasOwner(),
pet.Not(pet.HasFriends()),
)
).
All(ctx)

连词 Conjunction (AND)

client.Pet.
Query().
Where(
pet.And(
pet.HasOwner(),
pet.Not(pet.HasFriends()),
)
).
All(ctx)

自定义谓词

如果你想要写自己的特定方言逻辑来控制执行查询时自定义谓词会很有用。

获取用户(user)1、2和3所有的宠物(pet)

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 Scheme 关键词的用户(user)的数量

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 and MySQL
SELECT COUNT(DISTINCT `users`.`id`) FROM `users` WHERE JSON_EXTRACT(`url`, "$.Scheme") IS NOT NULL

获取拥有 "Tesla" 汽车(car)的所有用户(user)

考虑使用一个这样的 ent 查询:

users := client.User.Query().
Where(user.HasCarWith(car.Model("Tesla"))).
AllX(ctx)

该查询转化为三个不同的形式:INEXISTSJOIN

// `IN` version.
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` version.
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` version.
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` version.
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` version.
SELECT DISTINCT `users`.`id`, `users`.`age`, `users`.`name` FROM `users` JOIN `cars` ON `users`.`id` = `cars`.`owner_id` WHERE `cars`.`model` = 'Tesla'

-- `EXISTS` version.
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`)

获取名称中包含特定内容的所有宠物(pet)

生成的代码提供 HasPrefixHasSuffixContainsContainsFold 谓词进行内容匹配。 然而,若要使用具有自定义内容的 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 函数

为能够使用像 DATE() 一类的内建 SQL 函数,可以使用如下选项:

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"))

需要注意如何值是 JSON 字面上的 nullValueIsNull 返回真,但不是数据库的 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)