Search and filters
Write search, give the app sort options, and build the filter controls that show in the search screen.
getSearchResults(request, page)
async getSearchResults(request: SearchRequest, page: number): Promise<PagedItemList> {
const response = await this.client.get("/search", {
params: {
q: request.query ?? "",
page,
sort: request.sort?.key,
order: request.sort?.ascending ? "asc" : "desc",
...this.mapFilters(request.filters),
},
});
const json = await response.json();
return {
items: json.results.map(toItem),
isLastPage: page >= json.total_pages,
};
}
The page value starts at 1. The app sends it separately from the request.
The request holds:
| Field | Type | When it is there |
|---|---|---|
query |
string? |
A person typed text. |
sort |
{ key, ascending? }? |
You wrote getSortOptions. |
filters |
object? |
You wrote getSearchFilters. |
Your code must accept a request with no query value. Global search and a browse with filters
only can both make one.
Sort options
Write getSortOptions, and the search screen gets a sort control.
async getSortOptions(): Promise<SortOptions> {
return {
options: [
{ id: "relevance", title: "Relevance" },
{ id: "updated", title: "Recently updated" },
{ id: "rating", title: "Rating" },
],
canChangeOrder: true,
default: { key: "relevance", ascending: false },
};
}
The value that a person selects comes back as request.sort.key.
Search filters
Write getSearchFilters, and the search screen gets filter controls. A filter uses the same view
system as a settings page.
async getSearchFilters(): Promise<SearchFilter[]> {
return [
{
id: "genres",
title: "Genres",
type: "select",
options: [
{ id: "action", title: "Action" },
{ id: "romance", title: "Romance" },
],
exclude: true,
},
{
id: "status",
title: "Status",
type: "picker",
options: [
{ id: "ongoing", title: "Ongoing" },
{ id: "completed", title: "Completed" },
],
},
];
}
Include and exclude
A select filter with exclude: true gives a person three states for each option: include it,
exclude it, or ignore it. The value arrives in this shape:
{ include: ["action"], exclude: ["romance"] }
This is important for genres, because “all except romance” is a normal need.
If the site cannot express an exclusion, do not set exclude. Do not accept the input and then
ignore it.
Map the filters to the site
The request.filters object uses the id values that you declared. Change them to what the site
needs:
mapFilters(filters: Record<string, any> = {}) {
const genres = filters.genres ?? { include: [], exclude: [] };
return {
included_tags: genres.include.join(","),
excluded_tags: genres.exclude.join(","),
status: filters.status,
};
}