1
0
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:
Patrik J. Braun 2023-05-07 12:46:15 +02:00
parent 0e9714cc0e
commit 48e0c267a4
3 changed files with 171 additions and 14 deletions

View File

@ -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(

View File

@ -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;
}

View File

@ -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);