refactor: migrate person repository to kysely (#15242)

* refactor: migrate person repository to kysely

* `asVector` begone

* linting

* fix metadata faces

* update test

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
Daniel Dietzler
2025-01-21 19:12:28 +01:00
committed by GitHub
parent 0c152366ec
commit 332a865ce6
29 changed files with 715 additions and 747 deletions

View File

@@ -20,7 +20,7 @@ import {
SearchPaginationOptions,
SmartSearchOptions,
} from 'src/interfaces/search.interface';
import { anyUuid, asUuid, asVector } from 'src/utils/database';
import { anyUuid, asUuid } from 'src/utils/database';
import { Paginated } from 'src/utils/pagination';
import { isValidInteger } from 'src/validation';
@@ -82,7 +82,7 @@ export class SearchRepository implements ISearchRepository {
{ page: 1, size: 200 },
{
takenAfter: DummyValue.DATE,
embedding: Array.from({ length: 512 }, Math.random),
embedding: DummyValue.VECTOR,
lensModel: DummyValue.STRING,
withStacked: true,
isFavorite: true,
@@ -97,7 +97,7 @@ export class SearchRepository implements ISearchRepository {
const items = (await searchAssetBuilder(this.db, options)
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
.orderBy(sql`smart_search.embedding <=> ${asVector(options.embedding)}`)
.orderBy(sql`smart_search.embedding <=> ${options.embedding}`)
.limit(pagination.size + 1)
.offset((pagination.page - 1) * pagination.size)
.execute()) as any as AssetEntity[];
@@ -111,7 +111,7 @@ export class SearchRepository implements ISearchRepository {
params: [
{
assetId: DummyValue.UUID,
embedding: Array.from({ length: 512 }, Math.random),
embedding: DummyValue.VECTOR,
maxDistance: 0.6,
type: AssetType.IMAGE,
userIds: [DummyValue.UUID],
@@ -119,7 +119,6 @@ export class SearchRepository implements ISearchRepository {
],
})
searchDuplicates({ assetId, embedding, maxDistance, type, userIds }: AssetDuplicateSearch) {
const vector = asVector(embedding);
return this.db
.with('cte', (qb) =>
qb
@@ -127,7 +126,7 @@ export class SearchRepository implements ISearchRepository {
.select([
'assets.id as assetId',
'assets.duplicateId',
sql<number>`smart_search.embedding <=> ${vector}`.as('distance'),
sql<number>`smart_search.embedding <=> ${embedding}`.as('distance'),
])
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
.where('assets.ownerId', '=', anyUuid(userIds))
@@ -135,7 +134,7 @@ export class SearchRepository implements ISearchRepository {
.where('assets.isVisible', '=', true)
.where('assets.type', '=', type)
.where('assets.id', '!=', asUuid(assetId))
.orderBy(sql`smart_search.embedding <=> ${vector}`)
.orderBy(sql`smart_search.embedding <=> ${embedding}`)
.limit(64),
)
.selectFrom('cte')
@@ -148,7 +147,7 @@ export class SearchRepository implements ISearchRepository {
params: [
{
userIds: [DummyValue.UUID],
embedding: Array.from({ length: 512 }, Math.random),
embedding: DummyValue.VECTOR,
numResults: 10,
maxDistance: 0.6,
},
@@ -159,7 +158,6 @@ export class SearchRepository implements ISearchRepository {
throw new Error(`Invalid value for 'numResults': ${numResults}`);
}
const vector = asVector(embedding);
return this.db
.with('cte', (qb) =>
qb
@@ -167,14 +165,14 @@ export class SearchRepository implements ISearchRepository {
.select([
'asset_faces.id',
'asset_faces.personId',
sql<number>`face_search.embedding <=> ${vector}`.as('distance'),
sql<number>`face_search.embedding <=> ${embedding}`.as('distance'),
])
.innerJoin('assets', 'assets.id', 'asset_faces.assetId')
.innerJoin('face_search', 'face_search.faceId', 'asset_faces.id')
.where('assets.ownerId', '=', anyUuid(userIds))
.where('assets.deletedAt', 'is', null)
.$if(!!hasPerson, (qb) => qb.where('asset_faces.personId', 'is not', null))
.orderBy(sql`face_search.embedding <=> ${vector}`)
.orderBy(sql`face_search.embedding <=> ${embedding}`)
.limit(numResults),
)
.selectFrom('cte')
@@ -258,12 +256,11 @@ export class SearchRepository implements ISearchRepository {
.execute() as any as Promise<AssetEntity[]>;
}
async upsert(assetId: string, embedding: number[]): Promise<void> {
const vector = asVector(embedding);
async upsert(assetId: string, embedding: string): Promise<void> {
await this.db
.insertInto('smart_search')
.values({ assetId: asUuid(assetId), embedding: vector } as any)
.onConflict((oc) => oc.column('assetId').doUpdateSet({ embedding: vector } as any))
.values({ assetId: asUuid(assetId), embedding } as any)
.onConflict((oc) => oc.column('assetId').doUpdateSet({ embedding } as any))
.execute();
}