mirror of
https://github.com/xuthus83/pigallery2.git
synced 2024-11-03 21:04:03 +08:00
Fixing parsing errors.
This commit is contained in:
parent
47703b6b84
commit
41292ec4b4
@ -274,9 +274,7 @@ export class SearchManager implements ISearchManager {
|
|||||||
if (typeof (<FromDateSearch>query).value === 'undefined') {
|
if (typeof (<FromDateSearch>query).value === 'undefined') {
|
||||||
throw new Error('Invalid search query: Date Query should contain from value');
|
throw new Error('Invalid search query: Date Query should contain from value');
|
||||||
}
|
}
|
||||||
const whereFN = (<TextSearch>query).negate ? 'orWhere' : 'andWhere';
|
|
||||||
const relation = (<TextSearch>query).negate ? '<' : '>=';
|
const relation = (<TextSearch>query).negate ? '<' : '>=';
|
||||||
const relationRev = (<TextSearch>query).negate ? '>' : '<=';
|
|
||||||
|
|
||||||
const textParam: any = {};
|
const textParam: any = {};
|
||||||
textParam['from' + paramCounter.value] = (<FromDateSearch>query).value;
|
textParam['from' + paramCounter.value] = (<FromDateSearch>query).value;
|
||||||
|
@ -2,7 +2,7 @@ import {GPSMetadata} from './PhotoDTO';
|
|||||||
import {Utils} from '../Utils';
|
import {Utils} from '../Utils';
|
||||||
|
|
||||||
export enum SearchQueryTypes {
|
export enum SearchQueryTypes {
|
||||||
AND = 1, OR, SOME_OF,
|
AND = 1, OR, SOME_OF, UNKNOWN_RELATION = 99999,
|
||||||
|
|
||||||
// non-text metadata
|
// non-text metadata
|
||||||
// |- range types
|
// |- range types
|
||||||
@ -120,17 +120,22 @@ export namespace SearchQueryDTO {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parse = (str: string): SearchQueryDTO => {
|
export const parse = (str: string, implicitOR = true): SearchQueryDTO => {
|
||||||
console.log(str);
|
console.log('parsing: ' + str);
|
||||||
str = str.replace(/\s\s+/g, ' ') // remove double spaces
|
str = str.replace(/\s\s+/g, ' ') // remove double spaces
|
||||||
.replace(/:\s+/g, ':').replace(/\)(?=\S)/g, ') ').trim();
|
.replace(/:\s+/g, ':').replace(/\)(?=\S)/g, ') ').trim();
|
||||||
|
|
||||||
if (str.charAt(0) === '(' && str.charAt(str.length - 1) === ')') {
|
if (str.charAt(0) === '(' && str.charAt(str.length - 1) === ')') {
|
||||||
str = str.slice(1, str.length - 1);
|
str = str.slice(1, str.length - 1);
|
||||||
}
|
}
|
||||||
const fistNonBRSpace = () => {
|
const fistSpace = (start = 0) => {
|
||||||
const bracketIn = [];
|
const bracketIn = [];
|
||||||
for (let i = 0; i < str.length; ++i) {
|
let quotationMark = false;
|
||||||
|
for (let i = start; i < str.length; ++i) {
|
||||||
|
if (str.charAt(i) === '"') {
|
||||||
|
quotationMark = !quotationMark;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (str.charAt(i) === '(') {
|
if (str.charAt(i) === '(') {
|
||||||
bracketIn.push(i);
|
bracketIn.push(i);
|
||||||
continue;
|
continue;
|
||||||
@ -140,7 +145,9 @@ export namespace SearchQueryDTO {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bracketIn.length === 0 && str.charAt(i) === ' ') {
|
if (quotationMark === false &&
|
||||||
|
bracketIn.length === 0 &&
|
||||||
|
str.charAt(i) === ' ') {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,34 +155,41 @@ export namespace SearchQueryDTO {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// tokenize
|
// tokenize
|
||||||
const tokenEnd = fistNonBRSpace();
|
const tokenEnd = fistSpace();
|
||||||
|
|
||||||
if (tokenEnd !== str.length - 1) {
|
if (tokenEnd !== str.length - 1) {
|
||||||
if (str.startsWith(' and', tokenEnd)) {
|
if (str.startsWith(' and', tokenEnd)) {
|
||||||
return <ANDSearchQuery>{
|
return <ANDSearchQuery>{
|
||||||
type: SearchQueryTypes.AND,
|
type: SearchQueryTypes.AND,
|
||||||
list: [SearchQueryDTO.parse(str.slice(0, tokenEnd)), // trim brackets
|
list: [SearchQueryDTO.parse(str.slice(0, tokenEnd), implicitOR), // trim brackets
|
||||||
SearchQueryDTO.parse(str.slice(tokenEnd + 4))]
|
SearchQueryDTO.parse(str.slice(tokenEnd + 4), implicitOR)]
|
||||||
};
|
};
|
||||||
} else {
|
} else if (str.startsWith(' or', tokenEnd)) {
|
||||||
let padding = 0;
|
|
||||||
if (str.startsWith(' or', tokenEnd)) {
|
|
||||||
padding = 3;
|
|
||||||
}
|
|
||||||
return <ORSearchQuery>{
|
return <ORSearchQuery>{
|
||||||
type: SearchQueryTypes.OR,
|
type: SearchQueryTypes.OR,
|
||||||
list: [SearchQueryDTO.parse(str.slice(0, tokenEnd)), // trim brackets
|
list: [SearchQueryDTO.parse(str.slice(0, tokenEnd), implicitOR), // trim brackets
|
||||||
SearchQueryDTO.parse(str.slice(tokenEnd + padding))]
|
SearchQueryDTO.parse(str.slice(tokenEnd + 3), implicitOR)]
|
||||||
|
};
|
||||||
|
} else { // Relation cannot be detected
|
||||||
|
return <SearchListQuery>{
|
||||||
|
type: implicitOR === true ? SearchQueryTypes.OR : SearchQueryTypes.UNKNOWN_RELATION,
|
||||||
|
list: [SearchQueryDTO.parse(str.slice(0, tokenEnd), implicitOR), // trim brackets
|
||||||
|
SearchQueryDTO.parse(str.slice(tokenEnd), implicitOR)]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (str.startsWith('some-of:') ||
|
if (str.startsWith('some-of:') ||
|
||||||
new RegExp(/^\d*-of:/).test(str)) {
|
new RegExp(/^\d*-of:/).test(str)) {
|
||||||
const prefix = str.startsWith('some-of:') ? 'some-of:' : new RegExp(/^\d*-of:/).exec(str)[0];
|
const prefix = str.startsWith('some-of:') ? 'some-of:' : new RegExp(/^\d*-of:/).exec(str)[0];
|
||||||
let tmpList: any = SearchQueryDTO.parse(str.slice(prefix.length + 1, -1)); // trim brackets
|
let tmpList: any = SearchQueryDTO.parse(str.slice(prefix.length + 1, -1), false); // trim brackets
|
||||||
|
// console.log(JSON.stringify(tmpList, null, 4));
|
||||||
const unfoldList = (q: SearchListQuery): SearchQueryDTO[] => {
|
const unfoldList = (q: SearchListQuery): SearchQueryDTO[] => {
|
||||||
if (q.list) {
|
if (q.list) {
|
||||||
return [].concat.apply([], q.list.map(e => unfoldList(<any>e))); // flatten array
|
if (q.type === SearchQueryTypes.UNKNOWN_RELATION) {
|
||||||
|
return [].concat.apply([], q.list.map(e => unfoldList(<any>e))); // flatten array
|
||||||
|
} else {
|
||||||
|
q.list.forEach(e => unfoldList(<any>e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return [q];
|
return [q];
|
||||||
};
|
};
|
||||||
|
@ -792,7 +792,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
it('should search date', async () => {
|
it('should search date', async () => {
|
||||||
const sm = new SearchManager();
|
const sm = new SearchManager();
|
||||||
|
|
||||||
let query: any = <FromDateSearch>{value: 0, type: SearchQueryTypes.from_date};
|
let query: any = <ToDateSearch>{value: 0, type: SearchQueryTypes.to_date};
|
||||||
|
|
||||||
expect(Utils.clone(await sm.search(query)))
|
expect(Utils.clone(await sm.search(query)))
|
||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
@ -803,15 +803,15 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
query = <ToDateSearch>{
|
query = <FromDateSearch>{
|
||||||
value: p.metadata.creationDate, type: SearchQueryTypes.to_date
|
value: p.metadata.creationDate, type: SearchQueryTypes.from_date
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(Utils.clone(await sm.search(query)))
|
expect(Utils.clone(await sm.search(query)))
|
||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
searchQuery: query,
|
searchQuery: query,
|
||||||
directories: [],
|
directories: [],
|
||||||
media: [p],
|
media: [p, v],
|
||||||
metaFile: [],
|
metaFile: [],
|
||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
@ -826,7 +826,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
searchQuery: query,
|
searchQuery: query,
|
||||||
directories: [],
|
directories: [],
|
||||||
media: [p2, p_faceLess, p4, v],
|
media: [p2, p_faceLess, p4],
|
||||||
metaFile: [],
|
metaFile: [],
|
||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
@ -851,7 +851,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
it('should search rating', async () => {
|
it('should search rating', async () => {
|
||||||
const sm = new SearchManager();
|
const sm = new SearchManager();
|
||||||
|
|
||||||
let query: MinRatingSearch | MaxRatingSearch = <MinRatingSearch>{value: 0, type: SearchQueryTypes.min_rating};
|
let query: MinRatingSearch | MaxRatingSearch = <MaxRatingSearch>{value: 0, type: SearchQueryTypes.max_rating};
|
||||||
|
|
||||||
|
|
||||||
expect(Utils.clone(await sm.search(query)))
|
expect(Utils.clone(await sm.search(query)))
|
||||||
@ -888,7 +888,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
searchQuery: query,
|
searchQuery: query,
|
||||||
directories: [],
|
directories: [],
|
||||||
media: [p2],
|
media: [p2, p_faceLess],
|
||||||
metaFile: [],
|
metaFile: [],
|
||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
@ -898,7 +898,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
searchQuery: query,
|
searchQuery: query,
|
||||||
directories: [],
|
directories: [],
|
||||||
media: [p, p_faceLess],
|
media: [p],
|
||||||
metaFile: [],
|
metaFile: [],
|
||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
@ -909,7 +909,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
const sm = new SearchManager();
|
const sm = new SearchManager();
|
||||||
|
|
||||||
let query: MinResolutionSearch | MaxResolutionSearch =
|
let query: MinResolutionSearch | MaxResolutionSearch =
|
||||||
<MinResolutionSearch>{value: 0, type: SearchQueryTypes.min_resolution};
|
<MaxResolutionSearch>{value: 0, type: SearchQueryTypes.max_resolution};
|
||||||
|
|
||||||
expect(Utils.clone(await sm.search(query)))
|
expect(Utils.clone(await sm.search(query)))
|
||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
@ -930,27 +930,28 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
query = <MinResolutionSearch>{value: 2, type: SearchQueryTypes.min_resolution};
|
query = <MinResolutionSearch>{value: 3, type: SearchQueryTypes.min_resolution};
|
||||||
expect(Utils.clone(await sm.search(query)))
|
expect(Utils.clone(await sm.search(query)))
|
||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
searchQuery: query,
|
searchQuery: query,
|
||||||
directories: [],
|
directories: [],
|
||||||
media: [p2, p_faceLess],
|
media: [p4],
|
||||||
|
metaFile: [],
|
||||||
|
resultOverflow: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
query = <MinResolutionSearch>{value: 3, negate: true, type: SearchQueryTypes.min_resolution};
|
||||||
|
expect(Utils.clone(await sm.search(query)))
|
||||||
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
|
searchQuery: query,
|
||||||
|
directories: [],
|
||||||
|
media: [p, p2, p_faceLess, v],
|
||||||
metaFile: [],
|
metaFile: [],
|
||||||
resultOverflow: false
|
resultOverflow: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
query = <MaxResolutionSearch>{value: 3, negate: true, type: SearchQueryTypes.max_resolution};
|
query = <MaxResolutionSearch>{value: 3, negate: true, type: SearchQueryTypes.max_resolution};
|
||||||
expect(Utils.clone(await sm.search(query)))
|
|
||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
|
||||||
searchQuery: query,
|
|
||||||
directories: [],
|
|
||||||
media: [p, v, p4],
|
|
||||||
metaFile: [],
|
|
||||||
resultOverflow: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
query = <MinResolutionSearch>{value: 3, negate: true, type: SearchQueryTypes.min_resolution};
|
|
||||||
expect(Utils.clone(await sm.search(query)))
|
expect(Utils.clone(await sm.search(query)))
|
||||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||||
searchQuery: query,
|
searchQuery: query,
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
SearchQueryTypes,
|
SearchQueryTypes,
|
||||||
SomeOfSearchQuery,
|
SomeOfSearchQuery,
|
||||||
TextSearch,
|
TextSearch,
|
||||||
|
TextSearchQueryMatchTypes,
|
||||||
ToDateSearch
|
ToDateSearch
|
||||||
} from '../../../src/common/entities/SearchQueryDTO';
|
} from '../../../src/common/entities/SearchQueryDTO';
|
||||||
|
|
||||||
@ -33,6 +34,11 @@ describe('SearchQueryDTO', () => {
|
|||||||
check(<TextSearch>{type: SearchQueryTypes.caption, text: 'caption'});
|
check(<TextSearch>{type: SearchQueryTypes.caption, text: 'caption'});
|
||||||
check(<TextSearch>{type: SearchQueryTypes.file_name, text: 'filename'});
|
check(<TextSearch>{type: SearchQueryTypes.file_name, text: 'filename'});
|
||||||
check(<TextSearch>{type: SearchQueryTypes.position, text: 'New York'});
|
check(<TextSearch>{type: SearchQueryTypes.position, text: 'New York'});
|
||||||
|
check(<TextSearch>{
|
||||||
|
type: SearchQueryTypes.position,
|
||||||
|
matchType: TextSearchQueryMatchTypes.exact_match,
|
||||||
|
text: 'New York'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date search', () => {
|
it('Date search', () => {
|
||||||
@ -62,6 +68,18 @@ describe('SearchQueryDTO', () => {
|
|||||||
<TextSearch>{type: SearchQueryTypes.position, text: 'New York'}
|
<TextSearch>{type: SearchQueryTypes.position, text: 'New York'}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
check(<ANDSearchQuery>{
|
||||||
|
type: SearchQueryTypes.AND,
|
||||||
|
list: [
|
||||||
|
<TextSearch>{type: SearchQueryTypes.keyword, text: 'big boom'},
|
||||||
|
<TextSearch>{
|
||||||
|
type: SearchQueryTypes.position,
|
||||||
|
matchType: TextSearchQueryMatchTypes.exact_match,
|
||||||
|
text: 'New York'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
check(<ANDSearchQuery>{
|
check(<ANDSearchQuery>{
|
||||||
type: SearchQueryTypes.AND,
|
type: SearchQueryTypes.AND,
|
||||||
list: [
|
list: [
|
||||||
@ -75,6 +93,21 @@ describe('SearchQueryDTO', () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
check(<ANDSearchQuery>{
|
||||||
|
type: SearchQueryTypes.AND,
|
||||||
|
list: [
|
||||||
|
<SomeOfSearchQuery>{
|
||||||
|
type: SearchQueryTypes.SOME_OF,
|
||||||
|
min: 2,
|
||||||
|
list: [
|
||||||
|
<TextSearch>{type: SearchQueryTypes.keyword, text: 'big boom'},
|
||||||
|
<TextSearch>{type: SearchQueryTypes.position, text: 'New York'},
|
||||||
|
<TextSearch>{type: SearchQueryTypes.caption, text: 'caption test'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
<TextSearch>{type: SearchQueryTypes.position, text: 'New York'}
|
||||||
|
]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('Or search', () => {
|
it('Or search', () => {
|
||||||
check(<ORSearchQuery>{
|
check(<ORSearchQuery>{
|
||||||
@ -106,6 +139,21 @@ describe('SearchQueryDTO', () => {
|
|||||||
<TextSearch>{type: SearchQueryTypes.position, text: 'New York'}
|
<TextSearch>{type: SearchQueryTypes.position, text: 'New York'}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
check(<SomeOfSearchQuery>{
|
||||||
|
type: SearchQueryTypes.SOME_OF,
|
||||||
|
list: [
|
||||||
|
<TextSearch>{
|
||||||
|
type: SearchQueryTypes.keyword,
|
||||||
|
matchType: TextSearchQueryMatchTypes.exact_match,
|
||||||
|
text: 'big boom'
|
||||||
|
},
|
||||||
|
<TextSearch>{
|
||||||
|
type: SearchQueryTypes.position,
|
||||||
|
matchType: TextSearchQueryMatchTypes.exact_match,
|
||||||
|
text: 'New York'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
check(<SomeOfSearchQuery>{
|
check(<SomeOfSearchQuery>{
|
||||||
type: SearchQueryTypes.SOME_OF,
|
type: SearchQueryTypes.SOME_OF,
|
||||||
min: 2,
|
min: 2,
|
||||||
|
Loading…
Reference in New Issue
Block a user