mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-04 03:49:28 +02:00
refactoring code, improving advanced search
This commit is contained in:
parent
e643ee2ed1
commit
a8bbefe0f8
@ -4,11 +4,7 @@ import {GPSMetadata} from '../../../common/entities/PhotoDTO';
|
||||
export class LocationManager {
|
||||
|
||||
async getGPSData(text: string): Promise<GPSMetadata> {
|
||||
return {
|
||||
longitude: 0,
|
||||
latitude: 0,
|
||||
altitude: 0
|
||||
};
|
||||
throw new Error('TODO implement');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
import {AutoCompleteItem, SearchTypes} from '../../../../common/entities/AutoCompleteItem';
|
||||
import {AutoCompleteItem} from '../../../../common/entities/AutoCompleteItem';
|
||||
import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO';
|
||||
import {SearchQueryDTO, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO';
|
||||
|
||||
export interface ISearchManager {
|
||||
autocomplete(text: string): Promise<AutoCompleteItem[]>;
|
||||
|
||||
search(text: string, searchType: SearchTypes): Promise<SearchResultDTO>;
|
||||
search(query: SearchQueryDTO): Promise<SearchResultDTO>;
|
||||
|
||||
instantSearch(text: string): Promise<SearchResultDTO>;
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import {AutoCompleteItem, SearchTypes} from '../../../../common/entities/AutoCompleteItem';
|
||||
import {AutoCompleteItem} from '../../../../common/entities/AutoCompleteItem';
|
||||
import {ISearchManager} from '../interfaces/ISearchManager';
|
||||
import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO';
|
||||
import {SearchQueryDTO, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO';
|
||||
|
||||
export class SearchManager implements ISearchManager {
|
||||
autocomplete(text: string): Promise<AutoCompleteItem[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
search(text: string, searchType: SearchTypes): Promise<SearchResultDTO> {
|
||||
search(query: SearchQueryDTO): Promise<SearchResultDTO> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
import {DirectoryEntity} from './enitites/DirectoryEntity';
|
||||
import {SQLConnection} from './SQLConnection';
|
||||
import {DiskManager} from '../../DiskManger';
|
||||
import {PhotoEntity} from './enitites/PhotoEntity';
|
||||
import {PhotoEntity, PhotoMetadataEntity} from './enitites/PhotoEntity';
|
||||
import {Utils} from '../../../../common/Utils';
|
||||
import {FaceRegion, PhotoMetadata} from '../../../../common/entities/PhotoDTO';
|
||||
import {Connection, Repository} from 'typeorm';
|
||||
@ -206,10 +206,10 @@ export class IndexingManager implements IIndexingManager {
|
||||
.getMany());
|
||||
|
||||
const mediaChange: any = {
|
||||
saveP: [],
|
||||
saveV: [],
|
||||
insertP: [],
|
||||
insertV: []
|
||||
saveP: [], // save/update photo
|
||||
saveV: [], // save/update video
|
||||
insertP: [], // insert photo
|
||||
insertV: [] // insert video
|
||||
};
|
||||
const facesPerPhoto: { faces: FaceRegionEntry[], mediaName: string }[] = [];
|
||||
for (let i = 0; i < media.length; i++) {
|
||||
@ -223,14 +223,17 @@ export class IndexingManager implements IIndexingManager {
|
||||
}
|
||||
|
||||
const scannedFaces = (<PhotoMetadata>media[i].metadata).faces || [];
|
||||
delete (<PhotoMetadata>media[i].metadata).faces;
|
||||
if ((<PhotoMetadata>media[i].metadata).faces) { // if it has faces, cache them
|
||||
(<PhotoMetadataEntity>media[i].metadata).persons = (<PhotoMetadata>media[i].metadata).faces.map(f => f.name);
|
||||
}
|
||||
delete (<PhotoMetadata>media[i].metadata).faces; // this is a separated DB, lets save separately
|
||||
|
||||
if (mediaItem == null) { // not in DB yet
|
||||
media[i].directory = null;
|
||||
mediaItem = <any>Utils.clone(media[i]);
|
||||
mediaItem.directory = <any>{id: parentDirId};
|
||||
(MediaDTO.isPhoto(mediaItem) ? mediaChange.insertP : mediaChange.insertV).push(mediaItem);
|
||||
} else {
|
||||
} else { // already in the DB, only needs to be updated
|
||||
delete (<PhotoMetadata>mediaItem.metadata).faces;
|
||||
if (!Utils.equalsFilter(mediaItem.metadata, media[i].metadata)) {
|
||||
mediaItem.metadata = <any>media[i].metadata;
|
||||
|
@ -1,11 +1,10 @@
|
||||
import {AutoCompleteItem, SearchTypes} from '../../../../common/entities/AutoCompleteItem';
|
||||
import {AutoCompleteItem} from '../../../../common/entities/AutoCompleteItem';
|
||||
import {ISearchManager} from '../interfaces/ISearchManager';
|
||||
import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO';
|
||||
import {SQLConnection} from './SQLConnection';
|
||||
import {PhotoEntity} from './enitites/PhotoEntity';
|
||||
import {DirectoryEntity} from './enitites/DirectoryEntity';
|
||||
import {MediaEntity} from './enitites/MediaEntity';
|
||||
import {VideoEntity} from './enitites/VideoEntity';
|
||||
import {PersonEntry} from './enitites/PersonEntry';
|
||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||
import {Brackets, SelectQueryBuilder, WhereExpression} from 'typeorm';
|
||||
@ -50,7 +49,7 @@ export class SearchManager implements ISearchManager {
|
||||
|
||||
let result: AutoCompleteItem[] = [];
|
||||
const photoRepository = connection.getRepository(PhotoEntity);
|
||||
const videoRepository = connection.getRepository(VideoEntity);
|
||||
const mediaRepository = connection.getRepository(MediaEntity);
|
||||
const personRepository = connection.getRepository(PersonEntry);
|
||||
const directoryRepository = connection.getRepository(DirectoryEntity);
|
||||
|
||||
@ -64,7 +63,7 @@ export class SearchManager implements ISearchManager {
|
||||
.map(r => <Array<string>>(<string>r.metadataKeywords).split(','))
|
||||
.forEach(keywords => {
|
||||
result = result.concat(this.encapsulateAutoComplete(keywords
|
||||
.filter(k => k.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchTypes.keyword));
|
||||
.filter(k => k.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.keyword));
|
||||
});
|
||||
|
||||
result = result.concat(this.encapsulateAutoComplete((await personRepository
|
||||
@ -74,7 +73,7 @@ export class SearchManager implements ISearchManager {
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.orderBy('person.name')
|
||||
.getRawMany())
|
||||
.map(r => r.name), SearchTypes.person));
|
||||
.map(r => r.name), SearchQueryTypes.person));
|
||||
|
||||
(await photoRepository
|
||||
.createQueryBuilder('photo')
|
||||
@ -90,16 +89,16 @@ export class SearchManager implements ISearchManager {
|
||||
.map(pm => <Array<string>>[pm.city || '', pm.country || '', pm.state || ''])
|
||||
.forEach(positions => {
|
||||
result = result.concat(this.encapsulateAutoComplete(positions
|
||||
.filter(p => p.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchTypes.position));
|
||||
.filter(p => p.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.position));
|
||||
});
|
||||
|
||||
result = result.concat(this.encapsulateAutoComplete((await photoRepository
|
||||
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), SearchTypes.photo));
|
||||
.map(r => r.name), SearchQueryTypes.file_name));
|
||||
|
||||
|
||||
result = result.concat(this.encapsulateAutoComplete((await photoRepository
|
||||
@ -108,24 +107,16 @@ export class SearchManager implements ISearchManager {
|
||||
.where('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.map(r => r.caption), SearchTypes.photo));
|
||||
.map(r => r.caption), SearchQueryTypes.caption));
|
||||
|
||||
|
||||
result = result.concat(this.encapsulateAutoComplete((await videoRepository
|
||||
.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), SearchTypes.video));
|
||||
|
||||
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), SearchTypes.directory));
|
||||
.map(r => r.name), SearchQueryTypes.directory));
|
||||
|
||||
|
||||
return SearchManager.autoCompleteItemsUnique(result);
|
||||
@ -145,15 +136,13 @@ export class SearchManager implements ISearchManager {
|
||||
return query;
|
||||
}
|
||||
|
||||
async aSearch(query: SearchQueryDTO) {
|
||||
query = this.flattenSameOfQueries(query);
|
||||
console.log(JSON.stringify(query, null, 4));
|
||||
async search(queryIN: SearchQueryDTO) {
|
||||
let query = this.flattenSameOfQueries(queryIN);
|
||||
query = await this.getGPSData(query);
|
||||
const connection = await SQLConnection.getConnection();
|
||||
|
||||
const result: SearchResultDTO = {
|
||||
searchText: null,
|
||||
searchType: null,
|
||||
searchQuery: queryIN,
|
||||
directories: [],
|
||||
media: [],
|
||||
metaFile: [],
|
||||
@ -168,8 +157,6 @@ export class SearchManager implements ISearchManager {
|
||||
.limit(Config.Client.Search.maxMediaResult + 1);
|
||||
|
||||
subQuery.leftJoin('media.directory', 'directory')
|
||||
.leftJoin('media.metadata.faces', 'faces')
|
||||
.leftJoin('faces.person', 'person')
|
||||
.where(this.buildWhereQuery(query));
|
||||
|
||||
return subQuery;
|
||||
@ -201,96 +188,11 @@ export class SearchManager implements ISearchManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
async search(text: string, searchType: SearchTypes): Promise<SearchResultDTO> {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
|
||||
const result: SearchResultDTO = {
|
||||
searchText: text,
|
||||
searchType: searchType,
|
||||
directories: [],
|
||||
media: [],
|
||||
metaFile: [],
|
||||
resultOverflow: false
|
||||
};
|
||||
|
||||
let usedEntity = MediaEntity;
|
||||
|
||||
if (searchType === SearchTypes.photo) {
|
||||
usedEntity = PhotoEntity;
|
||||
} else if (searchType === SearchTypes.video) {
|
||||
usedEntity = VideoEntity;
|
||||
}
|
||||
|
||||
const query = await connection.getRepository(usedEntity).createQueryBuilder('media')
|
||||
.innerJoin(q => {
|
||||
const subQuery = q.from(usedEntity, 'media')
|
||||
.select('distinct media.id')
|
||||
.limit(Config.Client.Search.maxMediaResult + 1);
|
||||
|
||||
|
||||
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')
|
||||
.leftJoinAndSelect('media.metadata.faces', 'faces')
|
||||
.leftJoinAndSelect('faces.person', 'person');
|
||||
|
||||
|
||||
result.media = await this.loadMediaWithFaces(query);
|
||||
|
||||
if (result.media.length > Config.Client.Search.maxMediaResult) {
|
||||
result.resultOverflow = true;
|
||||
}
|
||||
|
||||
result.directories = await connection
|
||||
.getRepository(DirectoryEntity)
|
||||
.createQueryBuilder('dir')
|
||||
.where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(Config.Client.Search.maxMediaResult + 1)
|
||||
.getMany();
|
||||
|
||||
if (result.directories.length > Config.Client.Search.maxDirectoryResult) {
|
||||
result.resultOverflow = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async instantSearch(text: string): Promise<SearchResultDTO> {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
|
||||
const result: SearchResultDTO = {
|
||||
searchText: text,
|
||||
searchQuery: <TextSearch>{type: SearchQueryTypes.any_text, text: text},
|
||||
// searchType:undefined, not adding this
|
||||
directories: [],
|
||||
media: [],
|
||||
@ -521,20 +423,6 @@ export class SearchManager implements ISearchManager {
|
||||
q[whereFN](`media.metadata.caption ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
}
|
||||
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.person) {
|
||||
if (!(<TextSearch>query).negate) {
|
||||
q[whereFN](`person.name ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
} else {
|
||||
// because of the Left JOIN on the faces, we also need to check for NULL
|
||||
q[whereFN](new Brackets(dq => {
|
||||
dq.where(`person.name ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
dq.orWhere(`person.name is NULL`);
|
||||
return dq;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.position) {
|
||||
q[whereFN](`media.metadata.positionData.country ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
@ -544,9 +432,40 @@ export class SearchManager implements ISearchManager {
|
||||
[whereFN](`media.metadata.positionData.city ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
}
|
||||
|
||||
// Matching for array type fields
|
||||
const matchArrayField = (fieldName: string) => {
|
||||
q[whereFN](new Brackets(qbr => {
|
||||
if ((<TextSearch>query).matchType !== TextSearchQueryTypes.exact_match) {
|
||||
qbr[whereFN](`${fieldName} ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
} else {
|
||||
qbr[whereFN](new Brackets(qb => {
|
||||
textParam['CtextC' + paramCounter.value] = `%,${(<TextSearch>query).text},%`;
|
||||
textParam['Ctext' + paramCounter.value] = `%,${(<TextSearch>query).text}`;
|
||||
textParam['textC' + paramCounter.value] = `${(<TextSearch>query).text},%`;
|
||||
|
||||
qb[whereFN](`${fieldName} ${LIKE} :CtextC${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
qb[whereFN](`${fieldName} ${LIKE} :Ctext${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
qb[whereFN](`${fieldName} ${LIKE} :textC${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
}));
|
||||
}
|
||||
if ((<TextSearch>query).negate) {
|
||||
qbr.orWhere(`${fieldName} IS NULL`);
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.person) {
|
||||
matchArrayField('media.metadata.persons');
|
||||
}
|
||||
|
||||
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.keyword) {
|
||||
q[whereFN](`media.metadata.keywords ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
||||
textParam);
|
||||
matchArrayField('media.metadata.keywords');
|
||||
}
|
||||
return q;
|
||||
});
|
||||
@ -591,7 +510,7 @@ export class SearchManager implements ISearchManager {
|
||||
return query;
|
||||
}
|
||||
|
||||
private encapsulateAutoComplete(values: string[], type: SearchTypes): Array<AutoCompleteItem> {
|
||||
private encapsulateAutoComplete(values: string[], type: SearchQueryTypes): Array<AutoCompleteItem> {
|
||||
const res: AutoCompleteItem[] = [];
|
||||
values.forEach((value) => {
|
||||
res.push(new AutoCompleteItem(value, type));
|
||||
@ -617,14 +536,6 @@ export class SearchManager implements ISearchManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
if (rawAndEntities.raw[rawIndex].faces_id === null ||
|
||||
rawAndEntities.raw[rawIndex].media_id !== media[i].id) {
|
||||
delete media[i].metadata.faces;
|
||||
continue;
|
||||
}*/
|
||||
|
||||
// process all faces for one media
|
||||
media[i].metadata.faces = [];
|
||||
|
||||
|
@ -52,6 +52,9 @@ export class MediaMetadataEntity implements MediaMetadata {
|
||||
@OneToMany(type => FaceRegionEntry, faceRegion => faceRegion.media)
|
||||
faces: FaceRegionEntry[];
|
||||
|
||||
@Column('simple-array', {select: false, nullable: true})
|
||||
persons: string[]; // Caches the list of persons. Only used for searching
|
||||
|
||||
@Column('int', {unsigned: true})
|
||||
bitRate: number;
|
||||
|
||||
|
@ -67,7 +67,7 @@ export class VideoRendererFactory {
|
||||
if (!!err || data === null) {
|
||||
return reject('[FFmpeg] ' + err.toString());
|
||||
}
|
||||
/// console.log(data);
|
||||
|
||||
let width = null;
|
||||
let height = null;
|
||||
for (let i = 0; i < data.streams.length; i++) {
|
||||
|
@ -1 +1 @@
|
||||
export const DataStructureVersion = 17;
|
||||
export const DataStructureVersion = 18;
|
||||
|
@ -1,14 +1,7 @@
|
||||
export enum SearchTypes {
|
||||
directory = 1,
|
||||
person = 2,
|
||||
keyword = 3,
|
||||
position = 5,
|
||||
photo = 6,
|
||||
video = 7
|
||||
}
|
||||
import {SearchQueryTypes} from './SearchQueryDTO';
|
||||
|
||||
export class AutoCompleteItem {
|
||||
constructor(public text: string, public type: SearchTypes) {
|
||||
constructor(public text: string, public type: SearchQueryTypes) {
|
||||
}
|
||||
|
||||
equals(other: AutoCompleteItem) {
|
||||
|
@ -1,11 +1,10 @@
|
||||
import {DirectoryDTO} from './DirectoryDTO';
|
||||
import {SearchTypes} from './AutoCompleteItem';
|
||||
import {FileDTO} from './FileDTO';
|
||||
import {MediaDTO} from './MediaDTO';
|
||||
import {SearchQueryDTO} from './SearchQueryDTO';
|
||||
|
||||
export interface SearchResultDTO {
|
||||
searchText: string;
|
||||
searchType?: SearchTypes;
|
||||
searchQuery: SearchQueryDTO;
|
||||
directories: DirectoryDTO[];
|
||||
media: MediaDTO[];
|
||||
metaFile: FileDTO[];
|
||||
|
@ -61,7 +61,6 @@ export class BasicSettingsComponent extends SettingsComponent<BasicConfigDTO> {
|
||||
}
|
||||
|
||||
onUrlChanged() {
|
||||
console.log('called');
|
||||
if (this.urlBaseChanged === false) {
|
||||
this.states.urlBase.value = this.calcBaseUrl();
|
||||
} else {
|
||||
|
@ -4,10 +4,34 @@ import * as fs from 'fs';
|
||||
import {SQLConnection} from '../../src/backend/model/database/sql/SQLConnection';
|
||||
import {ServerConfig} from '../../src/common/config/private/PrivateConfig';
|
||||
import {ProjectPath} from '../../src/backend/ProjectPath';
|
||||
import {DirectoryDTO} from '../../src/common/entities/DirectoryDTO';
|
||||
import {DirectoryEntity} from '../../src/backend/model/database/sql/enitites/DirectoryEntity';
|
||||
import {ObjectManagers} from '../../src/backend/model/ObjectManagers';
|
||||
import {DiskMangerWorker} from '../../src/backend/model/threading/DiskMangerWorker';
|
||||
import {IndexingManager} from '../../src/backend/model/database/sql/IndexingManager';
|
||||
import {GalleryManager} from '../../src/backend/model/database/sql/GalleryManager';
|
||||
import {Connection} from 'typeorm';
|
||||
|
||||
declare let describe: any;
|
||||
const savedDescribe = describe;
|
||||
|
||||
class IndexingManagerTest extends IndexingManager {
|
||||
|
||||
public async saveToDB(scannedDirectory: DirectoryDTO): Promise<void> {
|
||||
return super.saveToDB(scannedDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
public async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<DirectoryEntity> {
|
||||
return super.selectParentDir(connection, directoryName, directoryParent);
|
||||
}
|
||||
|
||||
public async fillParentDir(connection: Connection, dir: DirectoryEntity): Promise<void> {
|
||||
return super.fillParentDir(connection, dir);
|
||||
}
|
||||
}
|
||||
|
||||
export class SQLTestHelper {
|
||||
|
||||
@ -41,6 +65,40 @@ export class SQLTestHelper {
|
||||
});
|
||||
}
|
||||
|
||||
public static async persistTestDir(directory: DirectoryDTO): Promise<DirectoryEntity> {
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
const connection = await SQLConnection.getConnection();
|
||||
ObjectManagers.getInstance().IndexingManager.indexDirectory = () => Promise.resolve(null);
|
||||
|
||||
|
||||
const im = new IndexingManagerTest();
|
||||
await im.saveToDB(directory);
|
||||
// not saving subdirs. saveToDB destroys data
|
||||
// await im.saveToDB(subDir);
|
||||
// await im.saveToDB(subDir2);
|
||||
|
||||
if (ObjectManagers.getInstance().IndexingManager &&
|
||||
ObjectManagers.getInstance().IndexingManager.IsSavingInProgress) {
|
||||
await ObjectManagers.getInstance().IndexingManager.SavingReady;
|
||||
}
|
||||
|
||||
const gm = new GalleryManagerTest();
|
||||
const dir = await gm.selectParentDir(connection, directory.name, path.join(path.dirname('.'), path.sep));
|
||||
await gm.fillParentDir(connection, dir);
|
||||
|
||||
const populateDir = async (d: DirectoryDTO) => {
|
||||
for (let i = 0; i < d.directories.length; i++) {
|
||||
d.directories[i] = await gm.selectParentDir(connection, d.directories[i].name,
|
||||
path.join(DiskMangerWorker.pathFromParent(d), path.sep));
|
||||
await gm.fillParentDir(connection, <any>d.directories[i]);
|
||||
await populateDir(d.directories[i]);
|
||||
}
|
||||
};
|
||||
await populateDir(dir);
|
||||
|
||||
await ObjectManagers.reset();
|
||||
return dir;
|
||||
}
|
||||
|
||||
public async initDB() {
|
||||
if (this.dbType === ServerConfig.DatabaseType.sqlite) {
|
||||
|
@ -2,15 +2,14 @@ import {expect} from 'chai';
|
||||
import {PersonManager} from '../../../../../src/backend/model/database/sql/PersonManager';
|
||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||
import {TestHelper} from './TestHelper';
|
||||
import {PhotoDTO} from '../../../../../src/common/entities/PhotoDTO';
|
||||
import {PersonEntry} from '../../../../../src/backend/model/database/sql/enitites/PersonEntry';
|
||||
import {FaceRegionEntry} from '../../../../../src/backend/model/database/sql/enitites/FaceRegionEntry';
|
||||
import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection';
|
||||
import {PhotoEntity} from '../../../../../src/backend/model/database/sql/enitites/PhotoEntity';
|
||||
import {DirectoryEntity} from '../../../../../src/backend/model/database/sql/enitites/DirectoryEntity';
|
||||
import {VideoEntity} from '../../../../../src/backend/model/database/sql/enitites/VideoEntity';
|
||||
import {PhotoDTO, PhotoMetadata} from '../../../../../src/common/entities/PhotoDTO';
|
||||
import {Utils} from '../../../../../src/common/Utils';
|
||||
import {PersonWithSampleRegion} from '../../../../../src/common/entities/PersonDTO';
|
||||
import {DirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
||||
import {VideoDTO} from '../../../../../src/common/entities/VideoDTO';
|
||||
import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection';
|
||||
import {PersonEntry} from '../../../../../src/backend/model/database/sql/enitites/PersonEntry';
|
||||
import {MediaDTO} from '../../../../../src/common/entities/MediaDTO';
|
||||
|
||||
|
||||
// to help WebStorm to handle the test cases
|
||||
@ -24,44 +23,34 @@ describe = SQLTestHelper.describe;
|
||||
describe('PersonManager', (sqlHelper: SQLTestHelper) => {
|
||||
|
||||
|
||||
const dir = TestHelper.getDirectoryEntry();
|
||||
let p = TestHelper.getPhotoEntry1(dir);
|
||||
let p2 = TestHelper.getPhotoEntry2(dir);
|
||||
let p_faceLess = TestHelper.getPhotoEntry2(dir);
|
||||
delete p_faceLess.metadata.faces;
|
||||
p_faceLess.name = 'fl';
|
||||
const v = TestHelper.getVideoEntry1(dir);
|
||||
const savedPerson: PersonWithSampleRegion[] = [];
|
||||
let dir: DirectoryDTO;
|
||||
|
||||
let v: VideoDTO;
|
||||
let p: PhotoDTO;
|
||||
let p2: PhotoDTO;
|
||||
let p_faceLess: PhotoDTO;
|
||||
|
||||
let savedPerson: PersonWithSampleRegion[] = [];
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
await sqlHelper.initDB();
|
||||
const directory: DirectoryDTO = TestHelper.getDirectoryEntry();
|
||||
TestHelper.getPhotoEntry1(directory);
|
||||
TestHelper.getPhotoEntry2(directory);
|
||||
const pFaceLess = TestHelper.getPhotoEntry3(directory);
|
||||
delete pFaceLess.metadata.faces;
|
||||
TestHelper.getVideoEntry1(directory);
|
||||
|
||||
const savePhoto = async (photo: PhotoDTO) => {
|
||||
const savedPhoto = await pr.save(photo);
|
||||
if (!photo.metadata.faces) {
|
||||
return savedPhoto;
|
||||
}
|
||||
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});
|
||||
savedPhoto.metadata.faces[i] = await conn.getRepository(FaceRegionEntry).save({box: face.box, person: person, media: savedPhoto});
|
||||
savedPerson.push(person);
|
||||
}
|
||||
return savedPhoto;
|
||||
};
|
||||
const conn = await SQLConnection.getConnection();
|
||||
|
||||
const pr = conn.getRepository(PhotoEntity);
|
||||
|
||||
await conn.getRepository(DirectoryEntity).save(p.directory);
|
||||
p = await savePhoto(p);
|
||||
console.log(p.id);
|
||||
p2 = await savePhoto(p2);
|
||||
p_faceLess = await savePhoto(p_faceLess);
|
||||
|
||||
await conn.getRepository(VideoEntity).save(v);
|
||||
await (new PersonManager()).onGalleryIndexUpdate();
|
||||
await SQLConnection.close();
|
||||
dir = await SQLTestHelper.persistTestDir(directory);
|
||||
p = <any>dir.media[0];
|
||||
p2 = <any>dir.media[1];
|
||||
p_faceLess = <any>dir.media[2];
|
||||
v = <any>dir.media[3];
|
||||
savedPerson = await (await SQLConnection.getConnection()).getRepository(PersonEntry).find({
|
||||
relations: ['sampleRegion',
|
||||
'sampleRegion.media',
|
||||
'sampleRegion.media.directory']
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -74,19 +63,14 @@ describe('PersonManager', (sqlHelper: SQLTestHelper) => {
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should get person', async () => {
|
||||
const pm = new PersonManager();
|
||||
const person = Utils.clone(savedPerson[0]);
|
||||
person.sampleRegion = <any>{
|
||||
id: p.metadata.faces[0].id,
|
||||
box: p.metadata.faces[0].box
|
||||
};
|
||||
const tmp = p.metadata.faces;
|
||||
delete p.metadata.faces;
|
||||
person.sampleRegion.media = Utils.clone(p);
|
||||
p.metadata.faces = tmp;
|
||||
person.count = 1;
|
||||
expect(await pm.get(person.name)).to.deep.equal(person);
|
||||
|
||||
expect(await pm.get('Boba Fett')).to.deep.equal(person);
|
||||
|
||||
expect((await pm.get('Boba Fett') as PersonWithSampleRegion).sampleRegion.media.name).to.deep.equal(p.name);
|
||||
});
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -73,7 +73,7 @@ export class TestHelper {
|
||||
m.creationDate = Date.now();
|
||||
m.fileSize = 123456789;
|
||||
m.orientation = OrientationTypes.TOP_LEFT;
|
||||
// m.rating = 0; no rating by default
|
||||
// m.rating = 0; no rating by default
|
||||
|
||||
// TODO: remove when typeorm is fixed
|
||||
m.duration = null;
|
||||
@ -82,6 +82,7 @@ export class TestHelper {
|
||||
|
||||
const d = new PhotoEntity();
|
||||
d.name = 'test media.jpg';
|
||||
d.directory = <any>dir;
|
||||
dir.media.push(d);
|
||||
d.metadata = m;
|
||||
dir.mediaCount++;
|
||||
|
Loading…
Reference in New Issue
Block a user