Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions coderd/database/modelqueries.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
arg.Deleted,
arg.OrganizationID,
arg.ExactName,
arg.ExactDisplayName,
arg.FuzzyName,
arg.FuzzyDisplayName,
pq.Array(arg.IDs),
arg.Deprecated,
arg.HasAITask,
Expand Down
50 changes: 36 additions & 14 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions coderd/database/queries/templates.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,30 @@ WHERE
LOWER(t.name) = LOWER(@exact_name)
ELSE true
END
-- Filter by exact display name
AND CASE
WHEN @exact_display_name :: text != '' THEN
LOWER(t.display_name) = LOWER(@exact_display_name)
ELSE true
END
-- Filter by name, matching on substring
AND CASE
WHEN @fuzzy_name :: text != '' THEN
lower(t.name) ILIKE '%' || lower(@fuzzy_name) || '%'
ELSE true
END
-- Filter by display_name, matching on substring (fallback to name if display_name is empty)
AND CASE
WHEN @fuzzy_display_name :: text != '' THEN
CASE
WHEN t.display_name IS NOT NULL AND t.display_name != '' THEN
lower(t.display_name) ILIKE '%' || lower(@fuzzy_display_name) || '%'
ELSE
-- Remove spaces if present since 't.name' cannot have any spaces
lower(t.name) ILIKE '%' || REPLACE(lower(@fuzzy_display_name), ' ', '') || '%'
END
ELSE true
END
-- Filter by ids
AND CASE
WHEN array_length(@ids :: uuid[], 1) > 0 THEN
Expand Down
30 changes: 27 additions & 3 deletions coderd/searchquery/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ func Templates(ctx context.Context, db database.Store, actorID uuid.UUID, query
// Always lowercase for all searches.
query = strings.ToLower(query)
values, errors := searchTerms(query, func(term string, values url.Values) error {
// Default to the template name
values.Add("name", term)
// Default to the display name
values.Add("display_name", term)
return nil
})
if len(errors) > 0 {
Expand All @@ -281,7 +281,9 @@ func Templates(ctx context.Context, db database.Store, actorID uuid.UUID, query
Deleted: parser.Boolean(values, false, "deleted"),
OrganizationID: parseOrganization(ctx, db, parser, values, "organization"),
ExactName: parser.String(values, "", "exact_name"),
ExactDisplayName: parser.String(values, "", "exact_display_name"),
FuzzyName: parser.String(values, "", "name"),
FuzzyDisplayName: parser.String(values, "", "display_name"),
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
HasAITask: parser.NullableBoolean(values, sql.NullBool{}, "has-ai-task"),
Expand All @@ -305,7 +307,8 @@ func searchTerms(query string, defaultKey func(term string, values url.Values) e
// Because we do this in 2 passes, we want to maintain quotes on the first
// pass. Further splitting occurs on the second pass and quotes will be
// dropped.
elements := splitQueryParameterByDelimiter(query, ' ', true)
tokens := splitQueryParameterByDelimiter(query, ' ', true)
elements := processTokens(tokens)
for _, element := range elements {
if strings.HasPrefix(element, ":") || strings.HasSuffix(element, ":") {
return nil, []codersdk.ValidationError{
Expand Down Expand Up @@ -385,3 +388,24 @@ func splitQueryParameterByDelimiter(query string, delimiter rune, maintainQuotes

return parts
}

// processTokens takes the split tokens and groups them based on a delimiter (':').
// Tokens without a delimiter present are joined to support searching with spaces.
//
// Example Input: ['deprecated:false', 'test', 'template']
// Example Output: ['deprecated:false', 'test template']
func processTokens(tokens []string) []string {
var results []string
var nonFieldTerms []string
for _, token := range tokens {
if strings.Contains(token, string(':')) {
results = append(results, token)
} else {
nonFieldTerms = append(nonFieldTerms, token)
}
}
if len(nonFieldTerms) > 0 {
results = append(results, strings.Join(nonFieldTerms, " "))
}
return results
}
39 changes: 38 additions & 1 deletion coderd/searchquery/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ func TestSearchTemplates(t *testing.T) {
Name: "OnlyName",
Query: "foobar",
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "foobar",
FuzzyDisplayName: "foobar",
},
},
{
Expand Down Expand Up @@ -757,6 +757,43 @@ func TestSearchTemplates(t *testing.T) {
AuthorID: userID,
},
},
{
Name: "SearchOnDisplayName",
Query: "test name",
Expected: database.GetTemplatesWithFilterParams{
FuzzyDisplayName: "test name",
},
},
{
Name: "NameField",
Query: "name:testname",
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "testname",
},
},
{
Name: "QuotedValue",
Query: `name:"test name"`,
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "test name",
},
},
{
Name: "MultipleTerms",
Query: `foo bar exact_name:"test display name"`,
Expected: database.GetTemplatesWithFilterParams{
ExactName: "test display name",
FuzzyDisplayName: "foo bar",
},
},
{
Name: "FieldAndSpaces",
Query: "deprecated:false test template",
Expected: database.GetTemplatesWithFilterParams{
Deprecated: sql.NullBool{Bool: false, Valid: true},
FuzzyDisplayName: "test template",
},
},
}

for _, c := range testCases {
Expand Down
Loading