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

improving advanced search

This commit is contained in:
Patrik J. Braun 2021-01-16 23:37:14 +01:00
parent 9a923aa8ab
commit e643ee2ed1
5 changed files with 375 additions and 63 deletions

View File

@ -15,17 +15,19 @@ import {
DateSearch,
DistanceSearch,
OrientationSearch,
OrientationSearchTypes,
ORSearchQuery,
RatingSearch,
ResolutionSearch,
SearchListQuery,
SearchQueryDTO,
SearchQueryTypes,
SomeOfSearchQuery,
TextSearch,
TextSearchQueryTypes
} from '../../../../common/entities/SearchQueryDTO';
import {GalleryManager} from './GalleryManager';
import {ObjectManagers} from '../../ObjectManagers';
import {Utils} from '../../../../common/Utils';
export class SearchManager implements ISearchManager {
@ -145,6 +147,7 @@ export class SearchManager implements ISearchManager {
async aSearch(query: SearchQueryDTO) {
query = this.flattenSameOfQueries(query);
console.log(JSON.stringify(query, null, 4));
query = await this.getGPSData(query);
const connection = await SQLConnection.getConnection();
@ -367,10 +370,17 @@ export class SearchManager implements ISearchManager {
textParam['minLat' + paramCounter.value] = minLat;
textParam['maxLon' + paramCounter.value] = maxLon;
textParam['minLon' + paramCounter.value] = minLon;
q.where(`media.metadata.positionData.GPSData.latitude < :maxLat${paramCounter.value}`, textParam);
q.andWhere(`media.metadata.positionData.GPSData.latitude > :minLat${paramCounter.value}`, textParam);
q.andWhere(`media.metadata.positionData.GPSData.longitude < :maxLon${paramCounter.value}`, textParam);
q.andWhere(`media.metadata.positionData.GPSData.longitude > :minLon${paramCounter.value}`, textParam);
if (!(<DistanceSearch>query).negate) {
q.where(`media.metadata.positionData.GPSData.latitude < :maxLat${paramCounter.value}`, textParam);
q.andWhere(`media.metadata.positionData.GPSData.latitude > :minLat${paramCounter.value}`, textParam);
q.andWhere(`media.metadata.positionData.GPSData.longitude < :maxLon${paramCounter.value}`, textParam);
q.andWhere(`media.metadata.positionData.GPSData.longitude > :minLon${paramCounter.value}`, textParam);
} else {
q.where(`media.metadata.positionData.GPSData.latitude > :maxLat${paramCounter.value}`, textParam);
q.orWhere(`media.metadata.positionData.GPSData.latitude < :minLat${paramCounter.value}`, textParam);
q.orWhere(`media.metadata.positionData.GPSData.longitude > :maxLon${paramCounter.value}`, textParam);
q.orWhere(`media.metadata.positionData.GPSData.longitude < :minLon${paramCounter.value}`, textParam);
}
return q;
});
@ -379,17 +389,22 @@ export class SearchManager implements ISearchManager {
if (typeof (<DateSearch>query).before === 'undefined' && typeof (<DateSearch>query).after === 'undefined') {
throw new Error('Invalid search query: Date Query should contain before or after value');
}
if (typeof (<DateSearch>query).before !== 'undefined') {
const textParam: any = {};
textParam['before' + paramCounter.value] = (<DateSearch>query).before;
q.where(`media.metadata.creationDate <= :before${paramCounter.value}`, textParam);
}
const whereFN = (<TextSearch>query).negate ? 'orWhere' : 'andWhere';
const relation = (<TextSearch>query).negate ? '<' : '>=';
const relationRev = (<TextSearch>query).negate ? '>' : '<=';
if (typeof (<DateSearch>query).after !== 'undefined') {
const textParam: any = {};
textParam['after' + paramCounter.value] = (<DateSearch>query).after;
q.andWhere(`media.metadata.creationDate >= :after${paramCounter.value}`, textParam);
q.where(`media.metadata.creationDate ${relation} :after${paramCounter.value}`, textParam);
}
if (typeof (<DateSearch>query).before !== 'undefined') {
const textParam: any = {};
textParam['before' + paramCounter.value] = (<DateSearch>query).before;
q[whereFN](`media.metadata.creationDate ${relationRev} :before${paramCounter.value}`, textParam);
}
paramCounter.value++;
return q;
});
@ -399,16 +414,20 @@ export class SearchManager implements ISearchManager {
if (typeof (<RatingSearch>query).min === 'undefined' && typeof (<RatingSearch>query).max === 'undefined') {
throw new Error('Invalid search query: Rating Query should contain min or max value');
}
const whereFN = (<TextSearch>query).negate ? 'orWhere' : 'andWhere';
const relation = (<TextSearch>query).negate ? '<' : '>=';
const relationRev = (<TextSearch>query).negate ? '>' : '<=';
if (typeof (<RatingSearch>query).min !== 'undefined') {
const textParam: any = {};
textParam['min' + paramCounter.value] = (<RatingSearch>query).min;
q.where(`media.metadata.rating >= :min${paramCounter.value}`, textParam);
q.where(`media.metadata.rating ${relation} :min${paramCounter.value}`, textParam);
}
if (typeof (<RatingSearch>query).max !== 'undefined') {
const textParam: any = {};
textParam['max' + paramCounter.value] = (<RatingSearch>query).max;
q.andWhere(`media.metadata.rating <= :max${paramCounter.value}`, textParam);
q[whereFN](`media.metadata.rating ${relationRev} :max${paramCounter.value}`, textParam);
}
paramCounter.value++;
return q;
@ -419,16 +438,20 @@ export class SearchManager implements ISearchManager {
if (typeof (<ResolutionSearch>query).min === 'undefined' && typeof (<ResolutionSearch>query).max === 'undefined') {
throw new Error('Invalid search query: Rating Query should contain min or max value');
}
const whereFN = (<TextSearch>query).negate ? 'orWhere' : 'andWhere';
const relation = (<TextSearch>query).negate ? '<' : '>=';
const relationRev = (<TextSearch>query).negate ? '>' : '<=';
if (typeof (<ResolutionSearch>query).min !== 'undefined') {
const textParam: any = {};
textParam['min' + paramCounter.value] = (<RatingSearch>query).min * 1000 * 1000;
q.where(`media.metadata.size.width * media.metadata.size.height >= :min${paramCounter.value}`, textParam);
q.where(`media.metadata.size.width * media.metadata.size.height ${relation} :min${paramCounter.value}`, textParam);
}
if (typeof (<ResolutionSearch>query).max !== 'undefined') {
const textParam: any = {};
textParam['max' + paramCounter.value] = (<RatingSearch>query).max * 1000 * 1000;
q.andWhere(`media.metadata.size.width * media.metadata.size.height <= :max${paramCounter.value}`, textParam);
q[whereFN](`media.metadata.size.width * media.metadata.size.height ${relationRev} :max${paramCounter.value}`, textParam);
}
paramCounter.value++;
return q;
@ -436,11 +459,10 @@ export class SearchManager implements ISearchManager {
case SearchQueryTypes.orientation:
return new Brackets(q => {
if ((<OrientationSearch>query).orientation === OrientationSearchTypes.landscape) {
if ((<OrientationSearch>query).landscape) {
q.where('media.metadata.size.width >= media.metadata.size.height');
}
if ((<OrientationSearch>query).orientation === OrientationSearchTypes.portrait) {
q.andWhere('media.metadata.size.width <= media.metadata.size.height');
} else {
q.where('media.metadata.size.width <= media.metadata.size.height');
}
paramCounter.value++;
return q;
@ -458,6 +480,11 @@ export class SearchManager implements ISearchManager {
return (<TextSearch>query).matchType === TextSearchQueryTypes.exact_match ? str : `%${str}%`;
};
const LIKE = (<TextSearch>query).negate ? 'NOT LIKE' : 'LIKE';
// if the expression is negated, we use AND instead of OR as nowhere should that match
const whereFN = (<TextSearch>query).negate ? 'andWhere' : 'orWhere';
const whereFNRev = (<TextSearch>query).negate ? 'orWhere' : 'andWhere';
const textParam: any = {};
paramCounter.value++;
textParam['text' + paramCounter.value] = createMatchString((<TextSearch>query).text);
@ -468,17 +495,17 @@ export class SearchManager implements ISearchManager {
textParam['fullPath' + paramCounter.value] = createMatchString(dirPathStr);
q.orWhere(`directory.path LIKE :fullPath${paramCounter.value} COLLATE utf8_general_ci`,
q[whereFN](`directory.path ${LIKE} :fullPath${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
const directoryPath = GalleryManager.parseRelativeDirePath(dirPathStr);
q.orWhere(new Brackets(dq => {
q[whereFN](new Brackets(dq => {
textParam['dirName' + paramCounter.value] = createMatchString(directoryPath.name);
dq.where(`directory.name LIKE :dirName${paramCounter.value} COLLATE utf8_general_ci`,
dq[whereFNRev](`directory.name ${LIKE} :dirName${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
if (dirPathStr.includes('/')) {
textParam['parentName' + paramCounter.value] = createMatchString(directoryPath.parent);
dq.andWhere(`directory.path LIKE :parentName${paramCounter.value} COLLATE utf8_general_ci`,
dq[whereFNRev](`directory.path ${LIKE} :parentName${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
}
return dq;
@ -486,29 +513,39 @@ export class SearchManager implements ISearchManager {
}
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.file_name) {
q.orWhere(`media.name LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
q[whereFN](`media.name ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
}
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.caption) {
q.orWhere(`media.metadata.caption LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
q[whereFN](`media.metadata.caption ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
}
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.person) {
q.orWhere(`person.name LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
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.orWhere(`media.metadata.positionData.country LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
q[whereFN](`media.metadata.positionData.country ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam)
.orWhere(`media.metadata.positionData.state LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam)
.orWhere(`media.metadata.positionData.city LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
[whereFN](`media.metadata.positionData.state ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam)
[whereFN](`media.metadata.positionData.city ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
}
if (query.type === SearchQueryTypes.any_text || query.type === SearchQueryTypes.keyword) {
q.orWhere(`media.metadata.keywords LIKE :text${paramCounter.value} COLLATE utf8_general_ci`,
q[whereFN](`media.metadata.keywords ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
textParam);
}
return q;
@ -516,6 +553,41 @@ export class SearchManager implements ISearchManager {
}
private flattenSameOfQueries(query: SearchQueryDTO): SearchQueryDTO {
switch (query.type) {
case SearchQueryTypes.AND:
case SearchQueryTypes.OR:
return <SearchListQuery>{
type: query.type,
list: (<SearchListQuery>query).list.map(q => this.flattenSameOfQueries(q))
};
case SearchQueryTypes.SOME_OF:
const someOfQ = <SomeOfSearchQuery>query;
someOfQ.min = someOfQ.min || 1;
if (someOfQ.min === 1) {
return this.flattenSameOfQueries(<ORSearchQuery>{
type: SearchQueryTypes.OR,
list: (<SearchListQuery>someOfQ).list
});
}
if (someOfQ.min === (<SearchListQuery>query).list.length) {
return this.flattenSameOfQueries(<ANDSearchQuery>{
type: SearchQueryTypes.AND,
list: (<SearchListQuery>someOfQ).list
});
}
const combinations: SearchQueryDTO[][] = Utils.getAnyX(someOfQ.min, (<SearchListQuery>query).list);
return this.flattenSameOfQueries(<ORSearchQuery>{
type: SearchQueryTypes.OR,
list: combinations.map(c => <ANDSearchQuery>{
type: SearchQueryTypes.AND, list: c
})
});
}
return query;
}

View File

@ -240,4 +240,24 @@ export class Utils {
(value <= 3.402823466 * E && value >= 1.175494351 * nE));
}
public static getAnyX(num: number, arr: any[], start = 0): any[][] {
if (num <= 0 || num > arr.length || start >= arr.length) {
return [];
}
if (num <= 1) {
return arr.slice(start).map(e => [e]);
}
if (num === arr.length - start) {
return [arr.slice(start)];
}
const ret: any[][] = [];
for (let i = start; i < arr.length; ++i) {
Utils.getAnyX(num - 1, arr, i + 1).forEach(a => {
a.push(arr[i]);
ret.push(a);
});
}
return ret;
}
}

View File

@ -24,30 +24,47 @@ export enum TextSearchQueryTypes {
exact_match = 1, like = 2
}
export enum OrientationSearchTypes {
portrait = 1, landscape = 2
export namespace SearchQueryDTO {
export const negate = (query: SearchQueryDTO): SearchQueryDTO => {
switch (query.type) {
case SearchQueryTypes.AND:
query.type = SearchQueryTypes.OR;
(<SearchListQuery>query).list = (<SearchListQuery>query).list.map(q => SearchQueryDTO.negate(q));
return query;
case SearchQueryTypes.OR:
query.type = SearchQueryTypes.AND;
(<SearchListQuery>query).list = (<SearchListQuery>query).list.map(q => SearchQueryDTO.negate(q));
return query;
case SearchQueryTypes.orientation:
(<OrientationSearch>query).landscape = !(<OrientationSearch>query).landscape;
return query;
case SearchQueryTypes.date:
case SearchQueryTypes.rating:
case SearchQueryTypes.resolution:
case SearchQueryTypes.distance:
case SearchQueryTypes.any_text:
case SearchQueryTypes.person:
case SearchQueryTypes.position:
case SearchQueryTypes.keyword:
case SearchQueryTypes.caption:
case SearchQueryTypes.file_name:
case SearchQueryTypes.directory:
(<NegatableSearchQuery>query).negate = !(<NegatableSearchQuery>query).negate;
return query;
case SearchQueryTypes.SOME_OF:
throw new Error('Some of not supported');
}
};
}
export interface SearchQueryDTO {
type: SearchQueryTypes;
}
export interface ANDSearchQuery extends SearchQueryDTO {
type: SearchQueryTypes.AND;
list: SearchQueryDTO[];
}
export interface ORSearchQuery extends SearchQueryDTO {
type: SearchQueryTypes.OR;
list: SearchQueryDTO[];
}
export interface SomeOfSearchQuery extends SearchQueryDTO, RangeSearchQuery {
type: SearchQueryTypes.SOME_OF;
list: NegatableSearchQuery[];
min?: number; // at least this amount of items
max?: number; // maximum this amount of items
}
export interface NegatableSearchQuery extends SearchQueryDTO {
negate?: boolean; // if true negates the expression
@ -58,6 +75,26 @@ export interface RangeSearchQuery extends SearchQueryDTO {
max?: number;
}
export interface SearchListQuery extends SearchQueryDTO {
list: SearchQueryDTO[];
}
export interface ANDSearchQuery extends SearchQueryDTO, SearchListQuery {
type: SearchQueryTypes.AND;
list: SearchQueryDTO[];
}
export interface ORSearchQuery extends SearchQueryDTO, SearchListQuery {
type: SearchQueryTypes.OR;
list: SearchQueryDTO[];
}
export interface SomeOfSearchQuery extends SearchQueryDTO, SearchListQuery {
type: SearchQueryTypes.SOME_OF;
list: NegatableSearchQuery[];
min?: number; // at least this amount of items
}
export interface TextSearch extends NegatableSearchQuery {
type: SearchQueryTypes.any_text |
@ -87,20 +124,20 @@ export interface DateSearch extends NegatableSearchQuery {
before?: number;
}
export interface RatingSearch extends NegatableSearchQuery, RangeSearchQuery {
export interface RatingSearch extends RangeSearchQuery, NegatableSearchQuery {
type: SearchQueryTypes.rating;
min?: number;
max?: number;
}
export interface ResolutionSearch extends NegatableSearchQuery, RangeSearchQuery {
export interface ResolutionSearch extends RangeSearchQuery, NegatableSearchQuery {
type: SearchQueryTypes.resolution;
min?: number; // in megapixels
max?: number; // in megapixels
}
export interface OrientationSearch extends NegatableSearchQuery {
export interface OrientationSearch {
type: SearchQueryTypes.orientation;
orientation: OrientationSearchTypes;
landscape: boolean;
}

View File

@ -9,12 +9,12 @@ import {
DateSearch,
DistanceSearch,
OrientationSearch,
OrientationSearchTypes,
ORSearchQuery,
RatingSearch,
ResolutionSearch,
SearchQueryDTO,
SearchQueryTypes,
SomeOfSearchQuery,
TextSearch,
TextSearchQueryTypes
} from '../../../../../src/common/entities/SearchQueryDTO';
@ -98,10 +98,11 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
const connection = await SQLConnection.getConnection();
ObjectManagers.getInstance().IndexingManager.indexDirectory = () => Promise.resolve(null);
const im = new IndexingManagerTest();
await im.saveToDB(directory);
await im.saveToDB(subDir);
await im.saveToDB(subDir2);
// await im.saveToDB(subDir);
// await im.saveToDB(subDir2);
if (ObjectManagers.getInstance().IndexingManager &&
ObjectManagers.getInstance().IndexingManager.IsSavingInProgress) {
@ -389,7 +390,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
searchText: null,
searchType: null,
directories: [],
media: [p, p2],
media: [p, p2, p4],
metaFile: [],
resultOverflow: false
}));
@ -467,7 +468,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
searchText: null,
searchType: null,
directories: [],
media: [p, p2],
media: [p, p2, p4],
metaFile: [],
resultOverflow: false
}));
@ -487,7 +488,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
searchText: null,
searchType: null,
directories: [],
media: [p, p2],
media: [p, p2, p4],
metaFile: [],
resultOverflow: false
}));
@ -515,6 +516,84 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
});
it('should minimum of', async () => {
const sm = new SearchManager();
let query: SomeOfSearchQuery = <SomeOfSearchQuery>{
type: SearchQueryTypes.SOME_OF,
list: [<TextSearch>{text: 'jpg', type: SearchQueryTypes.file_name},
<TextSearch>{text: 'mp4', type: SearchQueryTypes.file_name}]
};
expect(Utils.clone(await sm.aSearch(query))).to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p, p2, p_faceLess, p4, v],
metaFile: [],
resultOverflow: false
}));
query = <SomeOfSearchQuery>{
type: SearchQueryTypes.SOME_OF,
list: [<TextSearch>{text: 'R2', type: SearchQueryTypes.person},
<TextSearch>{text: 'Anakin', type: SearchQueryTypes.person},
<TextSearch>{text: 'Luke', type: SearchQueryTypes.person}]
};
expect(Utils.clone(await sm.aSearch(query))).to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p, p2, p4],
metaFile: [],
resultOverflow: false
}));
query.min = 2;
expect(Utils.clone(await sm.aSearch(query))).to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p, p2, p4],
metaFile: [],
resultOverflow: false
}));
query.min = 3;
expect(Utils.clone(await sm.aSearch(query))).to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [],
metaFile: [],
resultOverflow: false
}));
query = <SomeOfSearchQuery>{
type: SearchQueryTypes.SOME_OF,
min: 3,
list: [<TextSearch>{text: 'sw', type: SearchQueryTypes.file_name},
<TextSearch>{text: 'R2', type: SearchQueryTypes.person},
<TextSearch>{text: 'Kamino', type: SearchQueryTypes.position},
<TextSearch>{text: 'Han', type: SearchQueryTypes.person}]
};
expect(Utils.clone(await sm.aSearch(query))).to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p2],
metaFile: [],
resultOverflow: false
}));
});
describe('should search text', async () => {
it('as any', async () => {
const sm = new SearchManager();
@ -529,6 +608,17 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<TextSearch>{text: 'sw', negate: true, type: SearchQueryTypes.any_text})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<TextSearch>{text: 'Boba', type: SearchQueryTypes.any_text})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
@ -539,6 +629,28 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<TextSearch>{text: 'Boba', negate: true, type: SearchQueryTypes.any_text})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p2, p_faceLess, p4],
metaFile: [],
resultOverflow: false
}));
// all should have faces
const sRet = await sm.aSearch(<TextSearch>{text: 'Boba', negate: true, type: SearchQueryTypes.any_text});
for (let i = 0; i < sRet.media.length; ++i) {
if (sRet.media[i].id === p_faceLess.id) {
continue;
}
console.log(sRet.media[i]);
expect((<PhotoDTO>sRet.media[i]).metadata.faces).to.be.not.an('undefined');
expect((<PhotoDTO>sRet.media[i]).metadata.faces).to.be.lengthOf.above(1);
}
expect(Utils.clone(await sm.aSearch(<TextSearch>{
text: 'Boba',
type: SearchQueryTypes.any_text,
@ -815,6 +927,21 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<DateSearch>{
before: p.metadata.creationDate,
after: p.metadata.creationDate,
negate: true,
type: SearchQueryTypes.date
})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p2, p_faceLess, p4, v],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<DateSearch>{
before: p.metadata.creationDate + 1000000000,
after: 0, type: SearchQueryTypes.date
@ -854,6 +981,16 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<RatingSearch>{min: 0, max: 5, negate: true, type: SearchQueryTypes.rating})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<RatingSearch>{min: 2, max: 2, type: SearchQueryTypes.rating})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
@ -864,6 +1001,15 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<RatingSearch>{min: 2, max: 2, negate: true, type: SearchQueryTypes.rating})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p, p_faceLess],
metaFile: [],
resultOverflow: false
}));
});
@ -900,6 +1046,16 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<ResolutionSearch>{min: 2, max: 3, negate: true, type: SearchQueryTypes.resolution})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p, v, p4],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<ResolutionSearch>{min: 3, type: SearchQueryTypes.resolution})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
@ -917,7 +1073,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
const sm = new SearchManager();
expect(Utils.clone(await sm.aSearch(<OrientationSearch>{
orientation: OrientationSearchTypes.portrait,
landscape: false,
type: SearchQueryTypes.orientation
})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
@ -930,7 +1086,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
}));
expect(Utils.clone(await sm.aSearch(<OrientationSearch>{
orientation: OrientationSearchTypes.landscape,
landscape: true,
type: SearchQueryTypes.orientation
})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
@ -984,6 +1140,22 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<DistanceSearch>{
from: {GPSData: {latitude: 0, longitude: 0}},
distance: 112 * 10, // number of km per degree = ~111km
negate: true,
type: SearchQueryTypes.distance
})))
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
searchText: null,
searchType: null,
directories: [],
media: [p_faceLess, p4],
metaFile: [],
resultOverflow: false
}));
expect(Utils.clone(await sm.aSearch(<DistanceSearch>{
from: {GPSData: {latitude: 10, longitude: 10}},
distance: 1,

View File

@ -148,6 +148,9 @@ export class TestHelper {
}, <FaceRegion>{
box: {height: 10, width: 10, left: 105, top: 105},
name: 'Arvíztűrő Tükörfúrógép'
}, <FaceRegion>{
box: {height: 10, width: 10, left: 201, top: 201},
name: 'R2-D2'
}] as any[];
return p;
}
@ -178,6 +181,9 @@ export class TestHelper {
}, <FaceRegion>{
box: {height: 10, width: 10, left: 101, top: 101},
name: 'Obivan Kenobi'
}, <FaceRegion>{
box: {height: 10, width: 10, left: 201, top: 201},
name: 'R2-D2'
}] as any[];
return p;
}
@ -224,6 +230,7 @@ export class TestHelper {
p.metadata.creationDate = Date.now() - 4000;
p.metadata.size.height = 3000;
p.metadata.size.width = 2000;
p.metadata.faces = [<FaceRegion>{
box: {height: 10, width: 10, left: 10, top: 10},
name: 'Kylo Ren'
@ -233,7 +240,11 @@ export class TestHelper {
}, <FaceRegion>{
box: {height: 10, width: 10, left: 101, top: 101},
name: 'Obivan Kenobi'
}, <FaceRegion>{
box: {height: 10, width: 10, left: 201, top: 201},
name: 'R2-D2'
}] as any[];
return p;
}