1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

fixing searching

This commit is contained in:
Patrik J. Braun 2019-01-26 18:03:40 -05:00
parent 1f58bffa2c
commit 0445c499e8
13 changed files with 445 additions and 111 deletions

View File

@ -67,7 +67,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
// on the fly reindexing
Logger.silly(LOG_TAG, 'lazy reindexing reason: cache timeout: lastScanned: '
+ (Date.now() - dir.lastScanned) + ', cachedFolderTimeout:' + Config.Server.indexing.cachedFolderTimeout);
+ (Date.now() - dir.lastScanned) + ' ms ago, cachedFolderTimeout:' + Config.Server.indexing.cachedFolderTimeout);
ObjectManagerRepository.getInstance().IndexingManager.indexDirectory(relativeDirectoryName).catch((err) => {
console.error(err);
});

View File

@ -44,7 +44,7 @@ export class SQLConnection {
VersionEntity
];
options.synchronize = false;
// options.logging = 'all';
//options.logging = 'all';
this.connection = await createConnection(options);
await SQLConnection.schemeSync(this.connection);
}

View File

@ -8,6 +8,7 @@ import {MediaEntity} from './enitites/MediaEntity';
import {VideoEntity} from './enitites/VideoEntity';
import {PersonEntry} from './enitites/PersonEntry';
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
import {SelectQueryBuilder} from 'typeorm';
export class SearchManager implements ISearchManager {
@ -24,7 +25,7 @@ export class SearchManager implements ISearchManager {
return a;
}
async autocomplete(text: string): Promise<Array<AutoCompleteItem>> {
async autocomplete(text: string): Promise<AutoCompleteItem[]> {
const connection = await SQLConnection.getConnection();
@ -122,61 +123,60 @@ export class SearchManager implements ISearchManager {
resultOverflow: false
};
let repository = connection.getRepository(MediaEntity);
const faceRepository = connection.getRepository(FaceRegionEntry);
let usedEntity = MediaEntity;
if (searchType === SearchTypes.photo) {
repository = connection.getRepository(PhotoEntity);
usedEntity = PhotoEntity;
} else if (searchType === SearchTypes.video) {
repository = connection.getRepository(VideoEntity);
usedEntity = VideoEntity;
}
const query = repository.createQueryBuilder('media')
const query = await connection.getRepository(usedEntity).createQueryBuilder('media')
.innerJoin(q => {
const subQuery = q.from(usedEntity, 'media')
.select('distinct media.id')
.limit(2000);
if (!searchType || searchType === SearchTypes.directory) {
subQuery.leftJoin('media.directory', 'directory')
.orWhere('directory.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.photo || searchType === SearchTypes.video) {
subQuery.orWhere('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.photo) {
subQuery.orWhere('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.person) {
subQuery
.leftJoin('media.metadata.faces', 'faces')
.leftJoin('faces.person', 'person')
.orWhere('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.position) {
subQuery.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 + '%'});
}
if (!searchType || searchType === SearchTypes.keyword) {
subQuery.orWhere('media.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
return subQuery;
},
'innerMedia',
'media.id=innerMedia.id')
.leftJoinAndSelect('media.directory', 'directory')
.leftJoin('media.metadata.faces', 'faces')
.leftJoin('faces.person', 'person')
.orderBy('media.metadata.creationDate', 'ASC');
.leftJoinAndSelect('media.metadata.faces', 'faces')
.leftJoinAndSelect('faces.person', 'person');
if (!searchType || searchType === SearchTypes.directory) {
query.orWhere('directory.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.photo || searchType === SearchTypes.video) {
query.orWhere('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.photo) {
query.orWhere('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.person) {
query.orWhere('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
if (!searchType || searchType === SearchTypes.position) {
query.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 + '%'});
}
if (!searchType || searchType === SearchTypes.keyword) {
query.orWhere('media.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'});
}
result.media = (await query
.limit(5000).getMany()).slice(0, 2001);
for (let i = 0; i < result.media.length; i++) {
const faces = (await faceRepository
.createQueryBuilder('faces')
.leftJoinAndSelect('faces.person', 'person')
.where('faces.media = :media', {media: result.media[i].id})
.getMany()).map(fE => ({name: fE.person.name, box: fE.box}));
if (faces.length > 0) {
result.media[i].metadata.faces = faces;
}
}
result.media = await this.loadMediaWithFaces(query);
if (result.media.length > 2000) {
result.resultOverflow = true;
@ -208,35 +208,30 @@ export class SearchManager implements ISearchManager {
resultOverflow: false
};
const faceRepository = connection.getRepository(FaceRegionEntry);
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');
result.media = await connection
.getRepository(MediaEntity)
.createQueryBuilder('media')
.orderBy('media.metadata.creationDate', 'ASC')
.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 + '%'})
.innerJoinAndSelect('media.directory', 'directory')
.leftJoin('media.metadata.faces', 'faces')
.leftJoin('faces.person', 'person')
.limit(10)
.getMany();
for (let i = 0; i < result.media.length; i++) {
const faces = (await faceRepository
.createQueryBuilder('faces')
.leftJoinAndSelect('faces.person', 'person')
.where('faces.media = :media', {media: result.media[i].id})
.getMany()).map(fE => ({name: fE.person.name, box: fE.box}));
if (faces.length > 0) {
result.media[i].metadata.faces = faces;
}
}
result.media = await this.loadMediaWithFaces(query);
result.directories = await connection
.getRepository(DirectoryEntity)
@ -255,4 +250,29 @@ export class SearchManager implements ISearchManager {
});
return res;
}
private async loadMediaWithFaces(query: SelectQueryBuilder<MediaEntity>) {
const rawAndEntities = await query.orderBy('media.id').getRawAndEntities();
const media: MediaEntity[] = rawAndEntities.entities;
// console.log(rawAndEntities.raw);
let rawIndex = 0;
for (let i = 0; i < media.length; i++) {
if (rawAndEntities.raw[rawIndex].faces_id === null ||
rawAndEntities.raw[rawIndex].media_id !== media[i].id) {
delete media[i].metadata.faces;
continue;
}
media[i].metadata.faces = [];
while (rawAndEntities.raw[rawIndex].media_id === media[i].id) {
media[i].metadata.faces.push(<any>FaceRegionEntry.fromRawToDTO(rawAndEntities.raw[rawIndex]));
rawIndex++;
if (rawIndex >= rawAndEntities.raw.length) {
return media;
}
}
}
return media;
}
}

View File

@ -1,7 +1,7 @@
import {FaceRegionBox} from '../../../../common/entities/PhotoDTO';
import {Column, ManyToOne, Entity, PrimaryGeneratedColumn} from 'typeorm';
import {FaceRegion, FaceRegionBox} from '../../../../common/entities/PhotoDTO';
import {Column, Entity, ManyToOne, PrimaryGeneratedColumn} from 'typeorm';
import {PersonEntry} from './PersonEntry';
import {MediaEntity, MediaMetadataEntity} from './MediaEntity';
import {MediaEntity} from './MediaEntity';
export class FaceRegionBoxEntry implements FaceRegionBox {
@Column('int')
@ -35,4 +35,21 @@ export class FaceRegionEntry {
person: PersonEntry;
name: string;
public static fromRawToDTO(raw: {
faces_id: number,
faces_mediaId: number,
faces_personId: number,
faces_boxHeight: number,
faces_boxWidth: number,
faces_boxX: number,
faces_boxY: number,
person_id: number,
person_name: string
}): FaceRegion {
return {
box: {width: raw.faces_boxWidth, height: raw.faces_boxHeight, x: raw.faces_boxX, y: raw.faces_boxY},
name: raw.person_name
};
}
}

137
benchmark/Benchmarks.ts Normal file
View File

@ -0,0 +1,137 @@
import {SQLConnection} from '../backend/model/sql/SQLConnection';
import {Config} from '../common/config/private/Config';
import {DatabaseType, ReIndexingSensitivity} from '../common/config/private/IPrivateConfig';
import {ObjectManagerRepository} from '../backend/model/ObjectManagerRepository';
import {DiskMangerWorker} from '../backend/model/threading/DiskMangerWorker';
import {IndexingManager} from '../backend/model/sql/IndexingManager';
import {SearchManager} from '../backend/model/sql/SearchManager';
import * as fs from 'fs';
import {SearchTypes} from '../common/entities/AutoCompleteItem';
import {Utils} from '../common/Utils';
import {GalleryManager} from '../backend/model/sql/GalleryManager';
import {DirectoryDTO} from '../common/entities/DirectoryDTO';
export interface BenchmarkResult {
duration: number;
directories?: number;
media?: number;
items?: number;
}
export class BMIndexingManager extends IndexingManager {
public async saveToDB(scannedDirectory: DirectoryDTO): Promise<void> {
return super.saveToDB(scannedDirectory);
}
}
export class Benchmarks {
constructor(public RUNS: number, public dbPath: string) {
}
async bmSaveDirectory(): Promise<BenchmarkResult> {
await this.resetDB();
const dir = await DiskMangerWorker.scanDirectory('./');
const im = new BMIndexingManager();
return await this.benchmark(() => im.saveToDB(dir), () => this.resetDB());
}
async bmScanDirectory(): Promise<BenchmarkResult> {
return await this.benchmark(() => DiskMangerWorker.scanDirectory('./'));
}
async bmListDirectory(): Promise<BenchmarkResult> {
const gm = new GalleryManager();
await this.setupDB();
Config.Server.indexing.reIndexingSensitivity = ReIndexingSensitivity.low;
return await this.benchmark(() => gm.listDirectory('./'));
}
async bmAllSearch(text: string): Promise<{ result: BenchmarkResult, searchType: SearchTypes }[]> {
await this.setupDB();
const types = Utils.enumToArray(SearchTypes).map(a => a.key).concat([null]);
const results: { result: BenchmarkResult, searchType: SearchTypes }[] = [];
const sm = new SearchManager();
for (let i = 0; i < types.length; i++) {
results.push({result: await this.benchmark(() => sm.search(text, types[i])), searchType: types[i]});
}
return results;
}
async bmInstantSearch(text: string): Promise<BenchmarkResult> {
await this.setupDB();
const sm = new SearchManager();
return await this.benchmark(() => sm.instantSearch(text));
}
async bmAutocomplete(text: string): Promise<BenchmarkResult> {
await this.setupDB();
const sm = new SearchManager();
return await this.benchmark(() => sm.autocomplete(text));
}
private async benchmark(fn: () => Promise<{ media: any[], directories: any[] } | any[] | void>,
beforeEach: () => Promise<any> = null,
afterEach: () => Promise<any> = null) {
const scanned = await fn();
const start = process.hrtime();
let skip = 0;
for (let i = 0; i < this.RUNS; i++) {
if (beforeEach) {
const startSkip = process.hrtime();
await beforeEach();
const endSkip = process.hrtime(startSkip);
skip += (endSkip[0] * 1000 + endSkip[1] / 1000);
}
await fn();
if (afterEach) {
const startSkip = process.hrtime();
await afterEach();
const endSkip = process.hrtime(startSkip);
skip += (endSkip[0] * 1000 + endSkip[1] / 1000);
}
}
const end = process.hrtime(start);
const duration = (end[0] * 1000 + end[1] / 1000) / this.RUNS;
if (!scanned) {
return {
duration: duration
};
}
if (Array.isArray(scanned)) {
return {
duration: duration,
items: scanned.length
};
}
return {
duration: duration,
media: scanned.media.length,
directories: scanned.directories.length
};
}
private resetDB = async () => {
await SQLConnection.close();
if (fs.existsSync(this.dbPath)) {
fs.unlinkSync(this.dbPath);
}
Config.Server.database.type = DatabaseType.sqlite;
Config.Server.database.sqlite.storage = this.dbPath;
await ObjectManagerRepository.InitSQLManagers();
};
private async setupDB() {
const im = new BMIndexingManager();
await this.resetDB();
const dir = await DiskMangerWorker.scanDirectory('./');
await im.saveToDB(dir);
}
}

5
benchmark/README.md Normal file
View File

@ -0,0 +1,5 @@
# PiGallery2 performance benchmark results
These results are created mostly for development, but I'm making them public for curious users.

70
benchmark/index.ts Normal file
View File

@ -0,0 +1,70 @@
import {Config} from '../common/config/private/Config';
import * as path from 'path';
import {ProjectPath} from '../backend/ProjectPath';
import {BenchmarkResult, Benchmarks} from './Benchmarks';
import {SearchTypes} from '../common/entities/AutoCompleteItem';
import {Utils} from '../common/Utils';
import {DiskMangerWorker} from '../backend/model/threading/DiskMangerWorker';
const config: { path: string, system: string } = require(path.join(__dirname, 'config.json'));
Config.Server.imagesFolder = config.path;
const dbPath = path.join(__dirname, 'test.db');
ProjectPath.reset();
const RUNS = 1;
let resultsText = '';
const printLine = (text: string) => {
resultsText += text + '\n';
};
const printHeader = async () => {
const dt = new Date();
printLine('## PiGallery2 v' + require('./../package.json').version +
', ' + Utils.zeroPrefix(dt.getDay(), 2) +
'.' + Utils.zeroPrefix(dt.getMonth() + 1, 2) +
'.' + dt.getFullYear());
printLine('**System**: ' + config.system);
const dir = await DiskMangerWorker.scanDirectory('./');
printLine('**Gallery**: directories:' + dir.directories.length + ' media:' + dir.media.length);
};
const printTableHeader = () => {
printLine('| action | action details | average time | details |');
printLine('|:------:|:--------------:|:------------:|:-------:|');
};
const printResult = (result: BenchmarkResult, action: string, actionDetails: string = '') => {
let details = '-';
if (result.items) {
details = 'items: ' + result.items;
}
if (result.media) {
details = 'media: ' + result.media + ', directories:' + result.directories;
}
printLine('| ' + action + ' | ' + actionDetails +
' | ' + (result.duration / 1000).toFixed(2) + 's | ' + details + ' |');
};
const run = async () => {
const bm = new Benchmarks(RUNS, dbPath);
// header
await printHeader();
printTableHeader();
printResult(await bm.bmScanDirectory(), 'Scanning directory');
printResult(await bm.bmSaveDirectory(), 'Saving directory');
printResult(await bm.bmListDirectory(), 'Listing Directory');
(await bm.bmAllSearch('a')).forEach(res => {
if (res.searchType !== null) {
printResult(res.result, 'searching', '`a` as `' + SearchTypes[res.searchType] + '`');
} else {
printResult(res.result, 'searching', '`a` as `any`');
}
});
printResult(await bm.bmInstantSearch('a'), 'instant search', '`a`');
printResult(await bm.bmAutocomplete('a'), 'auto complete', '`a`');
console.log(resultsText);
};
run();

View File

@ -23,4 +23,8 @@ export enum ErrorCodes {
export class ErrorDTO {
constructor(public code: ErrorCodes, public message?: string, public details?: any) {
}
toString(): string {
return '[' + ErrorCodes[this.code] + '] ' + this.message + (this.details ? this.details.toString() : '');
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -13,15 +13,24 @@
"faces": [
{
"box": {
"height": 19,
"width": 20,
"x": 82,
"y": 38
"height": 2,
"width": 2,
"x": 8,
"y": 4
},
"name": "squirrel"
},
{
"box": {
"height": 3,
"width": 2,
"x": 5,
"y": 5
},
"name": "special_chars űáéúőóüío?._:"
}
],
"fileSize": 59187,
"fileSize": 39424,
"keywords": [
"Berkley",
"USA",
@ -39,7 +48,7 @@
"state": "test state őúéáűóöí-.,)("
},
"size": {
"height": 93,
"width": 140
"height": 10,
"width": 14
}
}

View File

@ -4,13 +4,7 @@ import * as path from 'path';
import {Config} from '../../../../../common/config/private/Config';
import {DatabaseType} from '../../../../../common/config/private/IPrivateConfig';
import {SQLConnection} from '../../../../../backend/model/sql/SQLConnection';
import {
CameraMetadataEntity,
GPSMetadataEntity,
PhotoEntity,
PhotoMetadataEntity,
PositionMetaDataEntity
} from '../../../../../backend/model/sql/enitites/PhotoEntity';
import {PhotoEntity} from '../../../../../backend/model/sql/enitites/PhotoEntity';
import {SearchManager} from '../../../../../backend/model/sql/SearchManager';
import {AutoCompleteItem, SearchTypes} from '../../../../../common/entities/AutoCompleteItem';
import {SearchResultDTO} from '../../../../../common/entities/SearchResultDTO';
@ -18,6 +12,9 @@ import {DirectoryEntity} from '../../../../../backend/model/sql/enitites/Directo
import {Utils} from '../../../../../common/Utils';
import {TestHelper} from './TestHelper';
import {VideoEntity} from '../../../../../backend/model/sql/enitites/VideoEntity';
import {PersonEntry} from '../../../../../backend/model/sql/enitites/PersonEntry';
import {FaceRegionEntry} from '../../../../../backend/model/sql/enitites/FaceRegionEntry';
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
describe('SearchManager', () => {
@ -28,6 +25,9 @@ describe('SearchManager', () => {
const dir = TestHelper.getDirectoryEntry();
const p = TestHelper.getPhotoEntry1(dir);
const p2 = TestHelper.getPhotoEntry2(dir);
const p_faceLess = TestHelper.getPhotoEntry2(dir);
delete p_faceLess.metadata.faces;
p_faceLess.name = 'fl';
const v = TestHelper.getVideoEntry1(dir);
const setUpSqlDB = async () => {
@ -41,13 +41,26 @@ describe('SearchManager', () => {
Config.Server.database.type = DatabaseType.sqlite;
Config.Server.database.sqlite.storage = dbPath;
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 pr.save(p);
await pr.save(p2);
await savePhoto(p);
await savePhoto(p2);
await savePhoto(p_faceLess);
await conn.getRepository(VideoEntity).save(v);
await SQLConnection.close();
@ -76,6 +89,9 @@ describe('SearchManager', () => {
const sm = new SearchManager();
const cmp = (a: AutoCompleteItem, b: AutoCompleteItem) => {
if (a.text === b.text) {
return a.type - b.type;
}
return a.text.localeCompare(b.text);
};
@ -89,9 +105,14 @@ describe('SearchManager', () => {
expect((await sm.autocomplete('arch'))).eql([new AutoCompleteItem('Research City', SearchTypes.position)]);
expect((await sm.autocomplete('a')).sort(cmp)).eql([
new AutoCompleteItem('Boba Fett', SearchTypes.keyword),
new AutoCompleteItem('Boba Fett', SearchTypes.person),
new AutoCompleteItem('star wars', SearchTypes.keyword),
new AutoCompleteItem('Anakin', SearchTypes.keyword),
new AutoCompleteItem('Anakin Skywalker', SearchTypes.person),
new AutoCompleteItem('Luke Skywalker', SearchTypes.person),
new AutoCompleteItem('Han Solo', SearchTypes.person),
new AutoCompleteItem('death star', SearchTypes.keyword),
new AutoCompleteItem('Padmé Amidala', SearchTypes.person),
new AutoCompleteItem('Padmé Amidala', SearchTypes.keyword),
new AutoCompleteItem('Natalie Portman', SearchTypes.keyword),
new AutoCompleteItem('Han Solo\'s dice', SearchTypes.photo),
@ -120,6 +141,15 @@ describe('SearchManager', () => {
resultOverflow: false
}));
expect(Utils.clone(await sm.search('Boba', null))).to.deep.equal(Utils.clone(<SearchResultDTO>{
searchText: 'Boba',
searchType: null,
directories: [],
media: [p],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.search('Tatooine', SearchTypes.position))).to.deep.equal(Utils.clone(<SearchResultDTO>{
searchText: 'Tatooine',
searchType: SearchTypes.position,
@ -133,7 +163,7 @@ describe('SearchManager', () => {
searchText: 'ortm',
searchType: SearchTypes.keyword,
directories: [],
media: [p2],
media: [p2, p_faceLess],
metaFile: [],
resultOverflow: false
}));
@ -142,7 +172,7 @@ describe('SearchManager', () => {
searchText: 'ortm',
searchType: SearchTypes.keyword,
directories: [],
media: [p2],
media: [p2, p_faceLess],
metaFile: [],
resultOverflow: false
}));
@ -151,7 +181,7 @@ describe('SearchManager', () => {
searchText: 'wa',
searchType: SearchTypes.keyword,
directories: [dir],
media: [p, p2],
media: [p, p2, p_faceLess],
metaFile: [],
resultOverflow: false
}));
@ -165,6 +195,15 @@ describe('SearchManager', () => {
resultOverflow: false
}));
expect(Utils.clone(await sm.search('sw', SearchTypes.video))).to.deep.equal(Utils.clone(<SearchResultDTO>{
searchText: 'sw',
searchType: SearchTypes.video,
directories: [],
media: [v],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.search('han', SearchTypes.keyword))).to.deep.equal(Utils.clone(<SearchResultDTO>{
searchText: 'han',
searchType: SearchTypes.keyword,
@ -173,6 +212,15 @@ describe('SearchManager', () => {
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.search('Boba', SearchTypes.person))).to.deep.equal(Utils.clone(<SearchResultDTO>{
searchText: 'Boba',
searchType: SearchTypes.person,
directories: [],
media: [p],
metaFile: [],
resultOverflow: false
}));
});
@ -198,23 +246,16 @@ describe('SearchManager', () => {
expect(Utils.clone(await sm.instantSearch('ortm'))).to.deep.equal(Utils.clone({
searchText: 'ortm',
directories: [],
media: [p2],
media: [p2, p_faceLess],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.instantSearch('ortm'))).to.deep.equal(Utils.clone({
searchText: 'ortm',
directories: [],
media: [p2],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.instantSearch('wa'))).to.deep.equal(Utils.clone({
searchText: 'wa',
directories: [dir],
media: [p, p2],
media: [p, p2, p_faceLess],
metaFile: [],
resultOverflow: false
}));
@ -226,6 +267,13 @@ describe('SearchManager', () => {
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.instantSearch('Boba'))).to.deep.equal(Utils.clone({
searchText: 'Boba',
directories: [],
media: [p],
metaFile: [],
resultOverflow: false
}));
});

View File

@ -104,6 +104,20 @@ export class TestHelper {
p.metadata.positionData.city = 'Mos Eisley';
p.metadata.positionData.country = 'Tatooine';
p.name = 'sw1';
p.metadata.faces = [<any>{
box: {height: 10, width: 10, x: 10, y: 10},
name: 'Boba Fett'
}, <any>{
box: {height: 10, width: 10, x: 101, y: 101},
name: 'Luke Skywalker'
}, <any>{
box: {height: 10, width: 10, x: 101, y: 101},
name: 'Han Solo'
}, <any>{
box: {height: 10, width: 10, x: 101, y: 101},
name: 'Unkle Ben'
}];
return p;
}
@ -121,6 +135,16 @@ export class TestHelper {
p.metadata.positionData.state = 'Research City';
p.metadata.positionData.country = 'Kamino';
p.name = 'sw2';
p.metadata.faces = [<any>{
box: {height: 10, width: 10, x: 10, y: 10},
name: 'Padmé Amidala'
}, <any>{
box: {height: 10, width: 10, x: 101, y: 101},
name: 'Anakin Skywalker'
}, <any>{
box: {height: 10, width: 10, x: 101, y: 101},
name: 'Obivan Kenobi'
}];
return p;
}

View File

@ -11,10 +11,10 @@ describe('DiskMangerWorker', () => {
Config.Server.imagesFolder = path.join(__dirname, '/../../assets');
ProjectPath.ImageFolder = path.join(__dirname, '/../../assets');
const dir = await DiskMangerWorker.scanDirectory('/');
expect(dir.media.length).to.be.equals(2);
expect(dir.media.length).to.be.equals(3);
const expected = require(path.join(__dirname, '/../../assets/test image öüóőúéáű-.,.json'));
expect(Utils.clone(dir.media[0].name)).to.be.deep.equal('test image öüóőúéáű-.,.jpg');
expect(Utils.clone(dir.media[0].metadata)).to.be.deep.equal(expected);
expect(Utils.clone(dir.media[1].name)).to.be.deep.equal('test image öüóőúéáű-.,.jpg');
expect(Utils.clone(dir.media[1].metadata)).to.be.deep.equal(expected);
});
});