From de9c58fd9052d5c905cf6e6f7da8238d7df33760 Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Sun, 17 Jan 2021 15:56:15 +0100 Subject: [PATCH] refactoring random photo. Fixing tests --- src/backend/middlewares/GalleryMWs.ts | 107 +++------ .../database/interfaces/IGalleryManager.ts | 1 - .../database/interfaces/ISearchManager.ts | 6 +- .../model/database/memory/SearchManager.ts | 7 +- .../model/database/sql/GalleryManager.ts | 57 +---- .../model/database/sql/SearchManager.ts | 208 ++++++++---------- src/backend/routes/GalleryRouter.ts | 15 -- src/common/QueryParams.ts | 3 +- src/common/config/public/ClientConfig.ts | 6 - test/backend/unit/model/sql/GalleryManager.ts | 47 ---- test/backend/unit/model/sql/SearchManager.ts | 142 ++++-------- 11 files changed, 171 insertions(+), 428 deletions(-) diff --git a/src/backend/middlewares/GalleryMWs.ts b/src/backend/middlewares/GalleryMWs.ts index 94e4e8de..46dd6c73 100644 --- a/src/backend/middlewares/GalleryMWs.ts +++ b/src/backend/middlewares/GalleryMWs.ts @@ -4,19 +4,17 @@ import {NextFunction, Request, Response} from 'express'; import {ErrorCodes, ErrorDTO} from '../../common/entities/Error'; import {DirectoryDTO} from '../../common/entities/DirectoryDTO'; import {ObjectManagers} from '../model/ObjectManagers'; -import {SearchTypes} from '../../common/entities/AutoCompleteItem'; import {ContentWrapper} from '../../common/entities/ConentWrapper'; import {PhotoDTO} from '../../common/entities/PhotoDTO'; import {ProjectPath} from '../ProjectPath'; import {Config} from '../../common/config/private/Config'; import {UserDTO} from '../../common/entities/UserDTO'; -import {RandomQuery} from '../model/database/interfaces/IGalleryManager'; import {MediaDTO} from '../../common/entities/MediaDTO'; import {VideoDTO} from '../../common/entities/VideoDTO'; import {Utils} from '../../common/Utils'; import {QueryParams} from '../../common/QueryParams'; import {VideoProcessing} from '../model/fileprocessing/VideoProcessing'; -import {DiskMangerWorker} from '../model/threading/DiskMangerWorker'; +import {SearchQueryDTO, SearchQueryTypes} from '../../common/entities/SearchQueryDTO'; export class GalleryMWs { @@ -115,53 +113,6 @@ export class GalleryMWs { } - public static async getRandomImage(req: Request, res: Response, next: NextFunction) { - if (Config.Client.RandomPhoto.enabled === false) { - return next(); - } - try { - const query: RandomQuery = {}; - if (req.query.directory) { - query.directory = DiskMangerWorker.normalizeDirPath(req.query.directory); - } - if (req.query.recursive === 'true') { - query.recursive = true; - } - if (req.query.orientation) { - query.orientation = parseInt(req.query.orientation.toString(), 10); - } - if (req.query.maxResolution) { - query.maxResolution = parseFloat(req.query.maxResolution.toString()); - } - if (req.query.minResolution) { - query.minResolution = parseFloat(req.query.minResolution.toString()); - } - if (req.query.fromDate) { - query.fromDate = new Date(req.query.fromDate); - } - if (req.query.toDate) { - query.toDate = new Date(req.query.toDate); - } - if (query.minResolution && query.maxResolution && query.maxResolution < query.minResolution) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'Input error: min resolution is greater than the max resolution')); - } - if (query.toDate && query.fromDate && query.toDate.getTime() < query.fromDate.getTime()) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'Input error: to date is earlier than from date')); - } - - const photo = await ObjectManagers.getInstance() - .GalleryManager.getRandomPhoto(query); - if (!photo) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'No photo found')); - } - - req.params.mediaPath = path.join(photo.directory.path, photo.directory.name, photo.name); - return next(); - } catch (e) { - return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Can\'t get random photo: ' + e.toString())); - } - } - public static async loadFile(req: Request, res: Response, next: NextFunction) { if (!(req.params.mediaPath)) { return next(); @@ -203,7 +154,7 @@ export class GalleryMWs { public static async search(req: Request, res: Response, next: NextFunction) { - if (Config.Client.Search.enabled === false) { + if (Config.Client.Search.enabled === false || !req.query[QueryParams.gallery.search.query]) { return next(); } @@ -211,12 +162,10 @@ export class GalleryMWs { return next(); } - let type: SearchTypes; - if (req.query[QueryParams.gallery.search.type]) { - type = parseInt(req.query[QueryParams.gallery.search.type], 10); - } + const query: SearchQueryDTO = req.query[QueryParams.gallery.search.type]; + try { - const result = await ObjectManagers.getInstance().SearchManager.search(req.params.text, type); + const result = await ObjectManagers.getInstance().SearchManager.search(query); result.directories.forEach(dir => dir.media = dir.media || []); req.resultPipe = new ContentWrapper(null, result); @@ -226,25 +175,6 @@ export class GalleryMWs { } } - public static async instantSearch(req: Request, res: Response, next: NextFunction) { - if (Config.Client.Search.instantSearchEnabled === false) { - return next(); - } - - if (!(req.params.text)) { - return next(); - } - - try { - const result = await ObjectManagers.getInstance().SearchManager.instantSearch(req.params.text); - - result.directories.forEach(dir => dir.media = dir.media || []); - req.resultPipe = new ContentWrapper(null, result); - return next(); - } catch (err) { - return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Error during searching', err)); - } - } public static async autocomplete(req: Request, res: Response, next: NextFunction) { if (Config.Client.Search.AutoComplete.enabled === false) { @@ -254,8 +184,12 @@ export class GalleryMWs { return next(); } + let type: SearchQueryTypes = SearchQueryTypes.any_text; + if (req.query[QueryParams.gallery.search.type]) { + type = parseInt(req.query[QueryParams.gallery.search.type], 10); + } try { - req.resultPipe = await ObjectManagers.getInstance().SearchManager.autocomplete(req.params.text); + req.resultPipe = await ObjectManagers.getInstance().SearchManager.autocomplete(req.params.text, type); return next(); } catch (err) { return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Error during searching', err)); @@ -264,4 +198,25 @@ export class GalleryMWs { } + public static async getRandomImage(req: Request, res: Response, next: NextFunction) { + if (Config.Client.RandomPhoto.enabled === false) { + return next(); + } + try { + const query: SearchQueryDTO = req.query[QueryParams.gallery.search.type]; + + const photo = await ObjectManagers.getInstance() + .SearchManager.getRandomPhoto(query); + if (!photo) { + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'No photo found')); + } + + req.params.mediaPath = path.join(photo.directory.path, photo.directory.name, photo.name); + return next(); + } catch (e) { + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Can\'t get random photo: ' + e.toString())); + } + } + + } diff --git a/src/backend/model/database/interfaces/IGalleryManager.ts b/src/backend/model/database/interfaces/IGalleryManager.ts index 8128feb8..79a635f3 100644 --- a/src/backend/model/database/interfaces/IGalleryManager.ts +++ b/src/backend/model/database/interfaces/IGalleryManager.ts @@ -17,6 +17,5 @@ export interface IGalleryManager { knownLastModified?: number, knownLastScanned?: number): Promise; - getRandomPhoto(queryFilter: RandomQuery): Promise; } diff --git a/src/backend/model/database/interfaces/ISearchManager.ts b/src/backend/model/database/interfaces/ISearchManager.ts index e8753766..6f3ee071 100644 --- a/src/backend/model/database/interfaces/ISearchManager.ts +++ b/src/backend/model/database/interfaces/ISearchManager.ts @@ -1,11 +1,13 @@ import {AutoCompleteItem} from '../../../../common/entities/AutoCompleteItem'; import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO'; import {SearchQueryDTO, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO'; +import {PhotoDTO} from '../../../../common/entities/PhotoDTO'; +import {RandomQuery} from './IGalleryManager'; export interface ISearchManager { - autocomplete(text: string): Promise; + autocomplete(text: string, type: SearchQueryTypes): Promise; search(query: SearchQueryDTO): Promise; - instantSearch(text: string): Promise; + getRandomPhoto(queryFilter: SearchQueryDTO): Promise; } diff --git a/src/backend/model/database/memory/SearchManager.ts b/src/backend/model/database/memory/SearchManager.ts index 587069ec..efbed5b3 100644 --- a/src/backend/model/database/memory/SearchManager.ts +++ b/src/backend/model/database/memory/SearchManager.ts @@ -4,16 +4,11 @@ import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO'; import {SearchQueryDTO, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO'; export class SearchManager implements ISearchManager { - autocomplete(text: string): Promise { + autocomplete(text: string, type: SearchQueryTypes): Promise { throw new Error('Method not implemented.'); } search(query: SearchQueryDTO): Promise { throw new Error('Method not implemented.'); } - - instantSearch(text: string): Promise { - throw new Error('Method not implemented.'); - } - } diff --git a/src/backend/model/database/sql/GalleryManager.ts b/src/backend/model/database/sql/GalleryManager.ts index 79686654..d3a135eb 100644 --- a/src/backend/model/database/sql/GalleryManager.ts +++ b/src/backend/model/database/sql/GalleryManager.ts @@ -19,6 +19,7 @@ import {FaceRegionEntry} from './enitites/FaceRegionEntry'; import {ObjectManagers} from '../../ObjectManagers'; import {DuplicatesDTO} from '../../../../common/entities/DuplicatesDTO'; import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; +import {SearchQueryDTO} from '../../../../common/entities/SearchQueryDTO'; const LOG_TAG = '[GalleryManager]'; @@ -87,62 +88,6 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { } - public async getRandomPhoto(queryFilter: RandomQuery): Promise { - const connection = await SQLConnection.getConnection(); - const photosRepository = connection.getRepository(PhotoEntity); - const query: SelectQueryBuilder = photosRepository.createQueryBuilder('photo'); - query.innerJoinAndSelect('photo.directory', 'directory'); - - if (queryFilter.directory) { - const directoryName = path.basename(queryFilter.directory); - const directoryParent = path.join(path.dirname(queryFilter.directory), path.sep); - - query.where(new Brackets(qb => { - qb.where('directory.name = :name AND directory.path = :path', { - name: directoryName, - path: directoryParent - }); - - if (queryFilter.recursive) { - qb.orWhere('directory.path LIKE :text COLLATE utf8_general_ci', {text: queryFilter.directory + '%'}); - } - })); - } - - if (queryFilter.fromDate) { - query.andWhere('photo.metadata.creationDate >= :fromDate', { - fromDate: queryFilter.fromDate.getTime() - }); - } - if (queryFilter.toDate) { - query.andWhere('photo.metadata.creationDate <= :toDate', { - toDate: queryFilter.toDate.getTime() - }); - } - if (queryFilter.minResolution) { - query.andWhere('photo.metadata.size.width * photo.metadata.size.height >= :minRes', { - minRes: queryFilter.minResolution * 1000 * 1000 - }); - } - - if (queryFilter.maxResolution) { - query.andWhere('photo.metadata.size.width * photo.metadata.size.height <= :maxRes', { - maxRes: queryFilter.maxResolution * 1000 * 1000 - }); - } - if (queryFilter.orientation === OrientationType.landscape) { - query.andWhere('photo.metadata.size.width >= photo.metadata.size.height'); - } - if (queryFilter.orientation === OrientationType.portrait) { - query.andWhere('photo.metadata.size.width <= photo.metadata.size.height'); - } - - if (Config.Server.Database.type === ServerConfig.DatabaseType.mysql) { - return await query.groupBy('RAND(), photo.id').limit(1).getOne(); - } - return await query.groupBy('RANDOM()').limit(1).getOne(); - - } async countDirectories(): Promise { const connection = await SQLConnection.getConnection(); diff --git a/src/backend/model/database/sql/SearchManager.ts b/src/backend/model/database/sql/SearchManager.ts index 1015c3b2..d7094b05 100644 --- a/src/backend/model/database/sql/SearchManager.ts +++ b/src/backend/model/database/sql/SearchManager.ts @@ -27,6 +27,8 @@ import { import {GalleryManager} from './GalleryManager'; import {ObjectManagers} from '../../ObjectManagers'; import {Utils} from '../../../../common/Utils'; +import {PhotoDTO} from '../../../../common/entities/PhotoDTO'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; export class SearchManager implements ISearchManager { @@ -43,7 +45,7 @@ export class SearchManager implements ISearchManager { return a; } - async autocomplete(text: string): Promise { + async autocomplete(text: string, type: SearchQueryTypes): Promise { const connection = await SQLConnection.getConnection(); @@ -54,88 +56,83 @@ export class SearchManager implements ISearchManager { const directoryRepository = connection.getRepository(DirectoryEntity); - (await photoRepository - .createQueryBuilder('photo') - .select('DISTINCT(photo.metadata.keywords)') - .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) - .getRawMany()) - .map(r => >(r.metadataKeywords).split(',')) - .forEach(keywords => { - result = result.concat(this.encapsulateAutoComplete(keywords - .filter(k => k.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.keyword)); - }); + if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.keyword) { + (await photoRepository + .createQueryBuilder('photo') + .select('DISTINCT(photo.metadata.keywords)') + .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) + .getRawMany()) + .map(r => >(r.metadataKeywords).split(',')) + .forEach(keywords => { + result = result.concat(this.encapsulateAutoComplete(keywords + .filter(k => k.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.keyword)); + }); + } - result = result.concat(this.encapsulateAutoComplete((await personRepository - .createQueryBuilder('person') - .select('DISTINCT(person.name)') - .where('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) - .orderBy('person.name') - .getRawMany()) - .map(r => r.name), SearchQueryTypes.person)); + if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.person) { + result = result.concat(this.encapsulateAutoComplete((await personRepository + .createQueryBuilder('person') + .select('DISTINCT(person.name)') + .where('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) + .orderBy('person.name') + .getRawMany()) + .map(r => r.name), SearchQueryTypes.person)); + } - (await photoRepository - .createQueryBuilder('photo') - .select('photo.metadata.positionData.country as country, ' + - 'photo.metadata.positionData.state as state, photo.metadata.positionData.city as city') - .where('photo.metadata.positionData.country LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('photo.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('photo.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .groupBy('photo.metadata.positionData.country, photo.metadata.positionData.state, photo.metadata.positionData.city') - .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) - .getRawMany()) - .filter(pm => !!pm) - .map(pm => >[pm.city || '', pm.country || '', pm.state || '']) - .forEach(positions => { - result = result.concat(this.encapsulateAutoComplete(positions - .filter(p => p.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.position)); - }); + if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.position) { + (await photoRepository + .createQueryBuilder('photo') + .select('photo.metadata.positionData.country as country, ' + + 'photo.metadata.positionData.state as state, photo.metadata.positionData.city as city') + .where('photo.metadata.positionData.country LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('photo.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('photo.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .groupBy('photo.metadata.positionData.country, photo.metadata.positionData.state, photo.metadata.positionData.city') + .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) + .getRawMany()) + .filter(pm => !!pm) + .map(pm => >[pm.city || '', pm.country || '', pm.state || '']) + .forEach(positions => { + result = result.concat(this.encapsulateAutoComplete(positions + .filter(p => p.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.position)); + }); + } - result = result.concat(this.encapsulateAutoComplete((await mediaRepository - .createQueryBuilder('media') - .select('DISTINCT(media.name)') - .where('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) - .getRawMany()) - .map(r => r.name), SearchQueryTypes.file_name)); + if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.file_name) { + result = result.concat(this.encapsulateAutoComplete((await mediaRepository + .createQueryBuilder('media') + .select('DISTINCT(media.name)') + .where('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) + .getRawMany()) + .map(r => r.name), SearchQueryTypes.file_name)); + } + if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.caption) { + result = result.concat(this.encapsulateAutoComplete((await photoRepository + .createQueryBuilder('media') + .select('DISTINCT(media.metadata.caption) as caption') + .where('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) + .getRawMany()) + .map(r => r.caption), SearchQueryTypes.caption)); + } - result = result.concat(this.encapsulateAutoComplete((await photoRepository - .createQueryBuilder('media') - .select('DISTINCT(media.metadata.caption) as caption') - .where('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) - .getRawMany()) - .map(r => r.caption), SearchQueryTypes.caption)); - - - result = result.concat(this.encapsulateAutoComplete((await directoryRepository - .createQueryBuilder('dir') - .select('DISTINCT(dir.name)') - .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) - .getRawMany()) - .map(r => r.name), SearchQueryTypes.directory)); - + if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.directory) { + result = result.concat(this.encapsulateAutoComplete((await directoryRepository + .createQueryBuilder('dir') + .select('DISTINCT(dir.name)') + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) + .getRawMany()) + .map(r => r.name), SearchQueryTypes.directory)); + } return SearchManager.autoCompleteItemsUnique(result); } - async getGPSData(query: SearchQueryDTO) { - if ((query as ANDSearchQuery | ORSearchQuery).list) { - for (let i = 0; i < (query as ANDSearchQuery | ORSearchQuery).list.length; ++i) { - (query as ANDSearchQuery | ORSearchQuery).list[i] = - await this.getGPSData((query as ANDSearchQuery | ORSearchQuery).list[i]); - } - } - if (query.type === SearchQueryTypes.distance && (query).from.text) { - (query).from.GPSData = - await ObjectManagers.getInstance().LocationManager.getGPSData((query).from.text); - } - return query; - } - async search(queryIN: SearchQueryDTO) { let query = this.flattenSameOfQueries(queryIN); query = await this.getGPSData(query); @@ -188,51 +185,34 @@ export class SearchManager implements ISearchManager { return result; } - async instantSearch(text: string): Promise { + public async getRandomPhoto(query: SearchQueryDTO): Promise { const connection = await SQLConnection.getConnection(); - - const result: SearchResultDTO = { - searchQuery: {type: SearchQueryTypes.any_text, text: text}, - // searchType:undefined, not adding this - directories: [], - media: [], - metaFile: [], - resultOverflow: false - }; - - const query = await connection.getRepository(MediaEntity).createQueryBuilder('media') - .innerJoin(q => q.from(MediaEntity, 'media') - .select('distinct media.id') - .limit(10) - .leftJoin('media.directory', 'directory') - .leftJoin('media.metadata.faces', 'faces') - .leftJoin('faces.person', 'person') - .where('media.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.country LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .orWhere('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - , - 'innerMedia', - 'media.id=innerMedia.id') - .leftJoinAndSelect('media.directory', 'directory') - .leftJoinAndSelect('media.metadata.faces', 'faces') - .leftJoinAndSelect('faces.person', 'person'); + const sqlQuery: SelectQueryBuilder = connection + .getRepository(PhotoEntity) + .createQueryBuilder('media') + .innerJoinAndSelect('media.directory', 'directory') + .where(this.buildWhereQuery(query)); - result.media = await this.loadMediaWithFaces(query); + if (Config.Server.Database.type === ServerConfig.DatabaseType.mysql) { + return await sqlQuery.groupBy('RAND(), media.id').limit(1).getOne(); + } + return await sqlQuery.groupBy('RANDOM()').limit(1).getOne(); + } - result.directories = await connection - .getRepository(DirectoryEntity) - .createQueryBuilder('dir') - .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) - .limit(10) - .getMany(); - - return result; + private async getGPSData(query: SearchQueryDTO) { + if ((query as ANDSearchQuery | ORSearchQuery).list) { + for (let i = 0; i < (query as ANDSearchQuery | ORSearchQuery).list.length; ++i) { + (query as ANDSearchQuery | ORSearchQuery).list[i] = + await this.getGPSData((query as ANDSearchQuery | ORSearchQuery).list[i]); + } + } + if (query.type === SearchQueryTypes.distance && (query).from.text) { + (query).from.GPSData = + await ObjectManagers.getInstance().LocationManager.getGPSData((query).from.text); + } + return query; } private buildWhereQuery(query: SearchQueryDTO, paramCounter = {value: 0}): Brackets { diff --git a/src/backend/routes/GalleryRouter.ts b/src/backend/routes/GalleryRouter.ts index 6ddca2e1..c38efb49 100644 --- a/src/backend/routes/GalleryRouter.ts +++ b/src/backend/routes/GalleryRouter.ts @@ -25,7 +25,6 @@ export class GalleryRouter { this.addDirectoryList(app); this.addSearch(app); - this.addInstantSearch(app); this.addAutoComplete(app); } @@ -199,20 +198,6 @@ export class GalleryRouter { ); } - protected static addInstantSearch(app: Express) { - app.get('/api/instant-search/:text', - // common part - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Guest), - VersionMWs.injectGalleryVersion, - - // specific part - GalleryMWs.instantSearch, - ThumbnailGeneratorMWs.addThumbnailInformation, - GalleryMWs.cleanUpGalleryResults, - RenderingMWs.renderResult - ); - } protected static addAutoComplete(app: Express) { app.get('/api/autocomplete/:text', diff --git a/src/common/QueryParams.ts b/src/common/QueryParams.ts index 0cac6f5d..ba7c52e4 100644 --- a/src/common/QueryParams.ts +++ b/src/common/QueryParams.ts @@ -10,7 +10,8 @@ export const QueryParams = { maxResolution: 'toRes' }, search: { - type: 'type' + type: 'type', + query: 'qs' }, photo: 'p', sharingKey_query: 'sk', diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index 774310e4..8f7b5e90 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -25,12 +25,6 @@ export module ClientConfig { export class SearchConfig { @ConfigProperty() enabled: boolean = true; - @ConfigProperty() - instantSearchEnabled: boolean = true; - @ConfigProperty({type: 'unsignedInt'}) - InstantSearchTimeout: number = 3000; - @ConfigProperty({type: 'unsignedInt'}) - instantSearchCacheTimeout: number = 1000 * 60 * 60; @ConfigProperty({type: 'unsignedInt'}) searchCacheTimeout: number = 1000 * 60 * 60; @ConfigProperty() diff --git a/test/backend/unit/model/sql/GalleryManager.ts b/test/backend/unit/model/sql/GalleryManager.ts index 1e409a1e..47e65e84 100644 --- a/test/backend/unit/model/sql/GalleryManager.ts +++ b/test/backend/unit/model/sql/GalleryManager.ts @@ -1,21 +1,6 @@ -import {expect} from 'chai'; -import {TestHelper} from './TestHelper'; import {SQLTestHelper} from '../../../SQLTestHelper'; import {GalleryManager} from '../../../../../src/backend/model/database/sql/GalleryManager'; -import {IndexingManager} from '../../../../../src/backend/model/database/sql/IndexingManager'; -import {DirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO'; -import {Utils} from '../../../../../src/common/Utils'; -import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers'; -import {PersonManager} from '../../../../../src/backend/model/database/sql/PersonManager'; -import {MediaEntity} from '../../../../../src/backend/model/database/sql/enitites/MediaEntity'; -import {VersionManager} from '../../../../../src/backend/model/database/sql/VersionManager'; -class IndexingManagerTest extends IndexingManager { - - public async saveToDB(scannedDirectory: DirectoryDTO): Promise { - return super.saveToDB(scannedDirectory); - } -} // to help WebStorm to handle the test cases declare let describe: any; @@ -25,36 +10,4 @@ describe = SQLTestHelper.describe; describe('GalleryManager', (sqlHelper: SQLTestHelper) => { - beforeEach(async () => { - await sqlHelper.initDB(); - ObjectManagers.getInstance().PersonManager = new PersonManager(); - ObjectManagers.getInstance().VersionManager = new VersionManager(); - }); - - - after(async () => { - await sqlHelper.clearDB(); - }); - - it('should get random photo', async () => { - const gm = new GalleryManager(); - const im = new IndexingManagerTest(); - - const parent = TestHelper.getRandomizedDirectoryEntry(); - const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1'); - expect(await gm.getRandomPhoto({})).to.not.exist; - DirectoryDTO.removeReferences(parent); - await im.saveToDB(Utils.clone(parent)); - - delete p1.metadata.faces; - delete p1.directory; - delete p1.id; - const found: MediaEntity = await gm.getRandomPhoto({}); - delete found.metadata.bitRate; - delete found.metadata.duration; - delete found.directory; - delete found.id; - expect(Utils.clone(found)).to.be.deep.equal(Utils.clone(p1)); - }); - }); diff --git a/test/backend/unit/model/sql/SearchManager.ts b/test/backend/unit/model/sql/SearchManager.ts index e8758cd5..9f959714 100644 --- a/test/backend/unit/model/sql/SearchManager.ts +++ b/test/backend/unit/model/sql/SearchManager.ts @@ -105,30 +105,6 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => { const setUpSqlDB = async () => { await sqlHelper.initDB(); await setUpTestGallery(); - /* - const savePhoto = async (photo: PhotoDTO) => { - const savedPhoto = await pr.save(photo); - if (!photo.metadata.faces) { - return; - } - for (let i = 0; i < photo.metadata.faces.length; i++) { - const face = photo.metadata.faces[i]; - const person = await conn.getRepository(PersonEntry).save({name: face.name}); - await conn.getRepository(FaceRegionEntry).save({box: face.box, person: person, media: savedPhoto}); - } - }; - const conn = await SQLConnection.getConnection(); - - const pr = conn.getRepository(PhotoEntity); - - await conn.getRepository(DirectoryEntity).save(p.directory); - await savePhoto(p); - await savePhoto(p2); - await savePhoto(p_faceLess); - - await conn.getRepository(VideoEntity).save(v);*/ - - // await SQLConnection.close(); }; @@ -151,53 +127,46 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => { return a.text.localeCompare(b.text); }; - expect((await sm.autocomplete('tat'))).to.deep.equalInAnyOrder([new AutoCompleteItem('Tatooine', SearchQueryTypes.position)]); - expect((await sm.autocomplete('star'))).to.deep.equalInAnyOrder([new AutoCompleteItem('star wars', SearchQueryTypes.keyword), + expect((await sm.autocomplete('tat', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ + new AutoCompleteItem('Tatooine', SearchQueryTypes.position)]); + expect((await sm.autocomplete('star', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ + new AutoCompleteItem('star wars', SearchQueryTypes.keyword), new AutoCompleteItem('death star', SearchQueryTypes.keyword)]); - expect((await sm.autocomplete('wars'))).to.deep.equalInAnyOrder([new AutoCompleteItem('star wars', SearchQueryTypes.keyword), + expect((await sm.autocomplete('wars', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ + new AutoCompleteItem('star wars', SearchQueryTypes.keyword), new AutoCompleteItem('wars dir', SearchQueryTypes.directory)]); - expect((await sm.autocomplete('arch'))).eql([new AutoCompleteItem('Research City', SearchQueryTypes.position)]); + expect((await sm.autocomplete('arch', SearchQueryTypes.any_text))).eql([ + new AutoCompleteItem('Research City', SearchQueryTypes.position)]); Config.Client.Search.AutoComplete.maxItemsPerCategory = 99999; - expect((await sm.autocomplete('a')).sort(cmp)).eql([ - new AutoCompleteItem('Boba Fett', SearchQueryTypes.keyword), - new AutoCompleteItem('Boba Fett', SearchQueryTypes.person), + expect((await sm.autocomplete('wa', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ new AutoCompleteItem('star wars', SearchQueryTypes.keyword), - new AutoCompleteItem('Anakin', SearchQueryTypes.keyword), new AutoCompleteItem('Anakin Skywalker', SearchQueryTypes.person), new AutoCompleteItem('Luke Skywalker', SearchQueryTypes.person), - new AutoCompleteItem('Han Solo', SearchQueryTypes.person), - new AutoCompleteItem('death star', SearchQueryTypes.keyword), - new AutoCompleteItem('Padmé Amidala', SearchQueryTypes.person), - new AutoCompleteItem('Obivan Kenobi', SearchQueryTypes.person), - new AutoCompleteItem('Arvíztűrő Tükörfúrógép', SearchQueryTypes.person), - new AutoCompleteItem('Padmé Amidala', SearchQueryTypes.keyword), - new AutoCompleteItem('Natalie Portman', SearchQueryTypes.keyword), - new AutoCompleteItem('Han Solo\'s dice', SearchQueryTypes.caption), - new AutoCompleteItem('Kamino', SearchQueryTypes.position), - new AutoCompleteItem('Tatooine', SearchQueryTypes.position), - new AutoCompleteItem('wars dir', SearchQueryTypes.directory), - new AutoCompleteItem('Research City', SearchQueryTypes.position)].sort(cmp)); + new AutoCompleteItem('wars dir', SearchQueryTypes.directory)]); Config.Client.Search.AutoComplete.maxItemsPerCategory = 1; - expect((await sm.autocomplete('a')).sort(cmp)).eql([ - new AutoCompleteItem('Anakin', SearchQueryTypes.keyword), + expect((await sm.autocomplete('a', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ + new AutoCompleteItem('Ajan Kloss', SearchQueryTypes.position), + new AutoCompleteItem('Amber stone', SearchQueryTypes.caption), new AutoCompleteItem('star wars', SearchQueryTypes.keyword), - new AutoCompleteItem('death star', SearchQueryTypes.keyword), new AutoCompleteItem('Anakin Skywalker', SearchQueryTypes.person), - new AutoCompleteItem('Han Solo\'s dice', SearchQueryTypes.caption), - new AutoCompleteItem('Kamino', SearchQueryTypes.position), - new AutoCompleteItem('Research City', SearchQueryTypes.position), - new AutoCompleteItem('wars dir', SearchQueryTypes.directory), - new AutoCompleteItem('Boba Fett', SearchQueryTypes.keyword)].sort(cmp)); + new AutoCompleteItem('Castilon', SearchQueryTypes.position), + new AutoCompleteItem('Devaron', SearchQueryTypes.position), + new AutoCompleteItem('The Phantom Menace', SearchQueryTypes.directory)]); Config.Client.Search.AutoComplete.maxItemsPerCategory = 5; - expect((await sm.autocomplete('sw')).sort(cmp)).to.deep.equalInAnyOrder([new AutoCompleteItem('sw1', SearchQueryTypes.file_name), - new AutoCompleteItem('sw2', SearchQueryTypes.file_name), new AutoCompleteItem(v.name, SearchQueryTypes.file_name)].sort(cmp)); + expect((await sm.autocomplete('sw', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ + new AutoCompleteItem('sw1.jpg', SearchQueryTypes.file_name), + new AutoCompleteItem('sw2.jpg', SearchQueryTypes.file_name), + new AutoCompleteItem('sw3.jpg', SearchQueryTypes.file_name), + new AutoCompleteItem('sw4.jpg', SearchQueryTypes.file_name), + new AutoCompleteItem(v.name, SearchQueryTypes.file_name)]); - expect((await sm.autocomplete(v.name)).sort(cmp)).to.deep.equalInAnyOrder([new AutoCompleteItem(v.name, SearchQueryTypes.file_name)]); + expect((await sm.autocomplete(v.name, SearchQueryTypes.any_text))).to.deep.equalInAnyOrder( + [new AutoCompleteItem(v.name, SearchQueryTypes.file_name)]); }); @@ -896,7 +865,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => { resultOverflow: false })); - query = {min: 0, max: 5, type: SearchQueryTypes.rating}; + query = {min: 0, max: 5, type: SearchQueryTypes.rating}; expect(Utils.clone(await sm.search(query))) .to.deep.equalInAnyOrder(removeDir({ searchQuery: query, @@ -1117,57 +1086,22 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => { }); - it('should instant search', async () => { + + it('should get random photo', async () => { const sm = new SearchManager(); - expect(Utils.clone(await sm.instantSearch('sw'))).to.deep.equalInAnyOrder(Utils.clone({ - searchText: 'sw', - directories: [], - media: [p, p2, v], - metaFile: [], - resultOverflow: false - })); + let query = { + text: 'xyz', + type: SearchQueryTypes.keyword + }; + expect(await sm.getRandomPhoto(query)).to.not.exist; - expect(Utils.clone(await sm.instantSearch('Tatooine'))).to.deep.equalInAnyOrder(Utils.clone({ - searchText: 'Tatooine', - directories: [], - media: [p], - metaFile: [], - resultOverflow: false - })); - - expect(Utils.clone(await sm.instantSearch('ortm'))).to.deep.equalInAnyOrder(Utils.clone({ - searchText: 'ortm', - directories: [], - media: [p2, p_faceLess], - metaFile: [], - resultOverflow: false - })); - - - expect(Utils.clone(await sm.instantSearch('wa'))).to.deep.equalInAnyOrder(Utils.clone({ - searchText: 'wa', - directories: [dir], - media: [p, p2, p_faceLess], - metaFile: [], - resultOverflow: false - })); - - expect(Utils.clone(await sm.instantSearch('han'))).to.deep.equalInAnyOrder(Utils.clone({ - searchText: 'han', - directories: [], - media: [p], - metaFile: [], - resultOverflow: false - })); - expect(Utils.clone(await sm.instantSearch('Boba'))).to.deep.equalInAnyOrder(Utils.clone({ - searchText: 'Boba', - directories: [], - media: [p], - metaFile: [], - resultOverflow: false - })); + query = { + text: 'wookiees', + matchType: TextSearchQueryTypes.exact_match, + type: SearchQueryTypes.keyword + }; + expect(Utils.clone(await sm.getRandomPhoto(query))).to.deep.equalInAnyOrder(searchifyMedia(p_faceLess)); }); - });