Skip to content

Commit 063969c

Browse files
authored
fix(server): searching with both personIds and withPeople (immich-app#13254)
* use cte * linting
1 parent 5b00bc4 commit 063969c

File tree

2 files changed

+11
-29
lines changed

2 files changed

+11
-29
lines changed

server/src/repositories/search.repository.ts

+2-19
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { asVector, searchAssetBuilder } from 'src/utils/database';
2626
import { Instrumentation } from 'src/utils/instrumentation';
2727
import { Paginated, PaginationResult, paginatedBuilder } from 'src/utils/pagination';
2828
import { isValidInteger } from 'src/validation';
29-
import { Repository, SelectQueryBuilder } from 'typeorm';
29+
import { Repository } from 'typeorm';
3030

3131
@Instrumentation()
3232
@Injectable()
@@ -113,14 +113,6 @@ export class SearchRepository implements ISearchRepository {
113113
return assets1;
114114
}
115115

116-
private createPersonFilter(builder: SelectQueryBuilder<AssetFaceEntity>, personIds: string[]) {
117-
return builder
118-
.select(`${builder.alias}."assetId"`)
119-
.where(`${builder.alias}."personId" IN (:...personIds)`, { personIds })
120-
.groupBy(`${builder.alias}."assetId"`)
121-
.having(`COUNT(DISTINCT ${builder.alias}."personId") = :personCount`, { personCount: personIds.length });
122-
}
123-
124116
@GenerateSql({
125117
params: [
126118
{ page: 1, size: 100 },
@@ -136,21 +128,12 @@ export class SearchRepository implements ISearchRepository {
136128
})
137129
async searchSmart(
138130
pagination: SearchPaginationOptions,
139-
{ embedding, userIds, personIds, ...options }: SmartSearchOptions,
131+
{ embedding, userIds, ...options }: SmartSearchOptions,
140132
): Paginated<AssetEntity> {
141133
let results: PaginationResult<AssetEntity> = { items: [], hasNextPage: false };
142134

143135
await this.assetRepository.manager.transaction(async (manager) => {
144136
let builder = manager.createQueryBuilder(AssetEntity, 'asset');
145-
146-
if (personIds?.length) {
147-
const assetFaceBuilder = manager.createQueryBuilder(AssetFaceEntity, 'asset_face');
148-
const cte = this.createPersonFilter(assetFaceBuilder, personIds);
149-
builder
150-
.addCommonTableExpression(cte, 'asset_face_ids')
151-
.innerJoin('asset_face_ids', 'a', 'a."assetId" = asset.id');
152-
}
153-
154137
builder = searchAssetBuilder(builder, options);
155138
builder
156139
.innerJoin('asset.smartSearch', 'search')

server/src/utils/database.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ from 'lodash';
2+
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
23
import { AssetEntity } from 'src/entities/asset.entity';
34
import { AssetSearchBuilderOptions } from 'src/interfaces/search.interface';
45
import { Between, IsNull, LessThanOrEqual, MoreThanOrEqual, Not, SelectQueryBuilder } from 'typeorm';
@@ -91,7 +92,6 @@ export function searchAssetBuilder(
9192
withPeople,
9293
withSmartInfo,
9394
personIds,
94-
withExif,
9595
withStacked,
9696
trashedAfter,
9797
trashedBefore,
@@ -128,15 +128,14 @@ export function searchAssetBuilder(
128128
}
129129

130130
if (personIds && personIds.length > 0) {
131-
builder
132-
.leftJoin(`${builder.alias}.faces`, 'faces')
133-
.andWhere('faces.personId IN (:...personIds)', { personIds })
134-
.addGroupBy(`${builder.alias}.id`)
135-
.having('COUNT(DISTINCT faces.personId) = :personCount', { personCount: personIds.length });
136-
137-
if (withExif) {
138-
builder.addGroupBy('exifInfo.assetId');
139-
}
131+
const cte = builder
132+
.createQueryBuilder()
133+
.select('faces."assetId"')
134+
.from(AssetFaceEntity, 'faces')
135+
.where('faces."personId" IN (:...personIds)', { personIds })
136+
.groupBy(`faces."assetId"`)
137+
.having(`COUNT(DISTINCT faces."personId") = :personCount`, { personCount: personIds.length });
138+
builder.addCommonTableExpression(cte, 'face_ids').innerJoin('face_ids', 'a', 'a."assetId" = asset.id');
140139
}
141140

142141
if (withStacked) {

0 commit comments

Comments
 (0)