mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-23 01:27:14 +02:00
Adding more indices to fields and improving SQL queries #437
This commit is contained in:
parent
eea77a5bea
commit
d75a307be6
@ -3,9 +3,9 @@ import { ErrorCodes, ErrorDTO } from '../../common/entities/Error';
|
||||
import { ObjectManagers } from '../model/ObjectManagers';
|
||||
import {
|
||||
PersonDTO,
|
||||
PersonWithSampleRegion,
|
||||
} from '../../common/entities/PersonDTO';
|
||||
import { Utils } from '../../common/Utils';
|
||||
import {PersonEntry} from '../model/database/sql/enitites/PersonEntry';
|
||||
|
||||
export class PersonMWs {
|
||||
public static async updatePerson(
|
||||
@ -90,7 +90,7 @@ export class PersonMWs {
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
const persons = Utils.clone(req.resultPipe as PersonWithSampleRegion[]);
|
||||
const persons = Utils.clone(req.resultPipe as PersonEntry[]);
|
||||
for (const item of persons) {
|
||||
delete item.sampleRegion;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import {NextFunction, Request, Response} from 'express';
|
||||
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
||||
import {ContentWrapper} from '../../../common/entities/ConentWrapper';
|
||||
import {
|
||||
DirectoryPathDTO,
|
||||
ParentDirectoryDTO,
|
||||
SubDirectoryDTO,
|
||||
} from '../../../common/entities/DirectoryDTO';
|
||||
@ -12,8 +13,8 @@ import {Config} from '../../../common/config/private/Config';
|
||||
import {ThumbnailSourceType} from '../../model/threading/PhotoWorker';
|
||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||
import {PhotoProcessing} from '../../model/fileprocessing/PhotoProcessing';
|
||||
import {PersonWithSampleRegion} from '../../../common/entities/PersonDTO';
|
||||
import {ServerTime} from '../ServerTimingMWs';
|
||||
import {PersonEntry} from '../../model/database/sql/enitites/PersonEntry';
|
||||
|
||||
export class ThumbnailGeneratorMWs {
|
||||
private static ThumbnailMapEntries =
|
||||
@ -71,7 +72,7 @@ export class ThumbnailGeneratorMWs {
|
||||
try {
|
||||
const size: number = Config.Client.Media.Thumbnail.personThumbnailSize;
|
||||
|
||||
const persons: PersonWithSampleRegion[] = req.resultPipe as PersonWithSampleRegion[];
|
||||
const persons: PersonEntry[] = req.resultPipe as PersonEntry[];
|
||||
|
||||
for (const item of persons) {
|
||||
if (!item.sampleRegion) {
|
||||
@ -88,7 +89,7 @@ export class ThumbnailGeneratorMWs {
|
||||
// generate thumbnail path
|
||||
const thPath = PhotoProcessing.generatePersonThumbnailPath(
|
||||
mediaPath,
|
||||
item.sampleRegion,
|
||||
item.sampleRegion.media.metadata.faces.find(f => f.name === item.name),
|
||||
size
|
||||
);
|
||||
|
||||
@ -115,7 +116,7 @@ export class ThumbnailGeneratorMWs {
|
||||
if (!req.resultPipe) {
|
||||
return next();
|
||||
}
|
||||
const person: PersonWithSampleRegion = req.resultPipe as PersonWithSampleRegion;
|
||||
const person: PersonEntry = req.resultPipe as PersonEntry;
|
||||
try {
|
||||
req.resultPipe = await PhotoProcessing.generatePersonThumbnail(person);
|
||||
return next();
|
||||
@ -214,24 +215,24 @@ export class ThumbnailGeneratorMWs {
|
||||
directory: ParentDirectoryDTO | SubDirectoryDTO
|
||||
): void {
|
||||
if (typeof directory.media !== 'undefined') {
|
||||
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media);
|
||||
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media, directory);
|
||||
}
|
||||
if (directory.preview) {
|
||||
ThumbnailGeneratorMWs.addThInfoToAPhoto(directory.preview);
|
||||
ThumbnailGeneratorMWs.addThInfoToAPhoto(directory.preview, directory);
|
||||
}
|
||||
}
|
||||
|
||||
private static addThInfoToPhotos(photos: MediaDTO[]): void {
|
||||
private static addThInfoToPhotos(photos: MediaDTO[], directory?: DirectoryPathDTO): void {
|
||||
for (let i = 0; i < photos.length; ++i) {
|
||||
this.addThInfoToAPhoto(photos[i]);
|
||||
this.addThInfoToAPhoto(photos[i], directory ? directory : photos[i].directory);
|
||||
}
|
||||
}
|
||||
|
||||
private static addThInfoToAPhoto(photo: MediaDTO): void {
|
||||
private static addThInfoToAPhoto(photo: MediaDTO, directory: DirectoryPathDTO): void {
|
||||
const fullMediaPath = path.join(
|
||||
ProjectPath.ImageFolder,
|
||||
photo.directory.path,
|
||||
photo.directory.name,
|
||||
directory.path,
|
||||
directory.name,
|
||||
photo.name
|
||||
);
|
||||
for (let i = 0; i < ThumbnailGeneratorMWs.ThumbnailMapEntries.length; ++i) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { PersonEntry } from '../sql/enitites/PersonEntry';
|
||||
import { PersonDTO } from '../../../../common/entities/PersonDTO';
|
||||
import { IObjectManager } from './IObjectManager';
|
||||
import { FaceRegion } from '../../../../common/entities/PhotoDTO';
|
||||
import {PersonEntry} from '../sql/enitites/PersonEntry';
|
||||
import {PersonDTO} from '../../../../common/entities/PersonDTO';
|
||||
import {IObjectManager} from './IObjectManager';
|
||||
|
||||
export interface IPersonManager extends IObjectManager {
|
||||
getAll(): Promise<PersonEntry[]>;
|
||||
@ -9,7 +8,7 @@ export interface IPersonManager extends IObjectManager {
|
||||
get(name: string): Promise<PersonEntry>;
|
||||
|
||||
// saving a Person with a sample region. Person entry cannot exist without a face region
|
||||
saveAll(person: { name: string; faceRegion: FaceRegion }[]): Promise<void>;
|
||||
saveAll(person: { name: string; mediaId: number }[]): Promise<void>;
|
||||
|
||||
updatePerson(name: string, partialPerson: PersonDTO): Promise<PersonEntry>;
|
||||
|
||||
|
@ -7,7 +7,7 @@ export class PersonManager implements IPersonManager {
|
||||
throw new Error('not supported by memory DB');
|
||||
}
|
||||
|
||||
saveAll(person: { name: string; faceRegion: FaceRegion }[]): Promise<void> {
|
||||
saveAll(person: { name: string; mediaId: number }[]): Promise<void> {
|
||||
throw new Error('not supported by memory DB');
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,24 @@
|
||||
import { IGalleryManager } from '../interfaces/IGalleryManager';
|
||||
import {IGalleryManager} from '../interfaces/IGalleryManager';
|
||||
import {
|
||||
ParentDirectoryDTO,
|
||||
SubDirectoryDTO,
|
||||
} from '../../../../common/entities/DirectoryDTO';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { DirectoryEntity } from './enitites/DirectoryEntity';
|
||||
import { SQLConnection } from './SQLConnection';
|
||||
import { PhotoEntity } from './enitites/PhotoEntity';
|
||||
import { ProjectPath } from '../../../ProjectPath';
|
||||
import { Config } from '../../../../common/config/private/Config';
|
||||
import { ISQLGalleryManager } from './IGalleryManager';
|
||||
import { PhotoDTO } from '../../../../common/entities/PhotoDTO';
|
||||
import { Connection } from 'typeorm';
|
||||
import { MediaEntity } from './enitites/MediaEntity';
|
||||
import { VideoEntity } from './enitites/VideoEntity';
|
||||
import { DiskMangerWorker } from '../../threading/DiskMangerWorker';
|
||||
import { Logger } from '../../../Logger';
|
||||
import { FaceRegionEntry } from './enitites/FaceRegionEntry';
|
||||
import { ObjectManagers } from '../../ObjectManagers';
|
||||
import { DuplicatesDTO } from '../../../../common/entities/DuplicatesDTO';
|
||||
import { ReIndexingSensitivity } from '../../../../common/config/private/PrivateConfig';
|
||||
import {DirectoryEntity} from './enitites/DirectoryEntity';
|
||||
import {SQLConnection} from './SQLConnection';
|
||||
import {PhotoEntity} from './enitites/PhotoEntity';
|
||||
import {ProjectPath} from '../../../ProjectPath';
|
||||
import {Config} from '../../../../common/config/private/Config';
|
||||
import {ISQLGalleryManager} from './IGalleryManager';
|
||||
import {Connection} from 'typeorm';
|
||||
import {MediaEntity} from './enitites/MediaEntity';
|
||||
import {VideoEntity} from './enitites/VideoEntity';
|
||||
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
|
||||
import {Logger} from '../../../Logger';
|
||||
import {ObjectManagers} from '../../ObjectManagers';
|
||||
import {DuplicatesDTO} from '../../../../common/entities/DuplicatesDTO';
|
||||
import {ReIndexingSensitivity} from '../../../../common/config/private/PrivateConfig';
|
||||
|
||||
const LOG_TAG = '[GalleryManager]';
|
||||
|
||||
@ -52,13 +50,11 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
);
|
||||
const lastModified = DiskMangerWorker.calcLastModified(stat);
|
||||
|
||||
const dir = await this.selectParentDir(
|
||||
connection,
|
||||
directoryPath.name,
|
||||
directoryPath.parent
|
||||
);
|
||||
|
||||
const dir = await this.getDirIdAndTime(connection, directoryPath.name, directoryPath.parent);
|
||||
|
||||
if (dir && dir.lastScanned != null) {
|
||||
// If it seems that the content did not changed, do not work on it
|
||||
// If it seems that the content did not change, do not work on it
|
||||
if (
|
||||
knownLastModified &&
|
||||
knownLastScanned &&
|
||||
@ -73,9 +69,9 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
}
|
||||
if (
|
||||
Date.now() - dir.lastScanned <=
|
||||
Config.Server.Indexing.cachedFolderTimeout &&
|
||||
Config.Server.Indexing.cachedFolderTimeout &&
|
||||
Config.Server.Indexing.reIndexingSensitivity ===
|
||||
ReIndexingSensitivity.medium
|
||||
ReIndexingSensitivity.medium
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@ -85,9 +81,9 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
Logger.silly(
|
||||
LOG_TAG,
|
||||
'Reindexing reason: lastModified mismatch: known: ' +
|
||||
dir.lastModified +
|
||||
', current:' +
|
||||
lastModified
|
||||
dir.lastModified +
|
||||
', current:' +
|
||||
lastModified
|
||||
);
|
||||
const ret =
|
||||
await ObjectManagers.getInstance().IndexingManager.indexDirectory(
|
||||
@ -95,7 +91,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
);
|
||||
for (const subDir of ret.directories) {
|
||||
if (!subDir.preview) {
|
||||
// if sub directories does not have photos, so cannot show a preview, try get one from DB
|
||||
// if subdirectories do not have photos, so cannot show a preview, try getting one from DB
|
||||
await this.fillPreviewForSubDir(connection, subDir);
|
||||
}
|
||||
}
|
||||
@ -107,25 +103,24 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
(Date.now() - dir.lastScanned >
|
||||
Config.Server.Indexing.cachedFolderTimeout &&
|
||||
Config.Server.Indexing.reIndexingSensitivity >=
|
||||
ReIndexingSensitivity.medium) ||
|
||||
ReIndexingSensitivity.medium) ||
|
||||
Config.Server.Indexing.reIndexingSensitivity >=
|
||||
ReIndexingSensitivity.high
|
||||
ReIndexingSensitivity.high
|
||||
) {
|
||||
// on the fly reindexing
|
||||
|
||||
Logger.silly(
|
||||
LOG_TAG,
|
||||
'lazy reindexing reason: cache timeout: lastScanned: ' +
|
||||
(Date.now() - dir.lastScanned) +
|
||||
'ms ago, cachedFolderTimeout:' +
|
||||
Config.Server.Indexing.cachedFolderTimeout
|
||||
(Date.now() - dir.lastScanned) +
|
||||
'ms ago, cachedFolderTimeout:' +
|
||||
Config.Server.Indexing.cachedFolderTimeout
|
||||
);
|
||||
ObjectManagers.getInstance()
|
||||
.IndexingManager.indexDirectory(relativeDirectoryName)
|
||||
.catch(console.error);
|
||||
}
|
||||
await this.fillParentDir(connection, dir);
|
||||
return dir;
|
||||
return await this.getParentDirFromId(connection, dir.id);
|
||||
}
|
||||
|
||||
// never scanned (deep indexed), do it and return with it
|
||||
@ -145,7 +140,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
|
||||
async countMediaSize(): Promise<number> {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
const { sum } = await connection
|
||||
const {sum} = await connection
|
||||
.getRepository(MediaEntity)
|
||||
.createQueryBuilder('media')
|
||||
.select('SUM(media.metadata.fileSize)', 'sum')
|
||||
@ -234,7 +229,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
}
|
||||
}
|
||||
|
||||
duplicateParis.push({ media: list });
|
||||
duplicateParis.push({media: list});
|
||||
}
|
||||
};
|
||||
|
||||
@ -318,17 +313,30 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
dir.isPartial = true;
|
||||
}
|
||||
|
||||
protected async selectParentDir(
|
||||
protected async getDirIdAndTime(connection: Connection, name: string, path: string): Promise<{ id: number, lastScanned: number, lastModified: number }> {
|
||||
return await connection
|
||||
.getRepository(DirectoryEntity)
|
||||
.createQueryBuilder('directory')
|
||||
.where('directory.name = :name AND directory.path = :path', {
|
||||
name: name,
|
||||
path: path,
|
||||
})
|
||||
.select([
|
||||
'directory.id',
|
||||
'directory.lastScanned',
|
||||
'directory.lastModified',
|
||||
]).getOne();
|
||||
}
|
||||
|
||||
protected async getParentDirFromId(
|
||||
connection: Connection,
|
||||
directoryName: string,
|
||||
directoryParent: string
|
||||
partialDirId: number
|
||||
): Promise<ParentDirectoryDTO> {
|
||||
const query = connection
|
||||
.getRepository(DirectoryEntity)
|
||||
.createQueryBuilder('directory')
|
||||
.where('directory.name = :name AND directory.path = :path', {
|
||||
name: directoryName,
|
||||
path: directoryParent,
|
||||
.where('directory.id = :id', {
|
||||
id: partialDirId
|
||||
})
|
||||
.leftJoinAndSelect('directory.directories', 'directories')
|
||||
.leftJoinAndSelect('directory.media', 'media')
|
||||
@ -344,7 +352,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
]);
|
||||
|
||||
// TODO: do better filtering
|
||||
// NOTE: it should not cause an issue as it also do not shave to the DB
|
||||
// NOTE: it should not cause an issue as it also do not save to the DB
|
||||
if (
|
||||
Config.Client.MetaFile.gpx === true ||
|
||||
Config.Client.MetaFile.pg2conf === true ||
|
||||
@ -353,52 +361,13 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
query.leftJoinAndSelect('directory.metaFile', 'metaFile');
|
||||
}
|
||||
|
||||
return await query.getOne();
|
||||
}
|
||||
|
||||
protected async fillParentDir(
|
||||
connection: Connection,
|
||||
dir: ParentDirectoryDTO
|
||||
): Promise<void> {
|
||||
if (dir.media) {
|
||||
const indexedFaces = await connection
|
||||
.getRepository(FaceRegionEntry)
|
||||
.createQueryBuilder('face')
|
||||
.leftJoinAndSelect('face.media', 'media')
|
||||
.where('media.directory = :directory', {
|
||||
directory: dir.id,
|
||||
})
|
||||
.leftJoinAndSelect('face.person', 'person')
|
||||
.select([
|
||||
'face.id',
|
||||
'face.box.left',
|
||||
'face.box.top',
|
||||
'face.box.width',
|
||||
'face.box.height',
|
||||
'media.id',
|
||||
'person.name',
|
||||
'person.id',
|
||||
])
|
||||
.getMany();
|
||||
for (const item of dir.media) {
|
||||
item.directory = dir;
|
||||
(item as PhotoDTO).metadata.faces = indexedFaces
|
||||
.filter((fe): boolean => fe.media.id === item.id)
|
||||
.map((f): { name: any; box: any } => ({
|
||||
box: f.box,
|
||||
name: f.person.name,
|
||||
}));
|
||||
}
|
||||
}
|
||||
if (dir.metaFile) {
|
||||
for (const item of dir.metaFile) {
|
||||
item.directory = dir;
|
||||
}
|
||||
}
|
||||
const dir = await query.getOne();
|
||||
if (dir.directories) {
|
||||
for (const item of dir.directories) {
|
||||
await this.fillPreviewForSubDir(connection, item);
|
||||
}
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import {VideoEntity} from './enitites/VideoEntity';
|
||||
import {FileEntity} from './enitites/FileEntity';
|
||||
import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||
import {NotificationManager} from '../../NotifocationManager';
|
||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||
import {ObjectManagers} from '../../ObjectManagers';
|
||||
import {IIndexingManager} from '../interfaces/IIndexingManager';
|
||||
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
|
||||
@ -29,6 +28,7 @@ import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import {SearchQueryDTO} from '../../../../common/entities/SearchQueryDTO';
|
||||
import {PersonEntry} from './enitites/PersonEntry';
|
||||
import {PersonJunctionTable} from './enitites/PersonJunctionTable';
|
||||
|
||||
const LOG_TAG = '[IndexingManager]';
|
||||
|
||||
@ -346,16 +346,16 @@ export class IndexingManager implements IIndexingManager {
|
||||
})
|
||||
.getMany();
|
||||
|
||||
const mediaChange: any = {
|
||||
saveP: [], // save/update photo
|
||||
saveV: [], // save/update video
|
||||
insertP: [], // insert photo
|
||||
insertV: [], // insert video
|
||||
const mediaChange = {
|
||||
saveP: [] as MediaDTO[], // save/update photo
|
||||
saveV: [] as MediaDTO[], // save/update video
|
||||
insertP: [] as MediaDTO[], // insert photo
|
||||
insertV: [] as MediaDTO[], // insert video
|
||||
};
|
||||
const facesPerPhoto: { faces: FaceRegionEntry[]; mediaName: string }[] = [];
|
||||
const personsPerPhoto: { faces: { name: string, mediaId?: number }[]; mediaName: string }[] = [];
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < media.length; i++) {
|
||||
let mediaItem: MediaEntity = null;
|
||||
let mediaItem: MediaDTO = null;
|
||||
for (let j = 0; j < indexedMedia.length; j++) {
|
||||
if (indexedMedia[j].name === media[i].name) {
|
||||
mediaItem = indexedMedia[j];
|
||||
@ -364,7 +364,7 @@ export class IndexingManager implements IIndexingManager {
|
||||
}
|
||||
}
|
||||
|
||||
const scannedFaces = (media[i].metadata as PhotoMetadata).faces || [];
|
||||
const scannedFaces: { name: string }[] = (media[i].metadata as PhotoMetadata).faces || [];
|
||||
if ((media[i].metadata as PhotoMetadata).faces) {
|
||||
// if it has faces, cache them
|
||||
// make the list distinct (some photos may contain the same person multiple times)
|
||||
@ -374,22 +374,22 @@ export class IndexingManager implements IIndexingManager {
|
||||
),
|
||||
];
|
||||
}
|
||||
delete (media[i].metadata as PhotoMetadata).faces; // this is a separated DB, lets save separately
|
||||
|
||||
|
||||
if (mediaItem == null) {
|
||||
// not in DB yet
|
||||
// Media not in DB yet
|
||||
media[i].directory = null;
|
||||
mediaItem = Utils.clone(media[i]) as any;
|
||||
mediaItem = Utils.clone(media[i]);
|
||||
mediaItem.directory = {id: parentDirId} as any;
|
||||
(MediaDTOUtils.isPhoto(mediaItem)
|
||||
? mediaChange.insertP
|
||||
: mediaChange.insertV
|
||||
).push(mediaItem);
|
||||
} else {
|
||||
// already in the DB, only needs to be updated
|
||||
// Media already in the DB, only needs to be updated
|
||||
delete (mediaItem.metadata as PhotoMetadata).faces;
|
||||
if (!Utils.equalsFilter(mediaItem.metadata, media[i].metadata)) {
|
||||
mediaItem.metadata = media[i].metadata as any;
|
||||
mediaItem.metadata = media[i].metadata;
|
||||
(MediaDTOUtils.isPhoto(mediaItem)
|
||||
? mediaChange.saveP
|
||||
: mediaChange.saveV
|
||||
@ -397,9 +397,9 @@ export class IndexingManager implements IIndexingManager {
|
||||
}
|
||||
}
|
||||
|
||||
facesPerPhoto.push({
|
||||
faces: scannedFaces as FaceRegionEntry[],
|
||||
mediaName: mediaItem.name,
|
||||
personsPerPhoto.push({
|
||||
faces: scannedFaces,
|
||||
mediaName: mediaItem.name
|
||||
});
|
||||
}
|
||||
|
||||
@ -416,44 +416,44 @@ export class IndexingManager implements IIndexingManager {
|
||||
.select(['media.name', 'media.id'])
|
||||
.getMany();
|
||||
|
||||
const faces: FaceRegionEntry[] = [];
|
||||
facesPerPhoto.forEach((group): void => {
|
||||
const persons: { name: string; mediaId: number }[] = [];
|
||||
personsPerPhoto.forEach((group): void => {
|
||||
const mIndex = indexedMedia.findIndex(
|
||||
(m): boolean => m.name === group.mediaName
|
||||
);
|
||||
group.faces.forEach(
|
||||
(sf: FaceRegionEntry): any =>
|
||||
(sf.media = {id: indexedMedia[mIndex].id} as any)
|
||||
group.faces.forEach((sf) =>
|
||||
(sf.mediaId = indexedMedia[mIndex].id)
|
||||
);
|
||||
|
||||
faces.push(...group.faces);
|
||||
persons.push(...group.faces as { name: string; mediaId: number }[]);
|
||||
indexedMedia.splice(mIndex, 1);
|
||||
});
|
||||
|
||||
await this.saveFaces(connection, parentDirId, faces);
|
||||
await this.savePersonsToMedia(connection, parentDirId, persons);
|
||||
await mediaRepository.remove(indexedMedia);
|
||||
}
|
||||
|
||||
protected async saveFaces(
|
||||
protected async savePersonsToMedia(
|
||||
connection: Connection,
|
||||
parentDirId: number,
|
||||
scannedFaces: FaceRegion[]
|
||||
scannedFaces: { name: string; mediaId: number }[]
|
||||
): Promise<void> {
|
||||
const faceRepository = connection.getRepository(FaceRegionEntry);
|
||||
const personJunctionTable = connection.getRepository(PersonJunctionTable);
|
||||
const personRepository = connection.getRepository(PersonEntry);
|
||||
|
||||
const persons: { name: string; faceRegion: FaceRegion }[] = [];
|
||||
const persons: { name: string; mediaId: number }[] = [];
|
||||
|
||||
// Make a set
|
||||
for (const face of scannedFaces) {
|
||||
if (persons.findIndex((f) => f.name === face.name) === -1) {
|
||||
persons.push({name: face.name, faceRegion: face});
|
||||
persons.push(face);
|
||||
}
|
||||
}
|
||||
await ObjectManagers.getInstance().PersonManager.saveAll(persons);
|
||||
// get saved persons without triggering denormalized data update (i.e.: do not use PersonManager.get).
|
||||
const savedPersons = await personRepository.find();
|
||||
|
||||
const indexedFaces = await faceRepository
|
||||
const indexedFaces = await personJunctionTable
|
||||
.createQueryBuilder('face')
|
||||
.leftJoin('face.media', 'media')
|
||||
.where('media.directory = :directory', {
|
||||
@ -462,19 +462,13 @@ export class IndexingManager implements IIndexingManager {
|
||||
.leftJoinAndSelect('face.person', 'person')
|
||||
.getMany();
|
||||
|
||||
const faceToInsert = [];
|
||||
const faceToInsert: { person: { id: number }, media: { id: number } }[] = [];
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < scannedFaces.length; i++) {
|
||||
// was the face region already indexed
|
||||
let face: FaceRegionEntry = null;
|
||||
// was the Person - media connection already indexed
|
||||
let face: PersonJunctionTable = null;
|
||||
for (let j = 0; j < indexedFaces.length; j++) {
|
||||
if (
|
||||
indexedFaces[j].box.height === scannedFaces[i].box.height &&
|
||||
indexedFaces[j].box.width === scannedFaces[i].box.width &&
|
||||
indexedFaces[j].box.left === scannedFaces[i].box.left &&
|
||||
indexedFaces[j].box.top === scannedFaces[i].box.top &&
|
||||
indexedFaces[j].person.name === scannedFaces[i].name
|
||||
) {
|
||||
if (indexedFaces[j].person.name === scannedFaces[i].name) {
|
||||
face = indexedFaces[j];
|
||||
indexedFaces.splice(j, 1);
|
||||
break; // region found, stop processing
|
||||
@ -482,16 +476,18 @@ export class IndexingManager implements IIndexingManager {
|
||||
}
|
||||
|
||||
if (face == null) {
|
||||
(scannedFaces[i] as FaceRegionEntry).person = savedPersons.find(
|
||||
(p) => p.name === scannedFaces[i].name
|
||||
);
|
||||
faceToInsert.push(scannedFaces[i]);
|
||||
faceToInsert.push({
|
||||
person: savedPersons.find(
|
||||
(p) => p.name === scannedFaces[i].name
|
||||
),
|
||||
media: {id: scannedFaces[i].mediaId}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (faceToInsert.length > 0) {
|
||||
await this.insertChunk(faceRepository, faceToInsert, 100);
|
||||
await this.insertChunk(personJunctionTable, faceToInsert, 100);
|
||||
}
|
||||
await faceRepository.remove(indexedFaces, {
|
||||
await personJunctionTable.remove(indexedFaces, {
|
||||
chunk: Math.max(Math.ceil(indexedFaces.length / 500), 1),
|
||||
});
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { SQLConnection } from './SQLConnection';
|
||||
import { PersonEntry } from './enitites/PersonEntry';
|
||||
import { FaceRegionEntry } from './enitites/FaceRegionEntry';
|
||||
import { PersonDTO } from '../../../../common/entities/PersonDTO';
|
||||
import { ISQLPersonManager } from './IPersonManager';
|
||||
import { Logger } from '../../../Logger';
|
||||
import { FaceRegion } from '../../../../common/entities/PhotoDTO';
|
||||
import { SQL_COLLATE } from './enitites/EntityUtils';
|
||||
import {SQLConnection} from './SQLConnection';
|
||||
import {PersonEntry} from './enitites/PersonEntry';
|
||||
import {PersonDTO} from '../../../../common/entities/PersonDTO';
|
||||
import {ISQLPersonManager} from './IPersonManager';
|
||||
import {Logger} from '../../../Logger';
|
||||
import {FaceRegion} from '../../../../common/entities/PhotoDTO';
|
||||
import {SQL_COLLATE} from './enitites/EntityUtils';
|
||||
import {PersonJunctionTable} from './enitites/PersonJunctionTable';
|
||||
|
||||
const LOG_TAG = '[PersonManager]';
|
||||
|
||||
@ -20,7 +20,7 @@ export class PersonManager implements ISQLPersonManager {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
await connection.query(
|
||||
'UPDATE person_entry SET count = ' +
|
||||
' (SELECT COUNT(1) FROM face_region_entry WHERE face_region_entry.personId = person_entry.id)'
|
||||
' (SELECT COUNT(1) FROM person_junction_table WHERE person_junction_table.personId = person_entry.id)'
|
||||
);
|
||||
|
||||
// remove persons without photo
|
||||
@ -36,11 +36,11 @@ export class PersonManager implements ISQLPersonManager {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
await connection.query(
|
||||
'update person_entry set sampleRegionId = ' +
|
||||
'(Select face_region_entry.id from media_entity ' +
|
||||
'left join face_region_entry on media_entity.id = face_region_entry.mediaId ' +
|
||||
'where face_region_entry.personId=person_entry.id ' +
|
||||
'order by media_entity.metadataCreationdate desc ' +
|
||||
'limit 1)'
|
||||
'(Select person_junction_table.id from media_entity ' +
|
||||
'left join person_junction_table on media_entity.id = person_junction_table.mediaId ' +
|
||||
'where person_junction_table.personId=person_entry.id ' +
|
||||
'order by media_entity.metadataCreationdate desc ' +
|
||||
'limit 1)'
|
||||
);
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ export class PersonManager implements ISQLPersonManager {
|
||||
const person = await repository
|
||||
.createQueryBuilder('person')
|
||||
.limit(1)
|
||||
.where('person.name LIKE :name COLLATE ' + SQL_COLLATE, { name })
|
||||
.where('person.name LIKE :name COLLATE ' + SQL_COLLATE, {name})
|
||||
.getOne();
|
||||
|
||||
if (typeof partialPerson.name !== 'undefined') {
|
||||
@ -83,8 +83,8 @@ export class PersonManager implements ISQLPersonManager {
|
||||
public async countFaces(): Promise<number> {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
return await connection
|
||||
.getRepository(FaceRegionEntry)
|
||||
.createQueryBuilder('faceRegion')
|
||||
.getRepository(PersonJunctionTable)
|
||||
.createQueryBuilder('personJunction')
|
||||
.getCount();
|
||||
}
|
||||
|
||||
@ -96,12 +96,12 @@ export class PersonManager implements ISQLPersonManager {
|
||||
}
|
||||
|
||||
public async saveAll(
|
||||
persons: { name: string; faceRegion: FaceRegion }[]
|
||||
persons: { name: string; mediaId: number }[]
|
||||
): Promise<void> {
|
||||
const toSave: { name: string; faceRegion: FaceRegion }[] = [];
|
||||
const toSave: { name: string; mediaId: number }[] = [];
|
||||
const connection = await SQLConnection.getConnection();
|
||||
const personRepository = connection.getRepository(PersonEntry);
|
||||
const faceRegionRepository = connection.getRepository(FaceRegionEntry);
|
||||
const personJunction = connection.getRepository(PersonJunctionTable);
|
||||
|
||||
const savedPersons = await personRepository.find();
|
||||
// filter already existing persons
|
||||
@ -117,14 +117,13 @@ export class PersonManager implements ISQLPersonManager {
|
||||
if (toSave.length > 0) {
|
||||
for (let i = 0; i < toSave.length / 200; i++) {
|
||||
const saving = toSave.slice(i * 200, (i + 1) * 200);
|
||||
// saving person
|
||||
const inserted = await personRepository.insert(
|
||||
saving.map((p) => ({ name: p.name }))
|
||||
saving.map((p) => ({name: p.name}))
|
||||
);
|
||||
// setting Person id
|
||||
inserted.identifiers.forEach((idObj: { id: number }, j: number) => {
|
||||
(saving[j].faceRegion as FaceRegionEntry).person = idObj as any;
|
||||
});
|
||||
await faceRegionRepository.insert(saving.map((p) => p.faceRegion));
|
||||
// saving junction table
|
||||
const junctionTable = inserted.identifiers.map((idObj, j) => ({person: idObj, media: {id: saving[j].mediaId}}));
|
||||
await personJunction.insert(junctionTable);
|
||||
}
|
||||
}
|
||||
this.isDBValid = false;
|
||||
@ -148,6 +147,7 @@ export class PersonManager implements ISQLPersonManager {
|
||||
'sampleRegion',
|
||||
'sampleRegion.media',
|
||||
'sampleRegion.media.directory',
|
||||
'sampleRegion.media.metadata',
|
||||
],
|
||||
});
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import { MediaEntity } from './enitites/MediaEntity';
|
||||
import { VideoEntity } from './enitites/VideoEntity';
|
||||
import { DataStructureVersion } from '../../../../common/DataStructureVersion';
|
||||
import { FileEntity } from './enitites/FileEntity';
|
||||
import { FaceRegionEntry } from './enitites/FaceRegionEntry';
|
||||
import { PersonEntry } from './enitites/PersonEntry';
|
||||
import { Utils } from '../../../../common/Utils';
|
||||
import * as path from 'path';
|
||||
@ -31,6 +30,7 @@ import {
|
||||
import { AlbumBaseEntity } from './enitites/album/AlbumBaseEntity';
|
||||
import { SavedSearchEntity } from './enitites/album/SavedSearchEntity';
|
||||
import { NotificationManager } from '../../NotifocationManager';
|
||||
import {PersonJunctionTable} from './enitites/PersonJunctionTable';
|
||||
|
||||
const LOG_TAG = '[SQLConnection]';
|
||||
|
||||
@ -45,7 +45,7 @@ export class SQLConnection {
|
||||
options.entities = [
|
||||
UserEntity,
|
||||
FileEntity,
|
||||
FaceRegionEntry,
|
||||
PersonJunctionTable,
|
||||
PersonEntry,
|
||||
MediaEntity,
|
||||
PhotoEntity,
|
||||
@ -84,7 +84,7 @@ export class SQLConnection {
|
||||
options.entities = [
|
||||
UserEntity,
|
||||
FileEntity,
|
||||
FaceRegionEntry,
|
||||
PersonJunctionTable,
|
||||
PersonEntry,
|
||||
MediaEntity,
|
||||
PhotoEntity,
|
||||
|
@ -37,19 +37,12 @@ import {FileEntity} from './enitites/FileEntity';
|
||||
import {SQL_COLLATE} from './enitites/EntityUtils';
|
||||
|
||||
export class SearchManager implements ISQLSearchManager {
|
||||
// This trick enables us to list less rows as faces will be concatenated into one row
|
||||
// Also typeorm does not support automatic mapping of nested foreign keys
|
||||
// (i.e: leftJoinAndSelect('media.metadata.faces', 'faces') does not work)
|
||||
private FACE_SELECT =
|
||||
Config.Server.Database.type === DatabaseType.mysql
|
||||
? 'CONCAT(\'[\' , GROUP_CONCAT( \'{"name": "\' , person.name , \'", "box": {"top":\' , faces.box.top , \', "left":\' , faces.box.left , \', "height":\' , faces.box.height ,\', "width":\' , faces.box.width , \'}}\' ) ,\']\') as media_metadataFaces'
|
||||
: '\'[\' || GROUP_CONCAT( \'{"name": "\' || person.name || \'", "box": {"top":\' || faces.box.top || \', "left":\' || faces.box.left || \', "height":\' || faces.box.height ||\', "width":\' || faces.box.width || \'}}\' ) ||\']\' as media_metadataFaces';
|
||||
private DIRECTORY_SELECT = [
|
||||
'directory.id',
|
||||
'directory.name',
|
||||
'directory.path',
|
||||
];
|
||||
// makes all search query params unique, so typeorm wont mix them
|
||||
// makes all search query params unique, so typeorm won't mix them
|
||||
private queryIdBase = 0;
|
||||
|
||||
private static autoCompleteItemsUnique(
|
||||
@ -293,47 +286,28 @@ export class SearchManager implements ISQLSearchManager {
|
||||
resultOverflow: false,
|
||||
};
|
||||
|
||||
const rawAndEntries = await connection
|
||||
result.media = await connection
|
||||
.getRepository(MediaEntity)
|
||||
.createQueryBuilder('media')
|
||||
.select(['media', ...this.DIRECTORY_SELECT, this.FACE_SELECT])
|
||||
.select(['media', ...this.DIRECTORY_SELECT])
|
||||
.where(this.buildWhereQuery(query))
|
||||
.leftJoin('media.directory', 'directory')
|
||||
.leftJoin('media.metadata.faces', 'faces')
|
||||
.leftJoin('faces.person', 'person')
|
||||
.limit(Config.Client.Search.maxMediaResult + 1)
|
||||
.groupBy('media.id')
|
||||
.getRawAndEntities();
|
||||
.getMany();
|
||||
|
||||
for (let i = 0; i < rawAndEntries.entities.length; ++i) {
|
||||
if (rawAndEntries.raw[i].media_metadataFaces) {
|
||||
rawAndEntries.entities[i].metadata.faces = JSON.parse(
|
||||
rawAndEntries.raw[i].media_metadataFaces
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result.media = rawAndEntries.entities;
|
||||
|
||||
if (result.media.length > Config.Client.Search.maxMediaResult) {
|
||||
result.resultOverflow = true;
|
||||
}
|
||||
|
||||
|
||||
if (Config.Client.Search.listMetafiles === true) {
|
||||
const dIds = Array.from(new Set(result.media.map(m => (m.directory as unknown as { id: number }).id)));
|
||||
result.metaFile = await connection
|
||||
.getRepository(FileEntity)
|
||||
.createQueryBuilder('file')
|
||||
.select(['file', ...this.DIRECTORY_SELECT])
|
||||
.innerJoin(
|
||||
(q) =>
|
||||
q
|
||||
.from(MediaEntity, 'media')
|
||||
.select('distinct directory.id')
|
||||
.where(this.buildWhereQuery(query))
|
||||
.leftJoin('media.directory', 'directory'),
|
||||
'dir',
|
||||
'file.directory=dir.id'
|
||||
)
|
||||
.where(`file.directoryId IN(${dIds})`)
|
||||
.leftJoin('file.directory', 'directory')
|
||||
.getMany();
|
||||
}
|
||||
|
@ -14,10 +14,10 @@ import {
|
||||
MediaDTO,
|
||||
MediaMetadata,
|
||||
} from '../../../../../common/entities/MediaDTO';
|
||||
import { FaceRegionEntry } from './FaceRegionEntry';
|
||||
import { PersonJunctionTable} from './PersonJunctionTable';
|
||||
import { columnCharsetCS } from './EntityUtils';
|
||||
import {
|
||||
CameraMetadata,
|
||||
CameraMetadata, FaceRegion,
|
||||
GPSMetadata,
|
||||
PositionMetaData,
|
||||
} from '../../../../../common/entities/PhotoDTO';
|
||||
@ -117,6 +117,7 @@ export class MediaMetadataEntity implements MediaMetadata {
|
||||
to: (v) => v,
|
||||
},
|
||||
})
|
||||
@Index()
|
||||
creationDate: number;
|
||||
|
||||
@Column('int', { unsigned: true })
|
||||
@ -136,10 +137,18 @@ export class MediaMetadataEntity implements MediaMetadata {
|
||||
positionData: PositionMetaDataEntity;
|
||||
|
||||
@Column('tinyint', { unsigned: true })
|
||||
@Index()
|
||||
rating: 0 | 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
@OneToMany((type) => FaceRegionEntry, (faceRegion) => faceRegion.media)
|
||||
faces: FaceRegionEntry[];
|
||||
@OneToMany((type) => PersonJunctionTable, (junctionTable) => junctionTable.media)
|
||||
personJunction: PersonJunctionTable[];
|
||||
|
||||
@Column({
|
||||
type:'simple-json',
|
||||
nullable: true,
|
||||
charset: columnCharsetCS.charset,
|
||||
collation: columnCharsetCS.collation})
|
||||
faces: FaceRegion[];
|
||||
|
||||
/**
|
||||
* Caches the list of persons. Only used for searching
|
||||
|
@ -7,32 +7,35 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
Unique,
|
||||
} from 'typeorm';
|
||||
import { FaceRegionEntry } from './FaceRegionEntry';
|
||||
import { columnCharsetCS } from './EntityUtils';
|
||||
import { PersonWithSampleRegion } from '../../../../../common/entities/PersonDTO';
|
||||
import {PersonJunctionTable} from './PersonJunctionTable';
|
||||
import {columnCharsetCS} from './EntityUtils';
|
||||
import { PersonDTO } from '../../../../../common/entities/PersonDTO';
|
||||
|
||||
@Entity()
|
||||
@Unique(['name'])
|
||||
export class PersonEntry implements PersonWithSampleRegion {
|
||||
export class PersonEntry implements PersonDTO {
|
||||
@Index()
|
||||
@PrimaryGeneratedColumn({ unsigned: true })
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Column(columnCharsetCS)
|
||||
name: string;
|
||||
|
||||
@Column('int', { unsigned: true, default: 0 })
|
||||
@Column('int', {unsigned: true, default: 0})
|
||||
count: number;
|
||||
|
||||
@Column({ default: false })
|
||||
@Column({default: false})
|
||||
isFavourite: boolean;
|
||||
|
||||
@OneToMany((type) => FaceRegionEntry, (faceRegion) => faceRegion.person)
|
||||
public faces: FaceRegionEntry[];
|
||||
@OneToMany((type) => PersonJunctionTable, (junctionTable) => junctionTable.person)
|
||||
public faces: PersonJunctionTable[];
|
||||
|
||||
@ManyToOne((type) => FaceRegionEntry, {
|
||||
@ManyToOne((type) => PersonJunctionTable, {
|
||||
onDelete: 'SET NULL',
|
||||
nullable: true,
|
||||
})
|
||||
sampleRegion: FaceRegionEntry;
|
||||
sampleRegion: PersonJunctionTable;
|
||||
|
||||
// does not store in the DB, temporal field
|
||||
missingThumbnail?: boolean;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
import {ITaskExecuter, TaskExecuter} from '../threading/TaskExecuter';
|
||||
import {FaceRegion, PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
||||
import {PersonWithSampleRegion} from '../../../common/entities/PersonDTO';
|
||||
import {PersonEntry} from '../database/sql/enitites/PersonEntry';
|
||||
|
||||
export class PhotoProcessing {
|
||||
private static initDone = false;
|
||||
@ -47,7 +47,7 @@ export class PhotoProcessing {
|
||||
}
|
||||
|
||||
public static async generatePersonThumbnail(
|
||||
person: PersonWithSampleRegion
|
||||
person: PersonEntry
|
||||
): Promise<string> {
|
||||
// load parameters
|
||||
const photo: PhotoDTO = person.sampleRegion.media;
|
||||
@ -58,10 +58,11 @@ export class PhotoProcessing {
|
||||
photo.name
|
||||
);
|
||||
const size: number = Config.Client.Media.Thumbnail.personThumbnailSize;
|
||||
const faceRegion = person.sampleRegion.media.metadata.faces.find(f => f.name === person.name);
|
||||
// generate thumbnail path
|
||||
const thPath = PhotoProcessing.generatePersonThumbnailPath(
|
||||
mediaPath,
|
||||
person.sampleRegion,
|
||||
faceRegion,
|
||||
size
|
||||
);
|
||||
|
||||
@ -75,11 +76,11 @@ export class PhotoProcessing {
|
||||
|
||||
const margin = {
|
||||
x: Math.round(
|
||||
person.sampleRegion.box.width *
|
||||
faceRegion.box.width *
|
||||
Config.Server.Media.Thumbnail.personFaceMargin
|
||||
),
|
||||
y: Math.round(
|
||||
person.sampleRegion.box.height *
|
||||
faceRegion.box.height *
|
||||
Config.Server.Media.Thumbnail.personFaceMargin
|
||||
),
|
||||
};
|
||||
@ -93,13 +94,13 @@ export class PhotoProcessing {
|
||||
makeSquare: false,
|
||||
cut: {
|
||||
left: Math.round(
|
||||
Math.max(0, person.sampleRegion.box.left - margin.x / 2)
|
||||
Math.max(0, faceRegion.box.left - margin.x / 2)
|
||||
),
|
||||
top: Math.round(
|
||||
Math.max(0, person.sampleRegion.box.top - margin.y / 2)
|
||||
Math.max(0, faceRegion.box.top - margin.y / 2)
|
||||
),
|
||||
width: person.sampleRegion.box.width + margin.x,
|
||||
height: person.sampleRegion.box.height + margin.y,
|
||||
width: faceRegion.box.width + margin.x,
|
||||
height: faceRegion.box.height + margin.y,
|
||||
},
|
||||
useLanczos3: Config.Server.Media.Thumbnail.useLanczos3,
|
||||
quality: Config.Server.Media.Thumbnail.quality,
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
* This version indicates that the SQL sql/entities/*Entity.ts files got changed and the db needs to be recreated
|
||||
* This version indicates that the sql/entities/*Entity.ts files got changed and the db needs to be recreated
|
||||
*/
|
||||
export const DataStructureVersion = 28;
|
||||
export const DataStructureVersion = 30;
|
||||
|
@ -1,9 +1,3 @@
|
||||
import { FaceRegionEntry } from '../../backend/model/database/sql/enitites/FaceRegionEntry';
|
||||
|
||||
export interface PersonWithSampleRegion extends PersonDTO {
|
||||
sampleRegion: FaceRegionEntry;
|
||||
}
|
||||
|
||||
export interface PersonDTO {
|
||||
id: number;
|
||||
name: string;
|
||||
|
@ -14,6 +14,7 @@ import {Utils} from '../../src/common/Utils';
|
||||
import {TestHelper} from '../TestHelper';
|
||||
import {VideoDTO} from '../../src/common/entities/VideoDTO';
|
||||
import {PhotoDTO} from '../../src/common/entities/PhotoDTO';
|
||||
import {Logger} from '../../src/backend/Logger';
|
||||
|
||||
declare let describe: any;
|
||||
const savedDescribe = describe;
|
||||
@ -27,15 +28,18 @@ class IndexingManagerTest extends IndexingManager {
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
public async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<ParentDirectoryDTO> {
|
||||
return super.selectParentDir(connection, directoryName, directoryParent);
|
||||
public async getDirIdAndTime(connection: Connection, directoryName: string, directoryParent: string) {
|
||||
return super.getDirIdAndTime(connection, directoryName, directoryParent);
|
||||
}
|
||||
|
||||
public async fillParentDir(connection: Connection, dir: ParentDirectoryDTO): Promise<void> {
|
||||
return super.fillParentDir(connection, dir);
|
||||
public async getParentDirFromId(connection: Connection, dir: number): Promise<ParentDirectoryDTO> {
|
||||
return super.getParentDirFromId(connection, dir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const LOG_TAG = 'DBTestHelper';
|
||||
|
||||
export class DBTestHelper {
|
||||
|
||||
static enable = {
|
||||
@ -44,7 +48,7 @@ export class DBTestHelper {
|
||||
mysql: process.env.TEST_MYSQL !== 'false'
|
||||
};
|
||||
public static readonly savedDescribe = savedDescribe;
|
||||
tempDir: string;
|
||||
public tempDir: string;
|
||||
public readonly testGalleyEntities: {
|
||||
dir: ParentDirectoryDTO,
|
||||
subDir: SubDirectoryDTO,
|
||||
@ -101,7 +105,6 @@ export class DBTestHelper {
|
||||
const helper = new DBTestHelper(DatabaseType.mysql);
|
||||
savedDescribe('mysql', function(): void {
|
||||
this.timeout(99999999); // hint for the test environment
|
||||
// @ts-ignore
|
||||
return tests(helper);
|
||||
});
|
||||
}
|
||||
@ -132,14 +135,15 @@ export class DBTestHelper {
|
||||
}
|
||||
|
||||
const gm = new GalleryManagerTest();
|
||||
const dir = await gm.selectParentDir(connection, directory.name, path.join(path.dirname('.'), path.sep));
|
||||
await gm.fillParentDir(connection, dir);
|
||||
|
||||
const dir = await gm.getParentDirFromId(connection,
|
||||
(await gm.getDirIdAndTime(connection, directory.name, path.join(path.dirname('.'), path.sep))).id);
|
||||
|
||||
const populateDir = async (d: DirectoryBaseDTO) => {
|
||||
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, d.directories[i] as any);
|
||||
d.directories[i] = await gm.getParentDirFromId(connection,
|
||||
(await gm.getDirIdAndTime(connection, d.directories[i].name,
|
||||
path.join(DiskMangerWorker.pathFromParent(d), path.sep))).id);
|
||||
await populateDir(d.directories[i]);
|
||||
}
|
||||
};
|
||||
@ -184,10 +188,15 @@ export class DBTestHelper {
|
||||
this.testGalleyEntities.subDir = this.testGalleyEntities.dir.directories[0];
|
||||
this.testGalleyEntities.subDir2 = this.testGalleyEntities.dir.directories[1];
|
||||
this.testGalleyEntities.p = (this.testGalleyEntities.dir.media.filter(m => m.name === this.testGalleyEntities.p.name)[0] as any);
|
||||
this.testGalleyEntities.p.directory = this.testGalleyEntities.dir;
|
||||
this.testGalleyEntities.p2 = (this.testGalleyEntities.dir.media.filter(m => m.name === this.testGalleyEntities.p2.name)[0] as any);
|
||||
this.testGalleyEntities.p2.directory = this.testGalleyEntities.dir;
|
||||
this.testGalleyEntities.v = (this.testGalleyEntities.dir.media.filter(m => m.name === this.testGalleyEntities.v.name)[0] as any);
|
||||
this.testGalleyEntities.v.directory = this.testGalleyEntities.dir;
|
||||
this.testGalleyEntities.p3 = (this.testGalleyEntities.dir.directories[0].media[0] as any);
|
||||
this.testGalleyEntities.p3.directory = this.testGalleyEntities.dir.directories[0];
|
||||
this.testGalleyEntities.p4 = (this.testGalleyEntities.dir.directories[1].media[0] as any);
|
||||
this.testGalleyEntities.p2.directory = this.testGalleyEntities.dir.directories[1];
|
||||
}
|
||||
|
||||
private async initMySQL(): Promise<void> {
|
||||
@ -195,20 +204,20 @@ export class DBTestHelper {
|
||||
}
|
||||
|
||||
private async resetMySQL(): Promise<void> {
|
||||
await ObjectManagers.reset();
|
||||
Config.Server.Database.type = DatabaseType.mysql;
|
||||
Config.Server.Database.mysql.database = 'pigallery2_test';
|
||||
Logger.debug(LOG_TAG, 'resetting up mysql');
|
||||
await this.clearUpMysql();
|
||||
const conn = await SQLConnection.getConnection();
|
||||
await conn.query('DROP DATABASE IF EXISTS ' + conn.options.database);
|
||||
await conn.query('CREATE DATABASE IF NOT EXISTS ' + conn.options.database);
|
||||
await SQLConnection.close();
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
}
|
||||
|
||||
private async clearUpMysql(): Promise<void> {
|
||||
Logger.debug(LOG_TAG, 'clearing up mysql');
|
||||
await ObjectManagers.reset();
|
||||
Config.Server.Database.type = DatabaseType.mysql;
|
||||
Config.Server.Database.mysql.database = 'pigallery2_test';
|
||||
await fs.promises.rm(this.tempDir, {recursive: true, force: true});
|
||||
const conn = await SQLConnection.getConnection();
|
||||
await conn.query('DROP DATABASE IF EXISTS ' + conn.options.database);
|
||||
await SQLConnection.close();
|
||||
@ -219,15 +228,13 @@ export class DBTestHelper {
|
||||
}
|
||||
|
||||
private async resetSQLite(): Promise<void> {
|
||||
Config.Server.Database.type = DatabaseType.sqlite;
|
||||
Config.Server.Database.dbFolder = this.tempDir;
|
||||
ProjectPath.reset();
|
||||
await ObjectManagers.reset();
|
||||
await fs.promises.rm(this.tempDir, {recursive: true, force: true});
|
||||
Logger.debug(LOG_TAG, 'resetting sqlite');
|
||||
await this.clearUpSQLite();
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
}
|
||||
|
||||
private async clearUpSQLite(): Promise<void> {
|
||||
Logger.debug(LOG_TAG, 'clearing up sqlite');
|
||||
Config.Server.Database.type = DatabaseType.sqlite;
|
||||
Config.Server.Database.dbFolder = this.tempDir;
|
||||
ProjectPath.reset();
|
||||
|
@ -1,9 +1,6 @@
|
||||
import {DBTestHelper} from '../../../DBTestHelper';
|
||||
import {ParentDirectoryDTO, SubDirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
||||
import {TestHelper} from '../../../../TestHelper';
|
||||
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
||||
import {PhotoDTO} from '../../../../../src/common/entities/PhotoDTO';
|
||||
import {VideoDTO} from '../../../../../src/common/entities/VideoDTO';
|
||||
import {AlbumManager} from '../../../../../src/backend/model/database/sql/AlbumManager';
|
||||
import {SearchQueryTypes, TextSearch} from '../../../../../src/common/entities/SearchQueryDTO';
|
||||
import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection';
|
||||
@ -13,7 +10,9 @@ import {MediaDTO} from '../../../../../src/common/entities/MediaDTO';
|
||||
import {SavedSearchDTO} from '../../../../../src/common/entities/album/SavedSearchDTO';
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const chai = require('chai');
|
||||
|
||||
chai.use(deepEqualInAnyOrder);
|
||||
|
@ -6,7 +6,9 @@ import {DirectoryEntity} from '../../../../../src/backend/model/database/sql/eni
|
||||
import {ParentDirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
||||
import {Connection} from 'typeorm';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const chai = require('chai');
|
||||
|
||||
chai.use(deepEqualInAnyOrder);
|
||||
@ -22,15 +24,13 @@ describe = DBTestHelper.describe();
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
|
||||
public async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<ParentDirectoryDTO> {
|
||||
return super.selectParentDir(connection, directoryName, directoryParent);
|
||||
public async getDirIdAndTime(connection: Connection, directoryName: string, directoryParent: string) {
|
||||
return super.getDirIdAndTime(connection, directoryName, directoryParent);
|
||||
}
|
||||
|
||||
public async fillParentDir(connection: Connection, dir: ParentDirectoryDTO): Promise<void> {
|
||||
return super.fillParentDir(connection, dir);
|
||||
public async getParentDirFromId(connection: Connection, dir: number): Promise<ParentDirectoryDTO> {
|
||||
return super.getParentDirFromId(connection, dir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe('GalleryManager', (sqlHelper: DBTestHelper) => {
|
||||
|
@ -5,7 +5,6 @@ import {GalleryManager} from '../../../../../src/backend/model/database/sql/Gall
|
||||
import {DirectoryBaseDTO, DirectoryDTOUtils, ParentDirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
||||
import {TestHelper} from '../../../../TestHelper';
|
||||
import {Connection} from 'typeorm';
|
||||
import {DirectoryEntity} from '../../../../../src/backend/model/database/sql/enitites/DirectoryEntity';
|
||||
import {Utils} from '../../../../../src/common/Utils';
|
||||
import {MediaDTO} from '../../../../../src/common/entities/MediaDTO';
|
||||
import {FileDTO} from '../../../../../src/common/entities/FileDTO';
|
||||
@ -13,7 +12,7 @@ import {IndexingManager} from '../../../../../src/backend/model/database/sql/Ind
|
||||
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
||||
import {DBTestHelper} from '../../../DBTestHelper';
|
||||
import {DiskMangerWorker} from '../../../../../src/backend/model/threading/DiskMangerWorker';
|
||||
import {ReIndexingSensitivity} from '../../../../../src/common/config/private/PrivateConfig';
|
||||
import {ReIndexingSensitivity, SQLLogLevel} from '../../../../../src/common/config/private/PrivateConfig';
|
||||
import {SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes} from '../../../../../src/common/entities/SearchQueryDTO';
|
||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||
import * as path from 'path';
|
||||
@ -31,15 +30,13 @@ const {expect} = chai;
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
|
||||
public async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<ParentDirectoryDTO> {
|
||||
return super.selectParentDir(connection, directoryName, directoryParent);
|
||||
public async getDirIdAndTime(connection: Connection, directoryName: string, directoryParent: string) {
|
||||
return super.getDirIdAndTime(connection, directoryName, directoryParent);
|
||||
}
|
||||
|
||||
public async fillParentDir(connection: Connection, dir: ParentDirectoryDTO): Promise<void> {
|
||||
return super.fillParentDir(connection, dir);
|
||||
public async getParentDirFromId(connection: Connection, dir: number): Promise<ParentDirectoryDTO> {
|
||||
return super.getParentDirFromId(connection, dir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IndexingManagerTest extends IndexingManager {
|
||||
@ -149,8 +146,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
|
||||
@ -175,14 +172,16 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
'.'
|
||||
);
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, directoryPath.name,
|
||||
directoryPath.parent);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, directoryPath.name, directoryPath.parent)).id);
|
||||
|
||||
|
||||
expect(selected?.media?.length)
|
||||
.to.be.greaterThan(0);
|
||||
const tmpDir = path.join(__dirname, '/../../../tmp/rnd5sdf_emptyDir');
|
||||
if (!fs.existsSync(sqlHelper.tempDir)) {
|
||||
fs.mkdirSync(sqlHelper.tempDir);
|
||||
}
|
||||
const tmpDir = path.join(sqlHelper.tempDir, '/rnd5sdf_emptyDir');
|
||||
fs.mkdirSync(tmpDir);
|
||||
ProjectPath.ImageFolder = tmpDir;
|
||||
let notFailed = false;
|
||||
@ -214,8 +213,9 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -244,8 +244,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
{
|
||||
const selected = await gm.selectParentDir(conn, parent1.name, parent1.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent1.name, parent1.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -254,8 +254,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
.to.deep.equalInAnyOrder(Utils.removeNullOrEmptyObj(indexifyReturn(parent1)));
|
||||
}
|
||||
{
|
||||
const selected = await gm.selectParentDir(conn, parent2.name, parent2.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent2.name, parent2.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -277,8 +277,9 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
|
||||
@ -290,8 +291,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
it('should select preview', async () => {
|
||||
const selectDirectory = async (gmTest: GalleryManagerTest, dir: DirectoryBaseDTO): Promise<ParentDirectoryDTO> => {
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gmTest.selectParentDir(conn, dir.name, dir.path);
|
||||
await gmTest.fillParentDir(conn, selected);
|
||||
const selected = await gmTest.getParentDirFromId(conn,
|
||||
(await gmTest.getDirIdAndTime(conn, dir.name, dir.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -369,8 +370,9 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -407,8 +409,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -437,8 +439,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -473,8 +475,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -499,8 +501,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
Config.Client.MetaFile.markdown = false;
|
||||
Config.Client.MetaFile.pg2conf = false;
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
delete parent.metaFile;
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
@ -530,8 +532,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(subDir) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, subDir.name, subDir.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, subDir.name, subDir.path)).id);
|
||||
|
||||
// subDir.isPartial = true;
|
||||
// delete subDir.directories;
|
||||
@ -570,8 +572,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
|
||||
await Promise.all([s1, s2, s3]);
|
||||
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -595,8 +597,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, parent.name, parent.path)).id);
|
||||
|
||||
DirectoryDTOUtils.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
@ -604,7 +606,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
.to.deep.equal(Utils.removeNullOrEmptyObj(indexifyReturn(parent)));
|
||||
|
||||
await im.resetDB();
|
||||
const selectReset = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
const selectReset = await gm.getDirIdAndTime(conn, parent.name, parent.path);
|
||||
expect(selectReset).to.deep.equal(null);
|
||||
});
|
||||
|
||||
@ -627,8 +629,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
DirectoryDTOUtils.removeReferences(parent);
|
||||
await im.saveToDB(subDir as ParentDirectoryDTO);
|
||||
|
||||
|
||||
const selected = await gm.selectParentDir(conn, subDir.name, subDir.path);
|
||||
const selected = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, subDir.name, subDir.path)).id);
|
||||
expect(selected.media.length).to.equal(subDir.media.length);
|
||||
}) as any).timeout(40000);
|
||||
|
||||
@ -661,11 +663,11 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
// @ts-ignore
|
||||
fs.statSync = () => ({ctime: new Date(dirTime), mtime: new Date(dirTime)});
|
||||
const gm = new GalleryManagerTest();
|
||||
gm.selectParentDir = (connection: Connection, directoryName: string, directoryParent: string) => {
|
||||
gm.getDirIdAndTime = () => {
|
||||
return Promise.resolve(indexedTime as any);
|
||||
};
|
||||
gm.fillParentDir = (connection: Connection, dir: DirectoryEntity) => {
|
||||
return Promise.resolve();
|
||||
gm.getParentDirFromId = ():Promise<ParentDirectoryDTO> => {
|
||||
return Promise.resolve(indexedTime) as unknown as Promise<ParentDirectoryDTO>;
|
||||
};
|
||||
|
||||
ObjectManagers.getInstance().IndexingManager.indexDirectory = (...args) => {
|
||||
|
@ -4,7 +4,6 @@ import {DBTestHelper} from '../../../DBTestHelper';
|
||||
import {TestHelper} from '../../../../TestHelper';
|
||||
import {PhotoDTO} from '../../../../../src/common/entities/PhotoDTO';
|
||||
import {Utils} from '../../../../../src/common/Utils';
|
||||
import {PersonWithSampleRegion} from '../../../../../src/common/entities/PersonDTO';
|
||||
import {ParentDirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
||||
import {VideoDTO} from '../../../../../src/common/entities/VideoDTO';
|
||||
import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection';
|
||||
@ -18,6 +17,7 @@ declare const before: any;
|
||||
declare const it: any;
|
||||
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
describe = DBTestHelper.describe();
|
||||
|
||||
describe('PersonManager', (sqlHelper: DBTestHelper) => {
|
||||
@ -30,7 +30,7 @@ describe('PersonManager', (sqlHelper: DBTestHelper) => {
|
||||
let p2: PhotoDTO;
|
||||
let pFaceLess: PhotoDTO;
|
||||
|
||||
let savedPerson: PersonWithSampleRegion[] = [];
|
||||
let savedPerson: PersonEntry[] = [];
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
await sqlHelper.initDB();
|
||||
@ -73,7 +73,7 @@ describe('PersonManager', (sqlHelper: DBTestHelper) => {
|
||||
person.count = 1;
|
||||
expect(selected).to.deep.equal(person);
|
||||
|
||||
expect((await pm.get('Boba Fett') as PersonWithSampleRegion).sampleRegion.media.name).to.deep.equal(p.name);
|
||||
expect((await pm.get('Boba Fett') as PersonEntry).sampleRegion.media.name).to.deep.equal(p.name);
|
||||
});
|
||||
|
||||
|
||||
|
@ -17,7 +17,9 @@ import {Utils} from '../../../../../src/common/Utils';
|
||||
import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection';
|
||||
import {DirectoryEntity} from '../../../../../src/backend/model/database/sql/enitites/DirectoryEntity';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const chai = require('chai');
|
||||
|
||||
chai.use(deepEqualInAnyOrder);
|
||||
@ -48,12 +50,12 @@ class SearchManagerTest extends SearchManager {
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
public async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<ParentDirectoryDTO> {
|
||||
return super.selectParentDir(connection, directoryName, directoryParent);
|
||||
public async getDirIdAndTime(connection: Connection, directoryName: string, directoryParent: string) {
|
||||
return super.getDirIdAndTime(connection, directoryName, directoryParent);
|
||||
}
|
||||
|
||||
public async fillParentDir(connection: Connection, dir: ParentDirectoryDTO): Promise<void> {
|
||||
return super.fillParentDir(connection, dir);
|
||||
public async getParentDirFromId(connection: Connection, dir: number): Promise<ParentDirectoryDTO> {
|
||||
return super.getParentDirFromId(connection, dir);
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +68,6 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
|
||||
* |- v
|
||||
* |- p
|
||||
* |- p2
|
||||
* |- gpx
|
||||
* |-> subDir2
|
||||
* |- p4
|
||||
*/
|
||||
@ -79,7 +80,6 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
|
||||
let p2: PhotoDTO;
|
||||
let pFaceLess: PhotoDTO;
|
||||
let p4: PhotoDTO;
|
||||
let gpx: FileDTO;
|
||||
|
||||
|
||||
const setUpTestGallery = async (): Promise<void> => {
|
||||
@ -94,7 +94,6 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
|
||||
p2.metadata.creationDate = 20000;
|
||||
v = TestHelper.getVideoEntry1(subDir);
|
||||
v.metadata.creationDate = 500;
|
||||
gpx = TestHelper.getRandomizedGPXEntry(subDir);
|
||||
const pFaceLessTmp = TestHelper.getPhotoEntry3(subDir);
|
||||
pFaceLessTmp.metadata.rating = 0;
|
||||
pFaceLessTmp.metadata.creationDate = 400000;
|
||||
@ -108,11 +107,15 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
|
||||
subDir = dir.directories[0];
|
||||
subDir2 = dir.directories[1];
|
||||
p = (subDir.media.filter(m => m.name === p.name)[0] as any);
|
||||
p.directory = subDir;
|
||||
p2 = (subDir.media.filter(m => m.name === p2.name)[0] as any);
|
||||
gpx = (subDir.metaFile[0] as any);
|
||||
p2.directory = subDir;
|
||||
v = (subDir.media.filter(m => m.name === v.name)[0] as any);
|
||||
v.directory = subDir;
|
||||
pFaceLess = (subDir.media.filter(m => m.name === pFaceLessTmp.name)[0] as any);
|
||||
pFaceLess.directory = subDir;
|
||||
p4 = (subDir2.media[0] as any);
|
||||
p4.directory = subDir2;
|
||||
};
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
@ -274,8 +277,9 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
|
||||
.set({validPreview: false, preview: null}).execute();
|
||||
expect((await selectDir()).preview).to.equal(null);
|
||||
|
||||
const res = await gm.selectParentDir(conn, dir.name, dir.path);
|
||||
await gm.fillParentDir(conn, res);
|
||||
|
||||
const res = await gm.getParentDirFromId(conn,
|
||||
(await gm.getDirIdAndTime(conn, dir.name, dir.path)).id);
|
||||
subdir = await selectDir();
|
||||
expect(subdir.validPreview).to.equal(true);
|
||||
expect(subdir.preview.id).to.equal(p2.id);
|
||||
|
@ -34,7 +34,9 @@ import {Config} from '../../../../../src/common/config/private/Config';
|
||||
import {SearchQueryParser} from '../../../../../src/common/SearchQueryParser';
|
||||
import {FileDTO} from '../../../../../src/common/entities/FileDTO';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const chai = require('chai');
|
||||
|
||||
chai.use(deepEqualInAnyOrder);
|
||||
@ -65,12 +67,12 @@ class SearchManagerTest extends SearchManager {
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
public async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<ParentDirectoryDTO> {
|
||||
return super.selectParentDir(connection, directoryName, directoryParent);
|
||||
public async getDirIdAndTime(connection: Connection, directoryName: string, directoryParent: string) {
|
||||
return super.getDirIdAndTime(connection, directoryName, directoryParent);
|
||||
}
|
||||
|
||||
public async fillParentDir(connection: Connection, dir: ParentDirectoryDTO): Promise<void> {
|
||||
return super.fillParentDir(connection, dir);
|
||||
public async getParentDirFromId(connection: Connection, dir: number): Promise<ParentDirectoryDTO> {
|
||||
return super.getParentDirFromId(connection, dir);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,11 +118,17 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
|
||||
subDir = dir.directories[0];
|
||||
subDir2 = dir.directories[1];
|
||||
p = (dir.media.filter(m => m.name === p.name)[0] as any);
|
||||
p.directory = dir;
|
||||
p2 = (dir.media.filter(m => m.name === p2.name)[0] as any);
|
||||
p2.directory = dir;
|
||||
gpx = (dir.metaFile[0] as any);
|
||||
gpx.directory = dir;
|
||||
v = (dir.media.filter(m => m.name === v.name)[0] as any);
|
||||
v.directory = dir;
|
||||
p4 = (dir.directories[1].media[0] as any);
|
||||
p4.directory = dir.directories[1];
|
||||
pFaceLess = (dir.directories[0].media[0] as any);
|
||||
pFaceLess.directory = dir.directories[0];
|
||||
};
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
@ -1149,7 +1157,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* flattenSameOfQueries converts converts some-of querries to AND and OR queries
|
||||
* flattenSameOfQueries converts some-of queries to AND and OR queries
|
||||
* E.g.:
|
||||
* 2-of:(A B C) to (A and (B or C)) or (B and C)
|
||||
* this tests makes sure that all queries has at least 2 constraints
|
||||
@ -1169,13 +1177,8 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
// its an or
|
||||
// it's an OR
|
||||
const lengths = (q as SearchListQuery).list.map(l => shortestDepth(l)).sort();
|
||||
|
||||
if (lengths[0] !== lengths[lengths.length - 1]) {
|
||||
for (const l of (q as SearchListQuery).list) {
|
||||
}
|
||||
}
|
||||
return lengths[0];
|
||||
}
|
||||
return 1;
|
||||
|
Loading…
Reference in New Issue
Block a user