0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-03-11 02:23:09 -05:00

fix(server): search and explore part 2 (#2031)

* explore logging

* chore: regenerate open api

* fix: explore page
This commit is contained in:
Jason Rasmussen 2023-03-20 23:07:22 -04:00 committed by GitHub
parent 6e1d09fc32
commit 25a10784eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 42 deletions

View file

@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.51.0 - API version: 1.51.1
- Build package: org.openapitools.codegen.languages.DartClientCodegen - Build package: org.openapitools.codegen.languages.DartClientCodegen
## Requirements ## Requirements

View file

@ -8,7 +8,7 @@ import {
} from '@app/domain'; } from '@app/domain';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import _, { Dictionary } from 'lodash'; import _, { Dictionary } from 'lodash';
import { filter, firstValueFrom, from, map, mergeMap, toArray } from 'rxjs'; import { catchError, filter, firstValueFrom, from, map, mergeMap, of, toArray } from 'rxjs';
import { Client } from 'typesense'; import { Client } from 'typesense';
import { CollectionCreateSchema } from 'typesense/lib/Typesense/Collections'; import { CollectionCreateSchema } from 'typesense/lib/Typesense/Collections';
import { DocumentSchema, SearchResponse } from 'typesense/lib/Typesense/Documents'; import { DocumentSchema, SearchResponse } from 'typesense/lib/Typesense/Documents';
@ -148,7 +148,7 @@ export class TypesenseRepository implements ISearchRepository {
const common = { const common = {
q: '*', q: '*',
filter_by: `ownerId:${userId}`, filter_by: this.buildFilterBy('ownerId', userId, true),
per_page: 100, per_page: 100,
}; };
@ -157,8 +157,8 @@ export class TypesenseRepository implements ISearchRepository {
const { facet_counts: facets } = await asset$.search({ const { facet_counts: facets } = await asset$.search({
...common, ...common,
query_by: 'exifInfo.imageName', query_by: 'exifInfo.imageName',
facet_by: this.getFacetFieldNames(SearchCollection.ASSETS), facet_by: 'exifInfo.city,smartInfo.objects',
max_facet_values: 50, max_facet_values: 12,
}); });
return firstValueFrom( return firstValueFrom(
@ -166,23 +166,31 @@ export class TypesenseRepository implements ISearchRepository {
mergeMap( mergeMap(
(facet) => (facet) =>
from(facet.counts).pipe( from(facet.counts).pipe(
mergeMap( mergeMap((count) => {
(count) => const config = {
from( ...common,
asset$.search({ query_by: 'exifInfo.imageName',
...common, filter_by: [
query_by: 'exifInfo.imageName', this.buildFilterBy('ownerId', userId, true),
filter_by: `${facet.field_name}:${count.value}`, this.buildFilterBy(facet.field_name, count.value, true),
}), ].join(' && '),
).pipe( per_page: 1,
map((result) => ({ };
value: count.value,
data: result.hits?.[0]?.document as AssetEntity, this.logger.verbose(`Explore subquery: "filter_by:${config.filter_by}" (count:${count.count})`);
})),
filter((item) => !!item.data), return from(asset$.search(config)).pipe(
), catchError((error: any) => {
5, this.logger.warn(`Explore subquery error: ${error}`, error?.stack);
), return of({ hits: [] });
}),
map((result) => ({
value: count.value,
data: result.hits?.[0]?.document as AssetEntity,
})),
filter((item) => !!item.data),
);
}, 5),
toArray(), toArray(),
map((items) => ({ map((items) => ({
fieldName: facet.field_name as string, fieldName: facet.field_name as string,
@ -208,7 +216,7 @@ export class TypesenseRepository implements ISearchRepository {
await this.client await this.client
.collections(schemaMap[collection].name) .collections(schemaMap[collection].name)
.documents() .documents()
.delete({ filter_by: `id: [${ids.join(',')}]` }); .delete({ filter_by: this.buildFilterBy('id', ids, true) });
} }
async searchAlbums(query: string, filters: SearchFilter): Promise<SearchResult<AlbumEntity>> { async searchAlbums(query: string, filters: SearchFilter): Promise<SearchResult<AlbumEntity>> {
@ -350,18 +358,17 @@ export class TypesenseRepository implements ISearchRepository {
private getAlbumFilters(filters: SearchFilter) { private getAlbumFilters(filters: SearchFilter) {
const { userId } = filters; const { userId } = filters;
const _filters = [`ownerId:${userId}`];
const _filters = [this.buildFilterBy('ownerId', userId, true)];
if (filters.id) { if (filters.id) {
_filters.push(`id:=${filters.id}`); _filters.push(this.buildFilterBy('id', filters.id, true));
} }
for (const item of albumSchema.fields || []) { for (const item of albumSchema.fields || []) {
let value = filters[item.name as keyof SearchFilter]; const value = filters[item.name as keyof SearchFilter];
if (Array.isArray(value)) {
value = `[${value.join(',')}]`;
}
if (item.facet && value !== undefined) { if (item.facet && value !== undefined) {
_filters.push(`${item.name}:${value}`); _filters.push(this.buildFilterBy(item.name, value));
} }
} }
@ -373,17 +380,17 @@ export class TypesenseRepository implements ISearchRepository {
} }
private getAssetFilters(filters: SearchFilter) { private getAssetFilters(filters: SearchFilter) {
const _filters = [`ownerId:${filters.userId}`]; const { userId } = filters;
const _filters = [this.buildFilterBy('ownerId', userId, true)];
if (filters.id) { if (filters.id) {
_filters.push(`id:=${filters.id}`); _filters.push(this.buildFilterBy('id', filters.id, true));
} }
for (const item of assetSchema.fields || []) { for (const item of assetSchema.fields || []) {
let value = filters[item.name as keyof SearchFilter]; const value = filters[item.name as keyof SearchFilter];
if (Array.isArray(value)) {
value = `[${value.join(',')}]`;
}
if (item.facet && value !== undefined) { if (item.facet && value !== undefined) {
_filters.push(`${item.name}:${value}`); _filters.push(this.buildFilterBy(item.name, value));
} }
} }
@ -393,4 +400,19 @@ export class TypesenseRepository implements ISearchRepository {
return result; return result;
} }
private buildFilterBy(key: string, values: boolean | string | string[], exact?: boolean) {
const token = exact ? ':=' : ':';
const _values = (Array.isArray(values) ? values : [values]).map((value) => {
if (typeof value === 'boolean' || value === 'true' || value === 'false') {
return value;
}
return '`' + value + '`';
});
const value = _values.length > 1 ? `[${_values.join(',')}]` : _values[0];
return `${key}${token}${value}`;
}
} }

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.51.0 * The version of the OpenAPI document: 1.51.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.51.0 * The version of the OpenAPI document: 1.51.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.51.0 * The version of the OpenAPI document: 1.51.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.51.0 * The version of the OpenAPI document: 1.51.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.51.0 * The version of the OpenAPI document: 1.51.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).