refactor(assistant): replace JSON LIKE handling with JSON contains logic

- Updated GetAssistants and DeleteAssistants methods to utilize jsonContainsValue for filtering tags and locales, improving query compatibility across database drivers.
- Renamed related test functions to reflect the new JSON handling approach.
- Removed deprecated jsonLikeExpr and whereJsonLike methods to streamline codebase.
This commit is contained in:
Max 2026-04-06 10:56:45 +08:00
parent 4b30e80924
commit 8d44ef31c1
3 changed files with 32 additions and 41 deletions

View file

@ -319,8 +319,12 @@ func (store *Xun) GetAssistants(filter types.AssistantFilter, locale ...string)
if len(filter.Tags) > 0 {
qb.Where(func(qb query.Query) {
for i, tag := range filter.Tags {
pattern := fmt.Sprintf("%%\"%s\"%%", tag)
store.whereJsonLike(qb, "tags", pattern, i > 0)
val := store.jsonContainsValue(fmt.Sprintf("%%\"%s\"%%", tag))
if i == 0 {
qb.WhereJSONContains("tags", val)
} else {
qb.OrWhereJSONContains("tags", val)
}
}
})
}
@ -332,7 +336,8 @@ func (store *Xun) GetAssistants(filter types.AssistantFilter, locale ...string)
qb.Where("name", "like", kw).
OrWhere("description", "like", kw).
OrWhere("capabilities", "like", kw)
store.whereJsonLike(qb, "locales", kw, true)
localeVal := store.jsonContainsValue(kw)
qb.OrWhereJSONContains("locales", localeVal)
})
}
@ -708,8 +713,12 @@ func (store *Xun) DeleteAssistants(filter types.AssistantFilter) (int64, error)
if len(filter.Tags) > 0 {
qb.Where(func(qb query.Query) {
for i, tag := range filter.Tags {
pattern := fmt.Sprintf("%%\"%s\"%%", tag)
store.whereJsonLike(qb, "tags", pattern, i > 0)
val := store.jsonContainsValue(fmt.Sprintf("%%\"%s\"%%", tag))
if i == 0 {
qb.WhereJSONContains("tags", val)
} else {
qb.OrWhereJSONContains("tags", val)
}
}
})
}
@ -900,6 +909,9 @@ func (store *Xun) translate(model *types.AssistantModel, assistantID string, loc
// sandboxRawSQL returns dialect-specific raw SQL fragments for sandbox JSON null detection.
// Returns (notNullExpr, isNullExpr) for filtering sandbox field.
// WhereRaw is used here because this is a JSON literal `null` comparison, not a JSON array
// contains query. Each dialect requires different casting to compare the JSON value as text.
// No bind parameters are needed (pure string comparison), so no placeholder issues.
func (store *Xun) sandboxRawSQL() (string, string) {
switch store.getDriver() {
case "postgres":

View file

@ -2,6 +2,7 @@ package xun
import (
"fmt"
"strings"
"time"
jsoniter "github.com/json-iterator/go"
@ -198,33 +199,13 @@ func (store *Xun) getDriver() string {
return "mysql"
}
// jsonLikeExpr returns a dialect-specific SQL expression for LIKE on a JSON column.
// PostgreSQL requires casting json/jsonb to text before applying LIKE.
func (store *Xun) jsonLikeExpr(column string) string {
switch store.getDriver() {
case "postgres":
return fmt.Sprintf(`"%s"::text LIKE ?`, column)
default:
return column + " LIKE ?"
}
}
// whereJsonLike applies a LIKE condition on a JSON column with proper dialect handling.
func (store *Xun) whereJsonLike(qb query.Query, column, pattern string, or bool) {
if store.getDriver() == "postgres" {
expr := fmt.Sprintf(`"%s"::text LIKE ?`, column)
if or {
qb.OrWhereRaw(expr, pattern)
} else {
qb.WhereRaw(expr, pattern)
}
} else {
if or {
qb.OrWhere(column, "like", pattern)
} else {
qb.Where(column, "like", pattern)
}
// jsonContainsValue formats a value for WhereJSONContains.
// PG/MySQL need JSON string (e.g. `"tag"`), SQLite needs LIKE pattern (e.g. `%"tag"%`).
func (store *Xun) jsonContainsValue(value string) string {
if store.getDriver() == "sqlite3" {
return value
}
return strings.TrimSuffix(strings.TrimPrefix(value, "%"), "%")
}
// GenerateAssistantID generates a random-looking 6-digit ID

View file

@ -81,7 +81,7 @@ func TestSandboxRawSQLAllDialects(t *testing.T) {
assert.Contains(t, isNull, "=")
}
func TestJsonLikeExpr(t *testing.T) {
func TestJsonContainsValue(t *testing.T) {
test.Prepare(t, config.Conf)
defer test.Clean()
@ -89,22 +89,20 @@ func TestJsonLikeExpr(t *testing.T) {
assert.NoError(t, err)
xunStore := store.(*Xun)
expr := xunStore.jsonLikeExpr("tags")
assert.Contains(t, expr, "LIKE ?")
driver := xunStore.getDriver()
val := xunStore.jsonContainsValue(`%"admin"%`)
switch driver {
case "postgres":
assert.Contains(t, expr, `"tags"::text`)
case "sqlite3":
assert.Equal(t, `%"admin"%`, val, "SQLite keeps LIKE pattern as-is")
default:
assert.Equal(t, "tags LIKE ?", expr)
assert.Equal(t, `"admin"`, val, "PG/MySQL strips % wrappers for JSON value")
}
}
func TestJsonLikeExprFallback(t *testing.T) {
func TestJsonContainsValueFallback(t *testing.T) {
store := &Xun{}
expr := store.jsonLikeExpr("tags")
assert.Equal(t, "tags LIKE ?", expr)
val := store.jsonContainsValue(`%"test"%`)
assert.Equal(t, `"test"`, val, "Default (mysql) strips % wrappers")
}
func TestToDBTime(t *testing.T) {