chore: implement fuzzy name matching for templates (#14211)

* chore: add fuzzy name search for templates
* chore: implement fuzzy name matching for templates

Templates search query defaults to a fuzzy name match
This commit is contained in:
Steven Masley
2024-08-09 10:21:26 -05:00
committed by GitHub
parent 27b8f201a4
commit 591385f2ca
9 changed files with 71 additions and 10 deletions
+5
View File
@@ -9647,6 +9647,11 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G
if arg.Deprecated.Valid && arg.Deprecated.Bool == (template.Deprecated != "") {
continue
}
if arg.FuzzyName != "" {
if !strings.Contains(strings.ToLower(template.Name), strings.ToLower(arg.FuzzyName)) {
continue
}
}
if len(arg.IDs) > 0 {
match := false
+1
View File
@@ -76,6 +76,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
arg.Deleted,
arg.OrganizationID,
arg.ExactName,
arg.FuzzyName,
pq.Array(arg.IDs),
arg.Deprecated,
)
+12 -4
View File
@@ -7831,17 +7831,23 @@ WHERE
LOWER("name") = LOWER($3)
ELSE true
END
-- Filter by name, matching on substring
AND CASE
WHEN $4 :: text != '' THEN
lower(name) ILIKE '%' || lower($4) || '%'
ELSE true
END
-- Filter by ids
AND CASE
WHEN array_length($4 :: uuid[], 1) > 0 THEN
id = ANY($4)
WHEN array_length($5 :: uuid[], 1) > 0 THEN
id = ANY($5)
ELSE true
END
-- Filter by deprecated
AND CASE
WHEN $5 :: boolean IS NOT NULL THEN
WHEN $6 :: boolean IS NOT NULL THEN
CASE
WHEN $5 :: boolean THEN
WHEN $6 :: boolean THEN
deprecated != ''
ELSE
deprecated = ''
@@ -7857,6 +7863,7 @@ type GetTemplatesWithFilterParams struct {
Deleted bool `db:"deleted" json:"deleted"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
ExactName string `db:"exact_name" json:"exact_name"`
FuzzyName string `db:"fuzzy_name" json:"fuzzy_name"`
IDs []uuid.UUID `db:"ids" json:"ids"`
Deprecated sql.NullBool `db:"deprecated" json:"deprecated"`
}
@@ -7866,6 +7873,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
arg.Deleted,
arg.OrganizationID,
arg.ExactName,
arg.FuzzyName,
pq.Array(arg.IDs),
arg.Deprecated,
)
+6
View File
@@ -28,6 +28,12 @@ WHERE
LOWER("name") = LOWER(@exact_name)
ELSE true
END
-- Filter by name, matching on substring
AND CASE
WHEN @fuzzy_name :: text != '' THEN
lower(name) ILIKE '%' || lower(@fuzzy_name) || '%'
ELSE true
END
-- Filter by ids
AND CASE
WHEN array_length(@ids :: uuid[], 1) > 0 THEN
+1
View File
@@ -198,6 +198,7 @@ func Templates(ctx context.Context, db database.Store, query string) (database.G
parser := httpapi.NewQueryParamParser()
filter := database.GetTemplatesWithFilterParams{
FuzzyName: parser.String(values, "", "name"),
Deleted: parser.Boolean(values, false, "deleted"),
ExactName: parser.String(values, "", "exact_name"),
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
+7
View File
@@ -469,6 +469,13 @@ func TestSearchTemplates(t *testing.T) {
Query: "",
Expected: database.GetTemplatesWithFilterParams{},
},
{
Name: "OnlyName",
Query: "foobar",
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "foobar",
},
},
}
for _, c := range testCases {
+27 -2
View File
@@ -438,8 +438,12 @@ func TestTemplatesByOrganization(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID)
foo := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "foobar"
})
bar := coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "barbaz"
})
ctx := testutil.Context(t, testutil.WaitLong)
@@ -460,6 +464,27 @@ func TestTemplatesByOrganization(t *testing.T) {
require.Equal(t, tmpl.OrganizationDisplayName, org.DisplayName, "organization display name")
require.Equal(t, tmpl.OrganizationIcon, org.Icon, "organization display name")
}
// Check fuzzy name matching
templates, err = client.Templates(ctx, codersdk.TemplateFilter{
FuzzyName: "bar",
})
require.NoError(t, err)
require.Len(t, templates, 2)
templates, err = client.Templates(ctx, codersdk.TemplateFilter{
FuzzyName: "foo",
})
require.NoError(t, err)
require.Len(t, templates, 1)
require.Equal(t, foo.ID, templates[0].ID)
templates, err = client.Templates(ctx, codersdk.TemplateFilter{
FuzzyName: "baz",
})
require.NoError(t, err)
require.Len(t, templates, 1)
require.Equal(t, bar.ID, templates[0].ID)
})
}