Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TagSearch: Add highlighting of exact matches #811

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
34 changes: 31 additions & 3 deletions frontend/src/components/tagFilter/TagFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import SearchTagsGQL from "src/graphql/queries/SearchTags.gql";

import { SearchTagsQuery, SearchTagsQueryVariables, useTag } from "src/graphql";

type Tag = NonNullable<SearchTagsQuery["searchTag"][number]>;
type Tag = NonNullable<SearchTagsQuery["query"][number]>;

interface TagFilterProps {
tag: string;
Expand Down Expand Up @@ -54,15 +54,43 @@ const TagFilter: FC<TagFilterProps> = ({
},
});

return data.searchTag
const { exact, query } = data;

const exactResult = exact
? {
label: exact.name,
value: exact,
sublabel: exact.description ?? "",
}
: undefined;

const queryResult = query
.filter(
(tag) => !excludeTags.includes(tag.id) && (allowDeleted || !tag.deleted)
(tag) =>
!excludeTags.includes(tag.id) &&
(allowDeleted || !tag.deleted) &&
tag.id !== exact?.id
)
.map((tag) => ({
label: tag.name,
value: tag,
sublabel: tag.description ?? "",
}));

return [
...(exactResult
? [
{
label:
exactResult.label.toLowerCase() === term.toLowerCase()
? "Exact Match"
: "Alias Match",
options: [exactResult],
},
]
: []),
...(queryResult ? [{ label: "Tags", options: queryResult }] : []),
];
};

const debouncedLoadOptions = debounce(handleSearch, 400);
Expand Down
34 changes: 31 additions & 3 deletions frontend/src/components/tagSelect/TagSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TagLink } from "src/components/fragments";
import { tagHref } from "src/utils/route";
import { compareByName } from "src/utils";

type Tag = NonNullable<SearchTagsQuery["searchTag"][number]>;
type Tag = NonNullable<SearchTagsQuery["query"][number]>;

type TagSlim = {
id: string;
Expand Down Expand Up @@ -91,15 +91,43 @@ const TagSelect: FC<TagSelectProps> = ({
},
});

return data.searchTag
const { exact, query } = data;

const exactResult = exact
? {
label: exact.name,
value: exact,
sublabel: exact.description ?? "",
}
: undefined;

const queryResult = query
.filter(
(tag) => !excluded.includes(tag.id) && (allowDeleted || !tag.deleted)
(tag) =>
!excluded.includes(tag.id) &&
(allowDeleted || !tag.deleted) &&
tag.id !== exact?.id
)
.map((tag) => ({
label: tag.name,
value: tag,
sublabel: tag.description ?? "",
}));

return [
...(exactResult
? [
{
label:
exactResult.label.toLowerCase() === term.toLowerCase()
? "Exact Match"
: "Alias Match",
options: [exactResult],
},
]
: []),
...(queryResult ? [{ label: "Tags", options: queryResult }] : []),
];
};

const debouncedLoadOptions = debounce(handleSearch, 400);
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/graphql/queries/SearchTags.gql
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
fragment SearchTagFragment on Tag {
deleted
id
name
description
aliases
}

query SearchTags($term: String!, $limit: Int = 5) {
searchTag(term: $term, limit: $limit) {
deleted
id
name
description
aliases
exact: findTagOrAlias(name: $term) {
...SearchTagFragment
}
query: searchTag(term: $term, limit: $limit) {
...SearchTagFragment
}
}
101 changes: 95 additions & 6 deletions frontend/src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,8 @@ export type Query = {
findTag?: Maybe<Tag>;
/** Find a tag category by ID */
findTagCategory?: Maybe<TagCategory>;
/** Find a tag with a matching name or alias */
findTagOrAlias?: Maybe<Tag>;
/** Find user by ID or username */
findUser?: Maybe<User>;
getConfig: StashBoxConfig;
Expand Down Expand Up @@ -1151,6 +1153,11 @@ export type QueryFindTagCategoryArgs = {
id: Scalars["ID"];
};

/** The query root for this schema */
export type QueryFindTagOrAliasArgs = {
name: Scalars["String"];
};

/** The query root for this schema */
export type QueryFindUserArgs = {
id?: InputMaybe<Scalars["ID"]>;
Expand Down Expand Up @@ -18292,14 +18299,31 @@ export type SearchPerformersQuery = {
}>;
};

export type SearchTagFragment = {
__typename: "Tag";
deleted: boolean;
id: string;
name: string;
description?: string | null;
aliases: Array<string>;
};

export type SearchTagsQueryVariables = Exact<{
term: Scalars["String"];
limit?: InputMaybe<Scalars["Int"]>;
}>;

export type SearchTagsQuery = {
__typename: "Query";
searchTag: Array<{
exact?: {
__typename: "Tag";
deleted: boolean;
id: string;
name: string;
description?: string | null;
aliases: Array<string>;
} | null;
query: Array<{
__typename: "Tag";
deleted: boolean;
id: string;
Expand Down Expand Up @@ -20950,6 +20974,29 @@ export const SearchPerformerFragmentDoc = {
},
],
} as unknown as DocumentNode<SearchPerformerFragment, unknown>;
export const SearchTagFragmentDoc = {
kind: "Document",
definitions: [
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "SearchTagFragment" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "Tag" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "deleted" } },
{ kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "name" } },
{ kind: "Field", name: { kind: "Name", value: "description" } },
{ kind: "Field", name: { kind: "Name", value: "aliases" } },
],
},
},
],
} as unknown as DocumentNode<SearchTagFragment, unknown>;
export const ActivateNewUserDocument = {
kind: "Document",
definitions: [
Expand Down Expand Up @@ -48107,6 +48154,31 @@ export const SearchTagsDocument = {
selections: [
{
kind: "Field",
alias: { kind: "Name", value: "exact" },
name: { kind: "Name", value: "findTagOrAlias" },
arguments: [
{
kind: "Argument",
name: { kind: "Name", value: "name" },
value: {
kind: "Variable",
name: { kind: "Name", value: "term" },
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{
kind: "FragmentSpread",
name: { kind: "Name", value: "SearchTagFragment" },
},
],
},
},
{
kind: "Field",
alias: { kind: "Name", value: "query" },
name: { kind: "Name", value: "searchTag" },
arguments: [
{
Expand All @@ -48129,17 +48201,34 @@ export const SearchTagsDocument = {
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "deleted" } },
{ kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "name" } },
{ kind: "Field", name: { kind: "Name", value: "description" } },
{ kind: "Field", name: { kind: "Name", value: "aliases" } },
{
kind: "FragmentSpread",
name: { kind: "Name", value: "SearchTagFragment" },
},
],
},
},
],
},
},
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "SearchTagFragment" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "Tag" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "deleted" } },
{ kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "name" } },
{ kind: "Field", name: { kind: "Name", value: "description" } },
{ kind: "Field", name: { kind: "Name", value: "aliases" } },
],
},
},
],
} as unknown as DocumentNode<SearchTagsQuery, SearchTagsQueryVariables>;
export const SiteDocument = {
Expand Down
2 changes: 2 additions & 0 deletions graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type Query {
# tag names will be unique
"""Find a tag by ID or name"""
findTag(id: ID, name: String): Tag @hasRole(role: READ)
"""Find a tag with a matching name or alias"""
findTagOrAlias(name: String!): Tag @hasRole(role: READ)
queryTags(input: TagQueryInput!): QueryTagsResultType! @hasRole(role: READ)

"""Find a tag category by ID"""
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/resolver_query_find_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ func (r *queryResolver) FindTag(ctx context.Context, id *uuid.UUID, name *string
return nil, nil
}

func (r *queryResolver) FindTagOrAlias(ctx context.Context, name string) (*models.Tag, error) {
fac := r.getRepoFactory(ctx)
qb := fac.Tag()

return qb.FindByNameOrAlias(name)
}

func (r *queryResolver) QueryTags(ctx context.Context, input models.TagQueryInput) (*models.QueryTagsResultType, error) {
fac := r.getRepoFactory(ctx)
qb := fac.Tag()
Expand Down
Loading
Loading