mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
fix(server): add filename search (#6394)
Fixes https://github.com/immich-app/immich/issues/5982. There are basically three options: 1. Search `originalFileName` by dropping a file extension from the query (if present). Lower fidelity but very easy - just a standard index & equality. 2. Search `originalPath` by adding an index on `reverse(originalPath)` and using `starts_with(reverse(query) + "/", reverse(originalPath)`. A weird index & query but high fidelity. 3. Add a new generated column called `originalFileNameWithExtension` or something. More storage, kinda jank. TBH, I think (1) is good enough and easy to make better in the future. For example, if I search "DSC_4242.jpg", I don't really think it matters if "DSC_4242.mov" also shows up. edit: There's a fourth approach that we discussed a bit in Discord and decided we could switch to it in the future: using a GIN. The minor issue is that Postgres doesn't tokenize paths in a useful (they're a single token and it won't match against partial components). We can solve that by tokenizing it ourselves. For example: ``` immich=# with vecs as (select to_tsvector('simple', array_to_string(string_to_array('upload/library/sushain/2015/2015-08-09/IMG_275.JPG', '/'), ' ')) as vec) select * from vecs where vec @@ phraseto_tsquery('simple', array_to_string(string_to_array('library/sushain', '/'), ' ')); vec ------------------------------------------------------------------------------- '-08':6 '-09':7 '2015':4,5 'img_275.jpg':8 'library':2 'sushain':3 'upload':1 (1 row) ``` The query is also tokenized with the 'split-by-slash-join-with-space' strategy. This strategy results in `IMG_275.JPG`, `2015`, `sushain` and `library/sushain` matching. But, `08` and `IMG_275` do not match. The former is because the token is `-08` and the latter because the `img_275.jpg` token is matched against exactly.
This commit is contained in:
parent
f160969894
commit
7fc1954e2a
5 changed files with 108 additions and 9 deletions
|
|
@ -212,4 +212,81 @@ describe(`${SearchController.name}`, () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /search (file name)', () => {
|
||||
beforeEach(async () => {
|
||||
const assetId = (await assetRepository.create(generateAsset(loginResponse.userId, libraries))).id;
|
||||
await assetRepository.upsertExif({ assetId, ...searchStub.exif });
|
||||
|
||||
const assetWithMetadata = await assetRepository.getById(assetId, { exifInfo: true });
|
||||
if (!assetWithMetadata) {
|
||||
throw new Error('Asset not found');
|
||||
}
|
||||
asset1 = mapAsset(assetWithMetadata);
|
||||
});
|
||||
|
||||
it('should return assets when searching by file name', async () => {
|
||||
if (asset1?.originalFileName.length === 0) {
|
||||
throw new Error('Asset 1 does not have an original file name');
|
||||
}
|
||||
|
||||
const { status, body } = await request(server)
|
||||
.get('/search')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.query({ q: asset1.originalFileName });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toMatchObject({
|
||||
albums: {
|
||||
total: 0,
|
||||
count: 0,
|
||||
items: [],
|
||||
facets: [],
|
||||
},
|
||||
assets: {
|
||||
total: 1,
|
||||
count: 1,
|
||||
items: [
|
||||
{
|
||||
id: asset1.id,
|
||||
originalFileName: asset1.originalFileName,
|
||||
},
|
||||
],
|
||||
facets: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return assets when searching by file name with extension', async () => {
|
||||
if (asset1?.originalFileName.length === 0) {
|
||||
throw new Error('Asset 1 does not have an original file name');
|
||||
}
|
||||
|
||||
const { status, body } = await request(server)
|
||||
.get('/search')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.query({ q: asset1.originalFileName + '.jpg' });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toMatchObject({
|
||||
albums: {
|
||||
total: 0,
|
||||
count: 0,
|
||||
items: [],
|
||||
facets: [],
|
||||
},
|
||||
assets: {
|
||||
total: 1,
|
||||
count: 1,
|
||||
items: [
|
||||
{
|
||||
id: asset1.id,
|
||||
originalFileName: asset1.originalFileName,
|
||||
},
|
||||
],
|
||||
facets: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue