sakshamchawla 2025-12-11 01:09:25 +07:00 committed by GitHub
commit 23032f160b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 11 deletions

@ -66,9 +66,10 @@ export class MemoryRepository implements IBulkAsset {
.selectAll('asset')
.innerJoin('memory_asset', 'asset.id', 'memory_asset.assetId')
.whereRef('memory_asset.memoriesId', '=', 'memory.id')
.orderBy('asset.fileCreatedAt', 'asc')
.where('asset.visibility', '=', sql.lit(AssetVisibility.Timeline))
.where('asset.deletedAt', 'is', null),
.where('asset.deletedAt', 'is', null)
.where(sql<boolean>`NOT asset_linked_to_hidden_person("asset"."id")`)
.orderBy('asset.fileCreatedAt', 'asc'),
).as('assets'),
)
.selectAll('memory')

@ -255,3 +255,21 @@ export const asset_face_audit = registerFunction({
RETURN NULL;
END`,
});
export const asset_linked_to_hidden_person = registerFunction({
name: 'asset_linked_to_hidden_person',
arguments: ['asset_id uuid'],
returnType: 'boolean',
language: 'PLPGSQL',
behavior: 'stable',
body: `
BEGIN
RETURN EXISTS (
SELECT 1
FROM asset_face
INNER JOIN person ON person.id = asset_face."personId"
WHERE asset_face."assetId" = asset_id
AND person."isHidden" = TRUE
);
END`,
});

@ -5,6 +5,7 @@ import {
album_user_delete_audit,
asset_delete_audit,
asset_face_audit,
asset_linked_to_hidden_person,
asset_metadata_audit,
f_concat_ws,
f_unaccent,
@ -154,6 +155,7 @@ export class ImmichDatabase {
user_metadata_audit,
asset_metadata_audit,
asset_face_audit,
asset_linked_to_hidden_person,
];
enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum];

@ -0,0 +1,24 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await sql`CREATE OR REPLACE FUNCTION asset_linked_to_hidden_person(asset_id uuid)
RETURNS boolean
STABLE LANGUAGE PLPGSQL
AS $$
BEGIN
RETURN EXISTS (
SELECT 1
FROM asset_face
INNER JOIN person ON person.id = asset_face."personId"
WHERE asset_face."assetId" = asset_id
AND person."isHidden" = TRUE
);
END
$$;`.execute(db);
await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('function_asset_linked_to_hidden_person', '{"type":"function","name":"asset_linked_to_hidden_person","sql":"CREATE OR REPLACE FUNCTION asset_linked_to_hidden_person(asset_id uuid)\\n RETURNS boolean\\n STABLE LANGUAGE PLPGSQL\\n AS $$\\n BEGIN\\n RETURN EXISTS (\\n SELECT 1\\n FROM asset_face\\n INNER JOIN person ON person.id = asset_face.\\"personId\\"\\n WHERE asset_face.\\"assetId\\" = asset_id\\n AND person.\\"isHidden\\" = TRUE\\n );\\n END\\n $$;"}'::jsonb);`.execute(db);
}
export async function down(db: Kysely<any>): Promise<void> {
await sql`DROP FUNCTION asset_linked_to_hidden_person;`.execute(db);
await sql`DELETE FROM "migration_overrides" WHERE "name" = 'function_asset_linked_to_hidden_person';`.execute(db);
}

@ -25,25 +25,26 @@ describe(MemoryService.name, () => {
});
describe('search', () => {
it('should search memories', async () => {
it('should search memories with assets', async () => {
const [userId] = newUuids();
const asset = factory.asset();
const memory1 = factory.memory({ ownerId: userId, assets: [asset] });
const memory2 = factory.memory({ ownerId: userId });
const memoryWithAsset = factory.memory({ ownerId: userId, assets: [asset] });
const memoryWithoutAsset = factory.memory({ ownerId: userId, assets: [] });
mocks.memory.search.mockResolvedValue([memory1, memory2]);
mocks.memory.search.mockResolvedValue([memoryWithAsset, memoryWithoutAsset]);
await expect(sut.search(factory.auth({ user: { id: userId } }), {})).resolves.toEqual(
expect.arrayContaining([
expect.objectContaining({ id: memory1.id, assets: [expect.objectContaining({ id: asset.id })] }),
expect.objectContaining({ id: memory2.id, assets: [] }),
expect.objectContaining({
id: memoryWithAsset.id,
assets: expect.arrayContaining([expect.objectContaining({ id: asset.id })]),
}),
]),
);
});
it('should map ', async () => {
it('should map empty result', async () => {
mocks.memory.search.mockResolvedValue([]);
await expect(sut.search(factory.auth(), {})).resolves.toEqual([]);
});
});

@ -1,5 +1,6 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { DateTime } from 'luxon';
import { Memory } from 'src/database';
import { OnJob } from 'src/decorators';
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
import { AuthDto } from 'src/dtos/auth.dto';
@ -70,7 +71,9 @@ export class MemoryService extends BaseService {
async search(auth: AuthDto, dto: MemorySearchDto) {
const memories = await this.memoryRepository.search(auth.user.id, dto);
return memories.map((memory) => mapMemory(memory, auth));
return memories
.filter((memory: Memory) => memory.assets && memory.assets.length > 0)
.map((memory: Memory) => mapMemory(memory, auth));
}
statistics(auth: AuthDto, dto: MemorySearchDto) {