refactor(assistant): streamline JSON handling in query filters

- Introduced utility functions for applying LIKE conditions on JSON columns, enhancing compatibility with PostgreSQL.
- Simplified the filtering logic for tags and keywords in the GetAssistants and DeleteAssistants methods.
- Added unit tests for the new JSON handling functions to ensure correctness across different database drivers.
This commit is contained in:
Max 2026-04-06 03:22:39 +08:00
parent 8786aa9a6a
commit 4b30e80924
3 changed files with 65 additions and 18 deletions

View file

@ -319,25 +319,20 @@ 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 {
// For each tag, we need to match it as part of a JSON array
// This will match both single tag arrays ["tag1"] and multi-tag arrays ["tag1","tag2"]
pattern := fmt.Sprintf("%%\"%s\"%%", tag)
if i == 0 {
qb.Where("tags", "like", pattern)
} else {
qb.OrWhere("tags", "like", pattern)
}
store.whereJsonLike(qb, "tags", pattern, i > 0)
}
})
}
// Apply keyword filter if provided
if filter.Keywords != "" {
kw := fmt.Sprintf("%%%s%%", filter.Keywords)
qb.Where(func(qb query.Query) {
qb.Where("name", "like", fmt.Sprintf("%%%s%%", filter.Keywords)).
OrWhere("description", "like", fmt.Sprintf("%%%s%%", filter.Keywords)).
OrWhere("capabilities", "like", fmt.Sprintf("%%%s%%", filter.Keywords)).
OrWhere("locales", "like", fmt.Sprintf("%%%s%%", filter.Keywords))
qb.Where("name", "like", kw).
OrWhere("description", "like", kw).
OrWhere("capabilities", "like", kw)
store.whereJsonLike(qb, "locales", kw, true)
})
}
@ -714,20 +709,17 @@ func (store *Xun) DeleteAssistants(filter types.AssistantFilter) (int64, error)
qb.Where(func(qb query.Query) {
for i, tag := range filter.Tags {
pattern := fmt.Sprintf("%%\"%s\"%%", tag)
if i == 0 {
qb.Where("tags", "like", pattern)
} else {
qb.OrWhere("tags", "like", pattern)
}
store.whereJsonLike(qb, "tags", pattern, i > 0)
}
})
}
// Apply keyword filter if provided
if filter.Keywords != "" {
kw := fmt.Sprintf("%%%s%%", filter.Keywords)
qb.Where(func(qb query.Query) {
qb.Where("name", "like", fmt.Sprintf("%%%s%%", filter.Keywords)).
OrWhere("description", "like", fmt.Sprintf("%%%s%%", filter.Keywords))
qb.Where("name", "like", kw).
OrWhere("description", "like", kw)
})
}

View file

@ -198,6 +198,35 @@ 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)
}
}
}
// GenerateAssistantID generates a random-looking 6-digit ID
func (store *Xun) GenerateAssistantID() (string, error) {
maxAttempts := 10 // Maximum number of attempts to generate a unique ID

View file

@ -81,6 +81,32 @@ func TestSandboxRawSQLAllDialects(t *testing.T) {
assert.Contains(t, isNull, "=")
}
func TestJsonLikeExpr(t *testing.T) {
test.Prepare(t, config.Conf)
defer test.Clean()
store, err := NewXun(typeSetting("default"))
assert.NoError(t, err)
xunStore := store.(*Xun)
expr := xunStore.jsonLikeExpr("tags")
assert.Contains(t, expr, "LIKE ?")
driver := xunStore.getDriver()
switch driver {
case "postgres":
assert.Contains(t, expr, `"tags"::text`)
default:
assert.Equal(t, "tags LIKE ?", expr)
}
}
func TestJsonLikeExprFallback(t *testing.T) {
store := &Xun{}
expr := store.jsonLikeExpr("tags")
assert.Equal(t, "tags LIKE ?", expr)
}
func TestToDBTime(t *testing.T) {
assert.Equal(t, int64(0), toDBTime(0))
assert.Equal(t, int64(1234567890), toDBTime(1234567890))