feat(web): add full-path search mode to UI (#26758)
Co-authored-by: mws-weekend-projects <mws-weekend-projects@users.noreply.github.com> Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> Co-authored-by: Daniel Dietzler <mail@ddietzler.dev> Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>pull/25579/head
parent
6580394cfe
commit
90a69e2ba6
|
|
@ -18,6 +18,7 @@ You can search the following types of content:
|
|||
| People | Faces that are recognized in your photos/videos. |
|
||||
| Contextual | Content of the photos and videos. |
|
||||
| File name or extension | Full or partial file's name, or file's extension |
|
||||
| Full path or folder | Full or partial folder names from the original path. |
|
||||
| Description | Description added to assets. |
|
||||
| Optical Character Recognition (OCR) | Text in images |
|
||||
| Locations | Cities, states, and countries from reverse geocoding. |
|
||||
|
|
@ -30,6 +31,12 @@ You can search the following types of content:
|
|||
|
||||
<img src={require('./img/advanced-search-filters.webp').default} width="70%" title='Advanced search filters' />
|
||||
|
||||
### Full path or folder
|
||||
|
||||
Use this mode when you know a folder name or part of the original asset path.
|
||||
|
||||
Example: for /John/Projects/3D_Printing/2026-07-01/IMG_0001.jpg, searches like Projects, 3D, Printing, or 2026 match the asset.
|
||||
|
||||
## Configuration
|
||||
|
||||
Navigating to `Administration > Settings > Machine Learning Settings > Smart Search` will show the options available.
|
||||
|
|
|
|||
|
|
@ -1240,6 +1240,7 @@
|
|||
"free_up_space_description": "Move backed-up photos and videos to your device's trash to free up space. Your copies on the server remain safe.",
|
||||
"free_up_space_settings_subtitle": "Free up device storage",
|
||||
"full_path": "Full path: {path}",
|
||||
"full_path_or_folder": "Full path or folder",
|
||||
"gcast_enabled": "Google Cast",
|
||||
"gcast_enabled_description": "This feature loads external resources from Google in order to work.",
|
||||
"general": "General",
|
||||
|
|
@ -1943,6 +1944,8 @@
|
|||
"search_by_description_example": "Hiking day in Sapa",
|
||||
"search_by_filename": "Search by file name or extension",
|
||||
"search_by_filename_example": "i.e. IMG_1234.JPG or PNG",
|
||||
"search_by_full_path": "Search by full path or folder",
|
||||
"search_by_full_path_example": "/John/Projects/3D_Printing/2026-07-01 - you can search for Projects, 3D, Printing, 2026 etc.",
|
||||
"search_by_ocr": "Search by OCR",
|
||||
"search_by_ocr_example": "Latte",
|
||||
"search_camera_lens_model": "Search lens model...",
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@
|
|||
case 'description': {
|
||||
return { description: term };
|
||||
}
|
||||
case 'fullPath': {
|
||||
const normalizedTerm = term.trim();
|
||||
return normalizedTerm ? { originalPath: normalizedTerm } : {};
|
||||
}
|
||||
case 'ocr': {
|
||||
return { ocr: term };
|
||||
}
|
||||
|
|
@ -198,6 +202,7 @@
|
|||
case 'smart':
|
||||
case 'metadata':
|
||||
case 'description':
|
||||
case 'fullPath':
|
||||
case 'ocr': {
|
||||
currentSearchType = searchType;
|
||||
return searchType;
|
||||
|
|
@ -220,6 +225,9 @@
|
|||
case 'description': {
|
||||
return $t('description');
|
||||
}
|
||||
case 'fullPath': {
|
||||
return $t('full_path_or_folder');
|
||||
}
|
||||
case 'ocr': {
|
||||
return $t('ocr');
|
||||
}
|
||||
|
|
@ -237,6 +245,7 @@
|
|||
{ value: 'smart', label: () => $t('context') },
|
||||
{ value: 'metadata', label: () => $t('filename') },
|
||||
{ value: 'description', label: () => $t('description') },
|
||||
{ value: 'fullPath', label: () => $t('full_path_or_folder') },
|
||||
{ value: 'ocr', label: () => $t('ocr') },
|
||||
] as const;
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
interface Props {
|
||||
query: string | undefined;
|
||||
queryType?: 'smart' | 'metadata' | 'description' | 'ocr';
|
||||
queryType?: 'smart' | 'metadata' | 'description' | 'fullPath' | 'ocr';
|
||||
}
|
||||
|
||||
let { query = $bindable(), queryType = $bindable('smart') }: Props = $props();
|
||||
|
|
@ -33,6 +33,13 @@
|
|||
bind:group={queryType}
|
||||
value="description"
|
||||
/>
|
||||
<RadioButton
|
||||
name="query-type"
|
||||
id="full-path-radio"
|
||||
label={$t('full_path_or_folder')}
|
||||
bind:group={queryType}
|
||||
value="fullPath"
|
||||
/>
|
||||
{#if featureFlagsManager.value.ocr}
|
||||
<RadioButton name="query-type" id="ocr-radio" label={$t('ocr')} bind:group={queryType} value="ocr" />
|
||||
{/if}
|
||||
|
|
@ -51,6 +58,10 @@
|
|||
<Field label={$t('search_by_description')}>
|
||||
<Input type="text" placeholder={$t('search_by_description_example')} bind:value={query} />
|
||||
</Field>
|
||||
{:else if queryType === 'fullPath'}
|
||||
<Field label={$t('search_by_full_path')}>
|
||||
<Input type="text" placeholder={$t('search_by_full_path_example')} bind:value={query} />
|
||||
</Field>
|
||||
{:else if queryType === 'ocr'}
|
||||
<Field label={$t('search_by_ocr')}>
|
||||
<Input type="text" placeholder={$t('search_by_ocr_example')} bind:value={query} />
|
||||
|
|
|
|||
|
|
@ -87,10 +87,17 @@ export enum QueryType {
|
|||
SMART = 'smart',
|
||||
METADATA = 'metadata',
|
||||
DESCRIPTION = 'description',
|
||||
FULL_PATH = 'fullPath',
|
||||
OCR = 'ocr',
|
||||
}
|
||||
|
||||
export const validQueryTypes = new Set([QueryType.SMART, QueryType.METADATA, QueryType.DESCRIPTION, QueryType.OCR]);
|
||||
export const validQueryTypes = new Set([
|
||||
QueryType.SMART,
|
||||
QueryType.METADATA,
|
||||
QueryType.DESCRIPTION,
|
||||
QueryType.FULL_PATH,
|
||||
QueryType.OCR,
|
||||
]);
|
||||
|
||||
export const locales = [
|
||||
{ code: 'af-ZA', name: 'Afrikaans (South Africa)' },
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@
|
|||
query = searchQuery.originalFileName;
|
||||
}
|
||||
|
||||
if ('originalPath' in searchQuery && searchQuery.originalPath) {
|
||||
query = searchQuery.originalPath;
|
||||
}
|
||||
|
||||
return {
|
||||
query,
|
||||
ocr: searchQuery.ocr,
|
||||
|
|
@ -133,6 +137,7 @@
|
|||
ocr: filter.queryType === 'ocr' ? query : undefined,
|
||||
originalFileName: filter.queryType === 'metadata' ? query : undefined,
|
||||
description: filter.queryType === 'description' ? query : undefined,
|
||||
originalPath: filter.queryType === 'fullPath' ? filter.query.trim() || undefined : undefined,
|
||||
country: filter.location.country,
|
||||
state: filter.location.state,
|
||||
city: filter.location.city,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export type SearchLocationFilter = {
|
|||
export type SearchFilter = {
|
||||
query: string;
|
||||
ocr?: string;
|
||||
queryType: 'smart' | 'metadata' | 'description' | 'ocr';
|
||||
queryType: 'smart' | 'metadata' | 'description' | 'fullPath' | 'ocr';
|
||||
personIds: SvelteSet<string>;
|
||||
tagIds: SvelteSet<string> | null;
|
||||
location: SearchLocationFilter;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@
|
|||
personIds: $t('people'),
|
||||
tagIds: $t('tags'),
|
||||
originalFileName: $t('file_name_text'),
|
||||
originalPath: $t('full_path_or_folder'),
|
||||
description: $t('description'),
|
||||
queryAssetId: $t('query_asset_id'),
|
||||
ocr: $t('ocr'),
|
||||
|
|
|
|||
Loading…
Reference in New Issue