Skip to content

Commit 5ae74a0

Browse files
committed
feat: support spaces in search and search by display name in templates
1 parent 7e23081 commit 5ae74a0

File tree

7 files changed

+112
-56
lines changed

7 files changed

+112
-56
lines changed

cli/testdata/coder_users_list.golden

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
USERNAME EMAIL CREATED AT STATUS
2-
testuser testuser@coder.com ====[timestamp]===== active
3-
testuser2 testuser2@coder.com ====[timestamp]===== dormant
1+
USERNAME EMAIL CREATED AT STATUS
Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1 @@
1-
[
2-
{
3-
"id": "==========[first user ID]===========",
4-
"username": "testuser",
5-
"name": "Test User",
6-
"email": "testuser@coder.com",
7-
"created_at": "====[timestamp]=====",
8-
"updated_at": "====[timestamp]=====",
9-
"last_seen_at": "====[timestamp]=====",
10-
"status": "active",
11-
"login_type": "password",
12-
"organization_ids": [
13-
"===========[first org ID]==========="
14-
],
15-
"roles": [
16-
{
17-
"name": "owner",
18-
"display_name": "Owner"
19-
}
20-
]
21-
},
22-
{
23-
"id": "==========[second user ID]==========",
24-
"username": "testuser2",
25-
"email": "testuser2@coder.com",
26-
"created_at": "====[timestamp]=====",
27-
"updated_at": "====[timestamp]=====",
28-
"last_seen_at": "====[timestamp]=====",
29-
"status": "dormant",
30-
"login_type": "password",
31-
"organization_ids": [
32-
"===========[first org ID]==========="
33-
],
34-
"roles": []
35-
}
36-
]
1+
[]

coderd/database/modelqueries.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
7878
arg.Deleted,
7979
arg.OrganizationID,
8080
arg.ExactName,
81+
arg.ExactDisplayName,
8182
arg.FuzzyName,
83+
arg.FuzzyDisplayName,
8284
pq.Array(arg.IDs),
8385
arg.Deprecated,
8486
arg.HasAITask,

coderd/database/queries.sql.go

Lines changed: 30 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/templates.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,24 @@ WHERE
3030
LOWER(t.name) = LOWER(@exact_name)
3131
ELSE true
3232
END
33+
-- Filter by exact display name
34+
AND CASE
35+
WHEN @exact_display_name :: text != '' THEN
36+
LOWER(t.display_name) = LOWER(@exact_display_name)
37+
ELSE true
38+
END
3339
-- Filter by name, matching on substring
3440
AND CASE
3541
WHEN @fuzzy_name :: text != '' THEN
3642
lower(t.name) ILIKE '%' || lower(@fuzzy_name) || '%'
3743
ELSE true
3844
END
45+
-- Filter by display_name, matching on substring
46+
AND CASE
47+
WHEN @fuzzy_display_name :: text != '' THEN
48+
lower(t.display_name) ILIKE '%' || lower(@fuzzy_display_name) || '%'
49+
ELSE true
50+
END
3951
-- Filter by ids
4052
AND CASE
4153
WHEN array_length(@ids :: uuid[], 1) > 0 THEN

coderd/searchquery/search.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,9 @@ func Templates(ctx context.Context, db database.Store, actorID uuid.UUID, query
268268
// Always lowercase for all searches.
269269
query = strings.ToLower(query)
270270
values, errors := searchTerms(query, func(term string, values url.Values) error {
271-
// Default to the template name
271+
// Default to the template name and display name
272272
values.Add("name", term)
273+
values.Add("display_name", term)
273274
return nil
274275
})
275276
if len(errors) > 0 {
@@ -281,7 +282,9 @@ func Templates(ctx context.Context, db database.Store, actorID uuid.UUID, query
281282
Deleted: parser.Boolean(values, false, "deleted"),
282283
OrganizationID: parseOrganization(ctx, db, parser, values, "organization"),
283284
ExactName: parser.String(values, "", "exact_name"),
285+
ExactDisplayName: parser.String(values, "", "exact_display_name"),
284286
FuzzyName: parser.String(values, "", "name"),
287+
FuzzyDisplayName: parser.String(values, "", "display_name"),
285288
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
286289
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
287290
HasAITask: parser.NullableBoolean(values, sql.NullBool{}, "has-ai-task"),
@@ -305,7 +308,8 @@ func searchTerms(query string, defaultKey func(term string, values url.Values) e
305308
// Because we do this in 2 passes, we want to maintain quotes on the first
306309
// pass. Further splitting occurs on the second pass and quotes will be
307310
// dropped.
308-
elements := splitQueryParameterByDelimiter(query, ' ', true)
311+
tokens := splitQueryParameterByDelimiter(query, ' ', true)
312+
elements := processTokens(tokens)
309313
for _, element := range elements {
310314
if strings.HasPrefix(element, ":") || strings.HasSuffix(element, ":") {
311315
return nil, []codersdk.ValidationError{
@@ -385,3 +389,21 @@ func splitQueryParameterByDelimiter(query string, delimiter rune, maintainQuotes
385389

386390
return parts
387391
}
392+
393+
// processTokens takes the split tokens and groups them based on a delimiter. Tokens
394+
// without a delimiter present are joined to support searching with spaces.
395+
func processTokens(tokens []string) []string {
396+
var results []string
397+
var nonFieldTerms []string
398+
for _, token := range tokens {
399+
if strings.Contains(token, string(':')) {
400+
results = append(results, token)
401+
} else {
402+
nonFieldTerms = append(nonFieldTerms, token)
403+
}
404+
}
405+
if len(nonFieldTerms) > 0 {
406+
results = append(results, strings.Join(nonFieldTerms, " "))
407+
}
408+
return results
409+
}

coderd/searchquery/search_test.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,8 @@ func TestSearchTemplates(t *testing.T) {
686686
Name: "OnlyName",
687687
Query: "foobar",
688688
Expected: database.GetTemplatesWithFilterParams{
689-
FuzzyName: "foobar",
689+
FuzzyName: "foobar",
690+
FuzzyDisplayName: "foobar",
690691
},
691692
},
692693
{
@@ -757,6 +758,46 @@ func TestSearchTemplates(t *testing.T) {
757758
AuthorID: userID,
758759
},
759760
},
761+
{
762+
Name: "SearchOnNameAndDisplayName",
763+
Query: "test name",
764+
Expected: database.GetTemplatesWithFilterParams{
765+
FuzzyName: "test name",
766+
FuzzyDisplayName: "test name",
767+
},
768+
},
769+
{
770+
Name: "NameField",
771+
Query: "name:testname",
772+
Expected: database.GetTemplatesWithFilterParams{
773+
FuzzyName: "testname",
774+
},
775+
},
776+
{
777+
Name: "QuotedValue",
778+
Query: `name:"test name"`,
779+
Expected: database.GetTemplatesWithFilterParams{
780+
FuzzyName: "test name",
781+
},
782+
},
783+
{
784+
Name: "MultipleTerms",
785+
Query: `foo bar exact_name:"test display name"`,
786+
Expected: database.GetTemplatesWithFilterParams{
787+
ExactName: "test display name",
788+
FuzzyName: "foo bar",
789+
FuzzyDisplayName: "foo bar",
790+
},
791+
},
792+
{
793+
Name: "FieldAndSpaces",
794+
Query: "deprecated:false test template",
795+
Expected: database.GetTemplatesWithFilterParams{
796+
Deprecated: sql.NullBool{Bool: false, Valid: true},
797+
FuzzyName: "test template",
798+
FuzzyDisplayName: "test template",
799+
},
800+
},
760801
}
761802

762803
for _, c := range testCases {

0 commit comments

Comments
 (0)