diff --git a/agent/store/xun/assistant.go b/agent/store/xun/assistant.go index 5369e140..af1fdc4b 100644 --- a/agent/store/xun/assistant.go +++ b/agent/store/xun/assistant.go @@ -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) }) } diff --git a/agent/store/xun/xun.go b/agent/store/xun/xun.go index 181249b6..46d54230 100644 --- a/agent/store/xun/xun.go +++ b/agent/store/xun/xun.go @@ -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 diff --git a/agent/store/xun/xun_internal_test.go b/agent/store/xun/xun_internal_test.go index fc64199f..5f437d7b 100644 --- a/agent/store/xun/xun_internal_test.go +++ b/agent/store/xun/xun_internal_test.go @@ -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))