mirror of
https://github.com/ent/ent.git
synced 2026-05-24 09:31:56 +03:00
dialect/sql: add HasPrefixFold and HasSuffixFold predicates (#4233)
* FieldHasPrefixFold and HasPrefixFold predicates * FieldHasSuffixFold and HasSuffixFold predicates * Review feedback - per dialect SQL generation.
This commit is contained in:
@@ -1716,6 +1716,32 @@ func (p *Predicate) escapedLike(col, left, right, word string) *Predicate {
|
||||
})
|
||||
}
|
||||
|
||||
// ContainsFold is a helper predicate that applies the LIKE predicate with case-folding.
|
||||
func (p *Predicate) escapedLikeFold(col, left, substr, right string) *Predicate {
|
||||
return p.Append(func(b *Builder) {
|
||||
w, escaped := escape(substr)
|
||||
switch b.dialect {
|
||||
case dialect.MySQL:
|
||||
// We assume the CHARACTER SET is configured to utf8mb4,
|
||||
// because this how it is defined in dialect/sql/schema.
|
||||
b.Ident(col).WriteString(" COLLATE utf8mb4_general_ci LIKE ")
|
||||
b.Arg(left + strings.ToLower(w) + right)
|
||||
case dialect.Postgres:
|
||||
b.Ident(col).WriteString(" ILIKE ")
|
||||
b.Arg(left + strings.ToLower(w) + right)
|
||||
default: // SQLite.
|
||||
var f Func
|
||||
f.SetDialect(b.dialect)
|
||||
f.Lower(col)
|
||||
b.WriteString(f.String()).WriteString(" LIKE ")
|
||||
b.Arg(left + strings.ToLower(w) + right)
|
||||
if escaped {
|
||||
p.WriteString(" ESCAPE ").Arg("\\")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// HasPrefix is a helper predicate that checks prefix using the LIKE predicate.
|
||||
func HasPrefix(col, prefix string) *Predicate {
|
||||
return P().HasPrefix(col, prefix)
|
||||
@@ -1726,6 +1752,16 @@ func (p *Predicate) HasPrefix(col, prefix string) *Predicate {
|
||||
return p.escapedLike(col, "", "%", prefix)
|
||||
}
|
||||
|
||||
// HasPrefixFold is a helper predicate that checks prefix using the ILIKE predicate.
|
||||
func HasPrefixFold(col, prefix string) *Predicate {
|
||||
return P().HasPrefixFold(col, prefix)
|
||||
}
|
||||
|
||||
// HasPrefixFold is a helper predicate that checks prefix using the ILIKE predicate.
|
||||
func (p *Predicate) HasPrefixFold(col, prefix string) *Predicate {
|
||||
return p.escapedLikeFold(col, "", prefix, "%")
|
||||
}
|
||||
|
||||
// ColumnsHasPrefix appends a new predicate that checks if the given column begins with the other column (prefix).
|
||||
func ColumnsHasPrefix(col, prefixC string) *Predicate {
|
||||
return P().ColumnsHasPrefix(col, prefixC)
|
||||
@@ -1760,6 +1796,14 @@ func (p *Predicate) HasSuffix(col, suffix string) *Predicate {
|
||||
return p.escapedLike(col, "%", "", suffix)
|
||||
}
|
||||
|
||||
// HasSuffixFold is a helper predicate that checks suffix using the ILIKE predicate.
|
||||
func HasSuffixFold(col, suffix string) *Predicate { return P().HasSuffixFold(col, suffix) }
|
||||
|
||||
// HasSuffixFold is a helper predicate that checks suffix using the ILIKE predicate.
|
||||
func (p *Predicate) HasSuffixFold(col, suffix string) *Predicate {
|
||||
return p.escapedLikeFold(col, "%", suffix, "")
|
||||
}
|
||||
|
||||
// EqualFold is a helper predicate that applies the "=" predicate with case-folding.
|
||||
func EqualFold(col, sub string) *Predicate { return P().EqualFold(col, sub) }
|
||||
|
||||
@@ -1800,28 +1844,7 @@ func ContainsFold(col, sub string) *Predicate { return P().ContainsFold(col, sub
|
||||
|
||||
// ContainsFold is a helper predicate that applies the LIKE predicate with case-folding.
|
||||
func (p *Predicate) ContainsFold(col, substr string) *Predicate {
|
||||
return p.Append(func(b *Builder) {
|
||||
w, escaped := escape(substr)
|
||||
switch b.dialect {
|
||||
case dialect.MySQL:
|
||||
// We assume the CHARACTER SET is configured to utf8mb4,
|
||||
// because this how it is defined in dialect/sql/schema.
|
||||
b.Ident(col).WriteString(" COLLATE utf8mb4_general_ci LIKE ")
|
||||
b.Arg("%" + strings.ToLower(w) + "%")
|
||||
case dialect.Postgres:
|
||||
b.Ident(col).WriteString(" ILIKE ")
|
||||
b.Arg("%" + strings.ToLower(w) + "%")
|
||||
default: // SQLite.
|
||||
var f Func
|
||||
f.SetDialect(b.dialect)
|
||||
f.Lower(col)
|
||||
b.WriteString(f.String()).WriteString(" LIKE ")
|
||||
b.Arg("%" + strings.ToLower(w) + "%")
|
||||
if escaped {
|
||||
p.WriteString(" ESCAPE ").Arg("\\")
|
||||
}
|
||||
}
|
||||
})
|
||||
return p.escapedLikeFold(col, "%", substr, "%")
|
||||
}
|
||||
|
||||
// CompositeGT returns a composite ">" predicate
|
||||
|
||||
@@ -521,6 +521,91 @@ func TestBuilder(t *testing.T) {
|
||||
wantQuery: `UPDATE "users" SET "age" = COALESCE("users"."age", 0) + $1 WHERE "nickname" LIKE $2`,
|
||||
wantArgs: []any{1, "a8m%"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Set("name", "foo").
|
||||
Where(And(HasPrefixFold("nickname", "a8m"), Contains("lastname", "mash"))),
|
||||
wantQuery: "UPDATE `users` SET `name` = ? WHERE LOWER(`nickname`) LIKE ? AND `lastname` LIKE ?",
|
||||
wantArgs: []any{"foo", "a8m%", "%mash%"},
|
||||
},
|
||||
{
|
||||
input: Dialect(dialect.Postgres).
|
||||
Update("users").
|
||||
Set("name", "foo").
|
||||
Where(And(HasPrefixFold("nickname", "a8m"), Contains("lastname", "mash"))),
|
||||
wantQuery: `UPDATE "users" SET "name" = $1 WHERE "nickname" ILIKE $2 AND "lastname" LIKE $3`,
|
||||
wantArgs: []any{"foo", "a8m%", "%mash%"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Add("age", 1).
|
||||
Where(HasPrefixFold("nickname", "a8m")),
|
||||
wantQuery: "UPDATE `users` SET `age` = COALESCE(`users`.`age`, 0) + ? WHERE LOWER(`nickname`) LIKE ?",
|
||||
wantArgs: []any{1, "a8m%"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Set("age", 1).
|
||||
Add("age", 2).
|
||||
Where(HasPrefixFold("nickname", "a8m")),
|
||||
wantQuery: "UPDATE `users` SET `age` = ?, `age` = COALESCE(`users`.`age`, 0) + ? WHERE LOWER(`nickname`) LIKE ?",
|
||||
wantArgs: []any{1, 2, "a8m%"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Add("age", 2).
|
||||
Set("age", 1).
|
||||
Where(HasPrefixFold("nickname", "a8m")),
|
||||
wantQuery: "UPDATE `users` SET `age` = ? WHERE LOWER(`nickname`) LIKE ?",
|
||||
wantArgs: []any{1, "a8m%"},
|
||||
},
|
||||
{
|
||||
input: Dialect(dialect.Postgres).
|
||||
Update("users").
|
||||
Add("age", 1).
|
||||
Where(HasPrefixFold("nickname", "a8m")),
|
||||
wantQuery: `UPDATE "users" SET "age" = COALESCE("users"."age", 0) + $1 WHERE "nickname" ILIKE $2`,
|
||||
wantArgs: []any{1, "a8m%"},
|
||||
},
|
||||
{
|
||||
input: Dialect(dialect.Postgres).
|
||||
Update("users").
|
||||
Set("name", "foo").
|
||||
Where(And(HasSuffixFold("nickname", "a8m"), Contains("lastname", "mash"))),
|
||||
wantQuery: `UPDATE "users" SET "name" = $1 WHERE "nickname" ILIKE $2 AND "lastname" LIKE $3`,
|
||||
wantArgs: []any{"foo", "%a8m", "%mash%"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Add("age", 1).
|
||||
Where(HasSuffixFold("nickname", "a8m")),
|
||||
wantQuery: "UPDATE `users` SET `age` = COALESCE(`users`.`age`, 0) + ? WHERE LOWER(`nickname`) LIKE ?",
|
||||
wantArgs: []any{1, "%a8m"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Set("age", 1).
|
||||
Add("age", 2).
|
||||
Where(HasSuffixFold("nickname", "a8m")),
|
||||
wantQuery: "UPDATE `users` SET `age` = ?, `age` = COALESCE(`users`.`age`, 0) + ? WHERE LOWER(`nickname`) LIKE ?",
|
||||
wantArgs: []any{1, 2, "%a8m"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Add("age", 2).
|
||||
Set("age", 1).
|
||||
Where(HasSuffixFold("nickname", "a8m")),
|
||||
wantQuery: "UPDATE `users` SET `age` = ? WHERE LOWER(`nickname`) LIKE ?",
|
||||
wantArgs: []any{1, "%a8m"},
|
||||
},
|
||||
{
|
||||
input: Dialect(dialect.Postgres).
|
||||
Update("users").
|
||||
Add("age", 1).
|
||||
Where(HasSuffixFold("nickname", "a8m")),
|
||||
wantQuery: `UPDATE "users" SET "age" = COALESCE("users"."age", 0) + $1 WHERE "nickname" ILIKE $2`,
|
||||
wantArgs: []any{1, "%a8m"},
|
||||
},
|
||||
{
|
||||
input: Update("users").
|
||||
Add("age", 1).
|
||||
|
||||
@@ -156,6 +156,13 @@ func FieldHasPrefix(name string, prefix string) func(*Selector) {
|
||||
}
|
||||
}
|
||||
|
||||
// FieldHasPrefixFold returns a raw predicate to check if the field has the given prefix with case-folding
|
||||
func FieldHasPrefixFold(name string, prefix string) func(*Selector) {
|
||||
return func(s *Selector) {
|
||||
s.Where(HasPrefixFold(s.C(name), prefix))
|
||||
}
|
||||
}
|
||||
|
||||
// FieldHasSuffix returns a raw predicate to check if the field has the given suffix.
|
||||
func FieldHasSuffix(name string, suffix string) func(*Selector) {
|
||||
return func(s *Selector) {
|
||||
@@ -163,6 +170,13 @@ func FieldHasSuffix(name string, suffix string) func(*Selector) {
|
||||
}
|
||||
}
|
||||
|
||||
// FieldHasSuffixFold returns a raw predicate to check if the field has the given suffix with case-folding
|
||||
func FieldHasSuffixFold(name string, suffix string) func(*Selector) {
|
||||
return func(s *Selector) {
|
||||
s.Where(HasSuffixFold(s.C(name), suffix))
|
||||
}
|
||||
}
|
||||
|
||||
// FieldContains returns a raw predicate to check if the field contains the given substring.
|
||||
func FieldContains(name string, substr string) func(*Selector) {
|
||||
return func(s *Selector) {
|
||||
|
||||
@@ -336,6 +336,24 @@ func TestFieldHasPrefix(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFieldHasPrefixFold(t *testing.T) {
|
||||
p := FieldHasPrefixFold("name", "a8m")
|
||||
t.Run("MySQL", func(t *testing.T) {
|
||||
s := Dialect(dialect.MySQL).Select("*").From(Table("users"))
|
||||
p(s)
|
||||
query, args := s.Query()
|
||||
require.Equal(t, "SELECT * FROM `users` WHERE `users`.`name` COLLATE utf8mb4_general_ci LIKE ?", query)
|
||||
require.Equal(t, []any{"a8m%"}, args)
|
||||
})
|
||||
t.Run("PostgreSQL", func(t *testing.T) {
|
||||
s := Dialect(dialect.Postgres).Select("*").From(Table("users"))
|
||||
p(s)
|
||||
query, args := s.Query()
|
||||
require.Equal(t, `SELECT * FROM "users" WHERE "users"."name" ILIKE $1`, query)
|
||||
require.Equal(t, []any{"a8m%"}, args)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFieldHasSuffix(t *testing.T) {
|
||||
p := FieldHasSuffix("name", "a8m")
|
||||
t.Run("MySQL", func(t *testing.T) {
|
||||
@@ -354,6 +372,24 @@ func TestFieldHasSuffix(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFieldHasSuffixFold(t *testing.T) {
|
||||
p := FieldHasSuffixFold("name", "a8m")
|
||||
t.Run("MySQL", func(t *testing.T) {
|
||||
s := Dialect(dialect.MySQL).Select("*").From(Table("users"))
|
||||
p(s)
|
||||
query, args := s.Query()
|
||||
require.Equal(t, "SELECT * FROM `users` WHERE `users`.`name` COLLATE utf8mb4_general_ci LIKE ?", query)
|
||||
require.Equal(t, []any{"%a8m"}, args)
|
||||
})
|
||||
t.Run("PostgreSQL", func(t *testing.T) {
|
||||
s := Dialect(dialect.Postgres).Select("*").From(Table("users"))
|
||||
p(s)
|
||||
query, args := s.Query()
|
||||
require.Equal(t, `SELECT * FROM "users" WHERE "users"."name" ILIKE $1`, query)
|
||||
require.Equal(t, []any{"%a8m"}, args)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFieldContains(t *testing.T) {
|
||||
p := FieldContains("name", "a8m")
|
||||
t.Run("MySQL", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user