mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
Creating parser for data patter search #660
This commit is contained in:
parent
0e9714cc0e
commit
48e0c267a4
@ -1,5 +1,7 @@
|
||||
import {
|
||||
ANDSearchQuery,
|
||||
DatePatternFrequency,
|
||||
DatePatternSearch,
|
||||
DistanceSearch,
|
||||
FromDateSearch,
|
||||
MaxRatingSearch,
|
||||
@ -22,6 +24,15 @@ import {
|
||||
import {Utils} from './Utils';
|
||||
|
||||
export interface QueryKeywords {
|
||||
days_ago: any;
|
||||
years_ago: string;
|
||||
months_ago: string;
|
||||
weeks_ago: string;
|
||||
every_year: string;
|
||||
every_month: string;
|
||||
every_week: string;
|
||||
lastNDays: string;
|
||||
sameDay: string;
|
||||
portrait: string;
|
||||
landscape: string;
|
||||
orientation: string;
|
||||
@ -52,12 +63,27 @@ export const defaultQueryKeywords: QueryKeywords = {
|
||||
|
||||
from: 'after',
|
||||
to: 'before',
|
||||
landscape: 'landscape',
|
||||
|
||||
maxRating: 'max-rating',
|
||||
maxResolution: 'max-resolution',
|
||||
minRating: 'min-rating',
|
||||
minResolution: 'min-resolution',
|
||||
|
||||
kmFrom: 'km-from',
|
||||
orientation: 'orientation',
|
||||
landscape: 'landscape',
|
||||
portrait: 'portrait',
|
||||
|
||||
|
||||
years_ago: '%d-years-ago',
|
||||
months_ago: '%d-months-ago',
|
||||
weeks_ago: '%d-weeks-ago',
|
||||
days_ago: '%d-days-ago',
|
||||
every_year: 'every-year',
|
||||
every_month: 'every-month',
|
||||
every_week: 'every-week',
|
||||
lastNDays: 'last-%d-days',
|
||||
sameDay: 'same-day',
|
||||
|
||||
any_text: 'any-text',
|
||||
keyword: 'keyword',
|
||||
@ -65,10 +91,8 @@ export const defaultQueryKeywords: QueryKeywords = {
|
||||
directory: 'directory',
|
||||
file_name: 'file-name',
|
||||
person: 'person',
|
||||
portrait: 'portrait',
|
||||
position: 'position',
|
||||
someOf: 'some-of',
|
||||
kmFrom: 'km-from',
|
||||
};
|
||||
|
||||
export class SearchQueryParser {
|
||||
@ -143,6 +167,13 @@ export class SearchQueryParser {
|
||||
.replace(/:\s+/g, ':')
|
||||
.trim();
|
||||
|
||||
|
||||
const humanToRegexpStr = (str: string) => {
|
||||
return str.replace(/%d/g, '\\d*');
|
||||
};
|
||||
const intFromRegexp = (str: string) => {
|
||||
return parseInt(new RegExp(/\d+/).exec(str)[0], 10);
|
||||
};
|
||||
if (str.charAt(0) === '(' && str.charAt(str.length - 1) === ')') {
|
||||
str = str.slice(1, str.length - 1);
|
||||
}
|
||||
@ -315,7 +346,7 @@ export class SearchQueryParser {
|
||||
}
|
||||
return {
|
||||
type: SearchQueryTypes.distance,
|
||||
distance: parseInt(new RegExp(/^\d*/).exec(str)[0], 10),
|
||||
distance: intFromRegexp(str),
|
||||
from: {text: from},
|
||||
...(new RegExp('^\\d*-' + this.keywords.kmFrom + '!:').test(str) && {
|
||||
negate: true,
|
||||
@ -332,6 +363,45 @@ export class SearchQueryParser {
|
||||
} as OrientationSearch;
|
||||
}
|
||||
|
||||
|
||||
if (kwStartsWith(str, this.keywords.sameDay) ||
|
||||
new RegExp('^' + humanToRegexpStr(this.keywords.lastNDays) + '!?:').test(str)) {
|
||||
const freqStr = str.slice(str.indexOf(':') + 1);
|
||||
let freq: DatePatternFrequency = null;
|
||||
let ago;
|
||||
if (freqStr == this.keywords.every_week) {
|
||||
freq = DatePatternFrequency.every_week;
|
||||
} else if (freqStr == this.keywords.every_month) {
|
||||
freq = DatePatternFrequency.every_month;
|
||||
} else if (freqStr == this.keywords.every_year) {
|
||||
freq = DatePatternFrequency.every_year;
|
||||
} else if (new RegExp('^' + humanToRegexpStr(this.keywords.days_ago) + '$').test(freqStr)) {
|
||||
freq = DatePatternFrequency.days_ago;
|
||||
ago = intFromRegexp(freqStr);
|
||||
} else if (new RegExp('^' + humanToRegexpStr(this.keywords.weeks_ago) + '$').test(freqStr)) {
|
||||
freq = DatePatternFrequency.weeks_ago;
|
||||
ago = intFromRegexp(freqStr);
|
||||
} else if (new RegExp('^' + humanToRegexpStr(this.keywords.months_ago) + '$').test(freqStr)) {
|
||||
freq = DatePatternFrequency.months_ago;
|
||||
ago = intFromRegexp(freqStr);
|
||||
} else if (new RegExp('^' + humanToRegexpStr(this.keywords.years_ago) + '$').test(freqStr)) {
|
||||
freq = DatePatternFrequency.years_ago;
|
||||
ago = intFromRegexp(freqStr);
|
||||
}
|
||||
|
||||
if (freq) {
|
||||
const ret = {
|
||||
type: SearchQueryTypes.date_pattern,
|
||||
daysLength: kwStartsWith(str, this.keywords.sameDay) ? 0 : intFromRegexp(str),
|
||||
frequency: freq
|
||||
} as DatePatternSearch;
|
||||
if (ago) {
|
||||
ret.agoNumber = ago;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// parse text search
|
||||
const tmp = TextSearchQueryTypes.map((type) => ({
|
||||
key: (this.keywords as any)[SearchQueryTypes[type]] + ':',
|
||||
@ -422,14 +492,6 @@ export class SearchQueryParser {
|
||||
')'
|
||||
);
|
||||
|
||||
case SearchQueryTypes.orientation:
|
||||
return (
|
||||
this.keywords.orientation +
|
||||
':' +
|
||||
((query as OrientationSearch).landscape
|
||||
? this.keywords.landscape
|
||||
: this.keywords.portrait)
|
||||
);
|
||||
|
||||
case SearchQueryTypes.from_date:
|
||||
if (!(query as FromDateSearch).value) {
|
||||
@ -500,7 +562,49 @@ export class SearchQueryParser {
|
||||
colon +
|
||||
(query as DistanceSearch).from.text
|
||||
);
|
||||
|
||||
case SearchQueryTypes.orientation:
|
||||
return (
|
||||
this.keywords.orientation +
|
||||
':' +
|
||||
((query as OrientationSearch).landscape
|
||||
? this.keywords.landscape
|
||||
: this.keywords.portrait)
|
||||
);
|
||||
case SearchQueryTypes.date_pattern: {
|
||||
const q = (query as DatePatternSearch);
|
||||
let strBuilder = '';
|
||||
if (q.daysLength <= 0) {
|
||||
strBuilder += this.keywords.sameDay;
|
||||
} else {
|
||||
strBuilder += this.keywords.lastNDays.replace(/%d/g, q.daysLength.toString());
|
||||
}
|
||||
strBuilder += ':';
|
||||
switch (q.frequency) {
|
||||
case DatePatternFrequency.every_week:
|
||||
strBuilder += this.keywords.every_week;
|
||||
break;
|
||||
case DatePatternFrequency.every_month:
|
||||
strBuilder += this.keywords.every_month;
|
||||
break;
|
||||
case DatePatternFrequency.every_year:
|
||||
strBuilder += this.keywords.every_year;
|
||||
break;
|
||||
case DatePatternFrequency.days_ago:
|
||||
strBuilder += this.keywords.days_ago.replace(/%d/g, q.agoNumber.toString());
|
||||
break;
|
||||
case DatePatternFrequency.weeks_ago:
|
||||
strBuilder += this.keywords.weeks_ago.replace(/%d/g, q.agoNumber.toString());
|
||||
break;
|
||||
case DatePatternFrequency.months_ago:
|
||||
strBuilder += this.keywords.months_ago.replace(/%d/g, q.agoNumber.toString());
|
||||
break;
|
||||
case DatePatternFrequency.years_ago:
|
||||
strBuilder += this.keywords.years_ago.replace(/%d/g, q.agoNumber.toString());
|
||||
break;
|
||||
}
|
||||
console.log(strBuilder);
|
||||
return strBuilder;
|
||||
}
|
||||
case SearchQueryTypes.any_text:
|
||||
if (!(query as TextSearch).negate) {
|
||||
return SearchQueryParser.stringifyText(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GPSMetadata } from './PhotoDTO';
|
||||
import {GPSMetadata} from './PhotoDTO';
|
||||
|
||||
export enum SearchQueryTypes {
|
||||
AND = 1,
|
||||
@ -17,6 +17,7 @@ export enum SearchQueryTypes {
|
||||
|
||||
distance,
|
||||
orientation,
|
||||
date_pattern,
|
||||
|
||||
// TEXT search types
|
||||
any_text = 100,
|
||||
@ -26,6 +27,8 @@ export enum SearchQueryTypes {
|
||||
keyword,
|
||||
person,
|
||||
position,
|
||||
|
||||
|
||||
}
|
||||
|
||||
export const ListSearchQueryTypes = [
|
||||
@ -223,3 +226,15 @@ export interface OrientationSearch {
|
||||
landscape: boolean;
|
||||
}
|
||||
|
||||
export enum DatePatternFrequency {
|
||||
every_week = 1, every_month, every_year,
|
||||
days_ago = 10, weeks_ago, months_ago, years_ago
|
||||
}
|
||||
|
||||
export interface DatePatternSearch {
|
||||
type: SearchQueryTypes.date_pattern;
|
||||
daysLength: number; // days
|
||||
frequency: DatePatternFrequency;
|
||||
agoNumber?: number;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import {expect} from 'chai';
|
||||
import {
|
||||
ANDSearchQuery,
|
||||
DatePatternFrequency,
|
||||
DatePatternSearch,
|
||||
DistanceSearch,
|
||||
FromDateSearch,
|
||||
MaxRatingSearch,
|
||||
@ -110,6 +112,42 @@ describe('SearchQueryParser', () => {
|
||||
check({type: SearchQueryTypes.orientation, landscape: true} as OrientationSearch);
|
||||
check({type: SearchQueryTypes.orientation, landscape: false} as OrientationSearch);
|
||||
});
|
||||
it('Date patter search', () => {
|
||||
for (let i = 0; i <= 10; ++i) {
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.every_week
|
||||
} as DatePatternSearch);
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.every_month
|
||||
} as DatePatternSearch);
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.every_year
|
||||
} as DatePatternSearch);
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.days_ago,
|
||||
agoNumber: 1
|
||||
} as DatePatternSearch);
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.weeks_ago,
|
||||
agoNumber: 1
|
||||
} as DatePatternSearch);
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.months_ago,
|
||||
agoNumber: 1
|
||||
} as DatePatternSearch);
|
||||
check({
|
||||
type: SearchQueryTypes.date_pattern, daysLength: i,
|
||||
frequency: DatePatternFrequency.years_ago,
|
||||
agoNumber: 1
|
||||
} as DatePatternSearch);
|
||||
}
|
||||
});
|
||||
it('Default logical operator should be AND', () => {
|
||||
|
||||
const parser = new SearchQueryParser(defaultQueryKeywords);
|
||||
|
Loading…
x
Reference in New Issue
Block a user