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

feat(web): search by filename #7624

Merged
merged 15 commits into from
Mar 5, 2024
1 change: 1 addition & 0 deletions server/src/infra/entities/asset.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_library_checksum';
@Index('IDX_day_of_month', { synchronize: false })
@Index('IDX_month', { synchronize: false })
@Index('IDX_originalPath_libraryId', ['originalPath', 'libraryId'])
@Index('idx_originalpath_trigram', { synchronize: false })
// For all assets, each originalpath must be unique per user and library
export class AssetEntity {
@PrimaryGeneratedColumn('uuid')
Expand Down
8 changes: 7 additions & 1 deletion server/src/infra/infra.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,15 @@ export function searchAssetBuilder(
builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds });
}

const path = _.pick(options, ['encodedVideoPath', 'originalFileName', 'originalPath', 'resizePath', 'webpPath']);
const path = _.pick(options, ['encodedVideoPath', 'originalFileName', 'resizePath', 'webpPath']);
builder.andWhere(_.omitBy(path, _.isUndefined));

if (options.originalPath) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this not support doing an exact match on original path anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

builder.andWhere(`${builder.alias}.originalPath ILIKE :originalPath`, {
alextran1502 marked this conversation as resolved.
Show resolved Hide resolved
originalPath: `%${options.originalPath}%`,
});
}

alextran1502 marked this conversation as resolved.
Show resolved Hide resolved
const status = _.pick(options, ['isExternal', 'isFavorite', 'isOffline', 'isReadOnly', 'isVisible', 'type']);
const {
isArchived,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddAssetOriginalPathTrigramIndex1709608140355 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE INDEX idx_originalpath_trigram
ON assets
USING gin (f_unaccent("originalPath") gin_trgm_ops)`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "idx_originalpath_trigram"`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

export type SearchFilter = {
context?: string;
fileName?: string;
alextran1502 marked this conversation as resolved.
Show resolved Hide resolved
personIds: Set<string>;
location: SearchLocationFilter;
camera: SearchCameraFilter;
Expand All @@ -32,6 +33,7 @@
import SearchMediaSection from './search-media-section.svelte';
import { parseUtcDate } from '$lib/utils/date-time';
import SearchDisplaySection from './search-display-section.svelte';
import SearchTextSection from './search-text-section.svelte';

export let searchQuery: MetadataSearchDto | SmartSearchDto;

Expand All @@ -41,6 +43,7 @@

let filter: SearchFilter = {
context: 'query' in searchQuery ? searchQuery.query : '',
fileName: 'originalFileName' in searchQuery ? searchQuery.originalFileName : undefined,
personIds: new Set('personIds' in searchQuery ? searchQuery.personIds : []),
location: {
country: searchQuery.country,
Expand Down Expand Up @@ -91,6 +94,7 @@

let payload: SmartSearchDto | MetadataSearchDto = {
query: filter.context || undefined,
originalPath: filter.fileName,
country: filter.location.country,
state: filter.location.state,
city: filter.location.city,
Expand Down Expand Up @@ -124,20 +128,8 @@
<!-- PEOPLE -->
<SearchPeopleSection width={filterBoxWidth} bind:selectedPeople={filter.personIds} />

<!-- CONTEXT -->
<div>
<label class="immich-form-label" for="context">
<span>CONTEXT</span>
<input
class="immich-form-input hover:cursor-text w-full mt-1"
type="text"
id="context"
name="context"
placeholder="Sunrise on the beach"
bind:value={filter.context}
/>
</label>
</div>
<!-- TEXT -->
<SearchTextSection bind:fileName={filter.fileName} bind:context={filter.context} />

<!-- LOCATION -->
<SearchLocationSection bind:filters={filter.location} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
export let fileName: string | undefined;
alextran1502 marked this conversation as resolved.
Show resolved Hide resolved
export let context: string | undefined;

enum TextSearchOptions {
Context = 'context',
FileName = 'file-name',
alextran1502 marked this conversation as resolved.
Show resolved Hide resolved
}
alextran1502 marked this conversation as resolved.
Show resolved Hide resolved

let selectedOption = fileName ? TextSearchOptions.FileName : TextSearchOptions.Context;

$: {
if (selectedOption === TextSearchOptions.Context) {
fileName = undefined;
} else {
context = undefined;
}
}
</script>

<div class="flex gap-5">
<label class="immich-form-label" for="context">
<input type="radio" name="context" id="context" bind:group={selectedOption} value={TextSearchOptions.Context} />
<span>CONTEXT</span>
</label>

<label class="immich-form-label" for="file-name">
<input
type="radio"
name="file-name"
id="file-name"
bind:group={selectedOption}
value={TextSearchOptions.FileName}
/>
<span>FILE NAME</span>
</label>
</div>

{#if selectedOption === TextSearchOptions.Context}
<input
class="immich-form-input hover:cursor-text w-full !mt-1"
type="text"
id="context"
name="context"
placeholder="Sunrise on the beach"
bind:value={context}
/>
{:else}
<input
class="immich-form-input hover:cursor-text w-full !mt-1"
type="text"
id="file-name"
name="file-name"
placeholder="File name or extension i.e. IMG_1234.JPG or PNG"
bind:value={fileName}
/>
{/if}
1 change: 1 addition & 0 deletions web/src/routes/(user)/search/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
make: 'Camera brand',
model: 'Camera model',
personIds: 'People',
originalPath: 'File name',
};
return keyMap[key] || key;
}
Expand Down