diff --git a/docs/docs/features/searching.md b/docs/docs/features/searching.md
index 92eb01c39d..1bdfeca8ba 100644
--- a/docs/docs/features/searching.md
+++ b/docs/docs/features/searching.md
@@ -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:
+### 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.
diff --git a/i18n/en.json b/i18n/en.json
index cc30d9e350..7616c0a55a 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -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...",
diff --git a/web/src/lib/components/shared-components/search-bar/SearchBar.svelte b/web/src/lib/components/shared-components/search-bar/SearchBar.svelte
index da61081e59..339bfb64a2 100644
--- a/web/src/lib/components/shared-components/search-bar/SearchBar.svelte
+++ b/web/src/lib/components/shared-components/search-bar/SearchBar.svelte
@@ -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;
diff --git a/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte b/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte
index 9e78531ac3..e63ab5bcf2 100644
--- a/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte
+++ b/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte
@@ -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"
/>
+
{#if featureFlagsManager.value.ocr}
{/if}
@@ -51,6 +58,10 @@
+ {:else if queryType === 'fullPath'}
+
+
+
{:else if queryType === 'ocr'}
diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts
index 9212343c04..3bb1539e0c 100644
--- a/web/src/lib/constants.ts
+++ b/web/src/lib/constants.ts
@@ -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)' },
diff --git a/web/src/lib/modals/SearchFilterModal.svelte b/web/src/lib/modals/SearchFilterModal.svelte
index 0cb5276343..0145caacaf 100644
--- a/web/src/lib/modals/SearchFilterModal.svelte
+++ b/web/src/lib/modals/SearchFilterModal.svelte
@@ -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,
diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts
index 835b6c151f..076847f65b 100644
--- a/web/src/lib/types.ts
+++ b/web/src/lib/types.ts
@@ -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;
tagIds: SvelteSet | null;
location: SearchLocationFilter;
diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte
index 488d699368..f32b74efa2 100644
--- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -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'),