Appearance
useSearch Composable ​
File Information ​
- Path:
app/composables/core/pages/useSearch.ts - Purpose: Search page logic, filtering, and results management
- Type: Core Composable
Overview ​
Search page logic, filtering, and results management
Features ​
Tabs and collections ​
Results are split into three tabs, one Typesense collection each (SEARCH_TAB_COLLECTION in useSearchUtils):
Tab (SearchTab) | Collection | Results ref |
|---|---|---|
assets | digital_assets | digitalAssets |
collages | dam_collections | collageResults |
folders | digital_assets_categories | folderResults |
- The active tab is stored inside the encrypted
qpayload astab(since ee26b58). On load (flag === "default"), a decryptedtabthat is inSEARCH_TABSwins. A plain?tab=query param is still read once when the composable is created (unknown or missing falls back toassets), so older links keep working, but it is no longer written. setTab(tab)ignores the current tab and unknown keys, thenselectTab(tab)clears asset selection, setsactiveTab, re-encrypts the URL withupdateSearchPageUrl()(onbrand_name-searchonly) and scrolls the list to the top;setTabthen setstabSwitchingand runsfetchResults("tab"), clearingtabSwitchingwhen it settles.- Only the active tab's collection is queried. Each tab keeps its own
pagination[tab]. - Collages and folders are only searchable when
libraryEnabledis true: no asset-type filter (assetTypeFilter) and no asset-only filter inallFilterList(hasAssetOnlyFilter). Otherwise a collages/folders request is skipped and that tab shows 0 results.tabsVisiblehides the tabs whenever an asset-type filter is set. - Folder hits load their item counts afterwards (
loadFolderCounts), three folders at a time, through the cachedqueryKeys.folderDetails.count(folderId, workspaceId)query.
List columns and sorting ​
activeColumns picks the column set for the active tab and passes it through withMatchColumn (useSearchUtils), which inserts MATCH_COLUMN (search_matches, label "Matched In", minw170 match-column, not sortable) right after display_file_name, only when at least one active item has search_matches and the column is not already present.
| Tab | Columns (key: label, sortable) |
|---|---|
assets (assetsColumns) | check: "" (no), display_file_name: Name, modified_at: Date Modified, created_at: Date Added, file_size: File Size, file_type: File Type (all yes) |
collages (collageColumns) | display_file_name: Name, modified_at: Date Modified, created_at: Date Added (yes); assets_count: "#Assets" (no) |
folders (folderColumns) | display_file_name: Name, modified_at: Date Modified, created_at: Date Added (yes); total_assets: "#" (no) |
handleSort(columnKey) (exposed as handleSortAssets):
- Looks the key up in
activeColumns; columns withsortingShow: falseare ignored. sortAssets(key)flips that column's direction inassetSortingState(one shared state for all three tabs), setsassetSortLoaderandassetsSortActiveColumn, resetspagination.assets.currentPageto 1 and scrollslistAssetContainerto the top.- After a fixed 2000 ms
setTimeoutit runsfetchResults("sort")and clears the loader.sortbehaves likefilterfor loading and URL updates but does not fire the "search performed" event.
The guard if (config.items.length <= 1) return; is commented out, so sorting runs (and refetches) even when the tab has zero or one result.
getAssetSortParams() uses the column in assetSortLoader when a sort is in flight; otherwise it keeps assetsSortActiveColumn and its current direction so the sort carries into later pages. With nothing set it falls back to modified_at:desc and resets the state to Date Modified descending.
Building the request (fetchResults(flag)) ​
flag is one of filter, sort, tab, scroll, search, all or a search key. Steps:
- For
filterandsort, copystore.allFilterListintoallFilterListand update the URL in the background (not awaited). - With no filters, no query and no asset-type filter (or an empty search),
allsearches*; any other flag clears results and returns. - Filters for the target collection come from
getCollectionFilters(assetTypeFilter, libraryEnabled)(see useCommonSearch); each includes the portal'sinstance_visibilityclause. - Sort:
getAssetSortParams(), remapped per collection byremapSortForCollection(display_file_namebecomescollection_nameorfolder_name;file_sizeandfile_typebecomemodified_at). With a text query it is prefixed with_text_match:desc,. commonSearchParams=buildSearchFieldParams([collection])+per_page+text_match_type: 'max_score';pageis the next page forscroll, else 1.searchApi.searchAssets(payload)posts to/api/typesense/search, prepending the_evalrelevance band. See Typesense Search API.scrollappends the next page to the active tab only. Other flags replace results, clearing the other tabs unless the flag istab. Every asset hit getsdocument.search_matches = getSearchMatches(hit)for the Matched-In chips.
URL state ​
The page keeps its full state in one encrypted q query param. updateSearchPageUrl() encrypts { searchKey, filter: allFilterList, pageType, tab: activeTab } (pageType is derived from the asset-type filter, for example images, or documents for application) and calls router.replace only when the encrypted value differs from the current q. It keeps other query params but drops a plain tab param, so the tab lives only in q. It sets isUpdatingUrl for 100 ms so the route watcher does not reload on its own change.
Decryption failure: if q cannot be decrypted on load, the composable now raises a Nuxt error with showError({ statusCode: 404, statusMessage: "The search URL is invalid.", message: "The search URL is invalid." }) and stops, instead of showing a snackbar and continuing. The error page renders inside the error-auth layout with the header search bar still visible (it is only shown on brand_name-search). A search URL with no q at all still shows the Invalid URL snackbar and redirects to /<brand_name>.
A watcher on route.query.q reloads the page (resetResult("watch") then initOps()) when q changes on brand_name-search and isUpdatingUrl is false. One exception: when the search bar submits on the search page it emits search-url-from-bar before it rewrites the URL. That sets skipBarUrlReload, so the next q change is ignored once (the flag clears itself on use, or after 1 s), and the results come from the search-results-page event instead. Without the flag the page would search twice: once from the event and once from the URL change.
Event bus handlers ​
| Event | Effect |
|---|---|
search-new-params | sets the store search key, clears results, initOps("reload") |
search-results-page | sets the store search key; loads recommended tags when the formatted key has at least MIN_SEARCH_LENGTH (3) characters; fetchResults(searchKey) or fetchResults("search") for an empty key |
search-url-from-bar | sets skipBarUrlReload for up to 1 s |
update-tags-search-page | fetchInitTags("reload") |
tags-pagination, file-type-pagination, custom-field-pagination | update the filter-panel pagination window (end capped at the tag count for tags; defaults 0-49 for custom fields) |
tags-loaded, file-type-loaded | reset that pagination window to 0-49 |
download-all-folder | resetDownload() |
download-file-search | resetDownload() when exactly one asset is selected |
menu-closed, filter-removed | resetSortingData() |
Return Type ​
Refer to the composable source code for return type definitions. Composables use TypeScript for type-safe return values.
Parameters ​
Refer to the composable source code for parameter definitions. Composables use TypeScript for type-safe parameters.
Usage Example ​
typescript
import { useSearch } from '~/composables/core/pages/useSearch'
const { /* destructured return values */ } = useSearch()Related Composables ​
- See Composables Overview for all composables
Related Documentation ​
- Composables Overview - All composables
- Types - TypeScript type definitions
- Components - Components using this composable