1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-10 11:10:35 +02:00

improving date autocomplete and parsing for search #58

This commit is contained in:
Patrik J. Braun 2021-04-04 11:40:43 +02:00
parent f30d6988ec
commit 5405a6f9d3
3 changed files with 77 additions and 27 deletions

View File

@ -48,6 +48,41 @@ export class SearchQueryParser {
constructor(private keywords: QueryKeywords) {
}
public static stringifyText(text: string, matchType = TextSearchQueryMatchTypes.like): string {
if (matchType === TextSearchQueryMatchTypes.exact_match) {
return '"' + text + '"';
}
if (text.indexOf(' ') !== -1) {
return '(' + text + ')';
}
return text;
}
private static stringifyDate(time: number): string {
const date = new Date(time);
// simplify date with yeah only if its first of jan
if (date.getMonth() === 0 && date.getDate() === 1) {
return date.getFullYear().toString();
}
return this.stringifyText(date.toLocaleDateString());
}
private static parseDate(text: string): number {
if (text.charAt(0) === '"' || text.charAt(0) === '(') {
text = text.substring(1);
}
if (text.charAt(text.length - 1) === '"' || text.charAt(text.length - 1) === ')') {
text = text.substring(0, text.length - 1);
}
// it is the year only
if (text.length === 4) {
const d = new Date(2000, 0, 1);
d.setFullYear(parseInt(text, 10));
return d.getTime();
}
return Date.parse(text);
}
public parse(str: string, implicitOR = true): SearchQueryDTO {
str = str.replace(/\s\s+/g, ' ') // remove double spaces
.replace(/:\s+/g, ':').replace(/\)(?=\S)/g, ') ').trim();
@ -136,13 +171,13 @@ export class SearchQueryParser {
if (str.startsWith(this.keywords.from + ':')) {
return <FromDateSearch>{
type: SearchQueryTypes.from_date,
value: Date.parse(str.slice((this.keywords.from + ':').length + 1, str.length - 1))
value: SearchQueryParser.parseDate(str.substring((this.keywords.from + ':').length))
};
}
if (str.startsWith(this.keywords.to + ':')) {
return <ToDateSearch>{
type: SearchQueryTypes.to_date,
value: Date.parse(str.slice((this.keywords.to + ':').length + 1, str.length - 1))
value: SearchQueryParser.parseDate(str.substring((this.keywords.to + ':').length))
};
}
@ -240,12 +275,14 @@ export class SearchQueryParser {
if (!(<FromDateSearch>query).value) {
return '';
}
return this.keywords.from + ':(' + new Date((<FromDateSearch>query).value).toLocaleDateString() + ')'.trim();
return this.keywords.from + ':' +
SearchQueryParser.stringifyDate((<FromDateSearch>query).value);
case SearchQueryTypes.to_date:
if (!(<ToDateSearch>query).value) {
return '';
}
return this.keywords.to + ':(' + new Date((<ToDateSearch>query).value).toLocaleDateString() + ')'.trim();
return this.keywords.to + ':' +
SearchQueryParser.stringifyDate((<ToDateSearch>query).value);
case SearchQueryTypes.min_rating:
return this.keywords.minRating + ':' + (isNaN((<RangeSearch>query).value) ? '' : (<RangeSearch>query).value);
case SearchQueryTypes.max_rating:
@ -261,13 +298,7 @@ export class SearchQueryParser {
return (<DistanceSearch>query).distance + '-' + this.keywords.kmFrom + ':' + (<DistanceSearch>query).from.text;
case SearchQueryTypes.any_text:
if ((<TextSearch>query).matchType === TextSearchQueryMatchTypes.exact_match) {
return '"' + (<TextSearch>query).text + '"';
} else if ((<TextSearch>query).text.indexOf(' ') !== -1) {
return '(' + (<TextSearch>query).text + ')';
}
return (<TextSearch>query).text;
return SearchQueryParser.stringifyText((<TextSearch>query).text, (<TextSearch>query).matchType);
case SearchQueryTypes.person:
case SearchQueryTypes.position:
@ -278,13 +309,8 @@ export class SearchQueryParser {
if (!(<TextSearch>query).text) {
return '';
}
if ((<TextSearch>query).matchType === TextSearchQueryMatchTypes.exact_match) {
return (<any>this.keywords)[SearchQueryTypes[query.type]] + ':"' + (<TextSearch>query).text + '"';
} else if ((<TextSearch>query).text.indexOf(' ') !== -1) {
return (<any>this.keywords)[SearchQueryTypes[query.type]] + ':(' + (<TextSearch>query).text + ')';
}
return (<any>this.keywords)[SearchQueryTypes[query.type]] + ':' + (<TextSearch>query).text;
return (<any>this.keywords)[SearchQueryTypes[query.type]] + ':' +
SearchQueryParser.stringifyText((<TextSearch>query).text, (<TextSearch>query).matchType);
default:
throw new Error('Unknown type: ' + query.type);

View File

@ -6,6 +6,7 @@ import {SearchQueryParserService} from './search-query-parser.service';
import {BehaviorSubject} from 'rxjs';
import {SearchQueryTypes, TextSearchQueryTypes} from '../../../../../common/entities/SearchQueryDTO';
import {QueryParams} from '../../../../../common/QueryParams';
import {SearchQueryParser} from '../../../../../common/SearchQueryParser';
@Injectable()
export class AutoCompleteService {
@ -31,6 +32,15 @@ export class AutoCompleteService {
this.keywords.push(i + this._searchQueryParserService.keywords.NSomeOf);
}
this.keywords.push(this._searchQueryParserService.keywords.to + ':' +
SearchQueryParser.stringifyText((new Date).getFullYear().toString()));
this.keywords.push(this._searchQueryParserService.keywords.to + ':' +
SearchQueryParser.stringifyText((new Date).toLocaleDateString()));
this.keywords.push(this._searchQueryParserService.keywords.from + ':' +
SearchQueryParser.stringifyText((new Date).getFullYear().toString()));
this.keywords.push(this._searchQueryParserService.keywords.from + ':' +
SearchQueryParser.stringifyText((new Date).toLocaleDateString()));
TextSearchQueryTypes.forEach(t => {
this.textSearchKeywordsMap[(<any>this._searchQueryParserService.keywords)[SearchQueryTypes[t]]] = t;
});
@ -69,14 +79,6 @@ export class AutoCompleteService {
return items;
}
private getTypeFromPrefix(text: string): SearchQueryTypes {
const tokens = text.split(':');
if (tokens.length !== 2) {
return null;
}
return this.textSearchKeywordsMap[tokens[0]] || null;
}
public getPrefixLessSearchText(text: string): string {
const tokens = text.split(':');
if (tokens.length !== 2) {
@ -89,6 +91,14 @@ export class AutoCompleteService {
return tokens[1];
}
private getTypeFromPrefix(text: string): SearchQueryTypes {
const tokens = text.split(':');
if (tokens.length !== 2) {
return null;
}
return this.textSearchKeywordsMap[tokens[0]] || null;
}
private ACItemToRenderable(item: IAutoCompleteItem): RenderableAutoCompleteItem {
if (!item.type) {
return {text: item.text, queryHint: item.text};

View File

@ -9,6 +9,7 @@ import {
MinResolutionSearch,
OrientationSearch,
ORSearchQuery,
RangeSearch,
SearchQueryDTO,
SearchQueryTypes,
SomeOfSearchQuery,
@ -66,8 +67,21 @@ describe('SearchQueryParser', () => {
});
it('Date search', () => {
check(<FromDateSearch>{type: SearchQueryTypes.from_date, value: (new Date(2020, 1, 10)).getTime()});
check(<FromDateSearch>{type: SearchQueryTypes.from_date, value: (new Date(2020, 1, 1)).getTime()});
check(<ToDateSearch>{type: SearchQueryTypes.to_date, value: (new Date(2020, 1, 2)).getTime()});
check(<ToDateSearch>{type: SearchQueryTypes.to_date, value: (new Date(2020, 1, 20)).getTime()});
check(<ToDateSearch>{type: SearchQueryTypes.to_date, value: (new Date(2020, 1, 1)).getTime()});
const parser = new SearchQueryParser(keywords);
// test if date gets simplified on 1st of Jan.
let query: RangeSearch = <ToDateSearch>{type: SearchQueryTypes.to_date, value: (new Date(2020, 0, 1)).getTime()};
expect(parser.parse(keywords.to + ':' + (new Date(query.value)).getFullYear()))
.to.deep.equals(query, parser.stringify(query));
query = <FromDateSearch>{type: SearchQueryTypes.from_date, value: (new Date(2020, 0, 1)).getTime()};
expect(parser.parse(keywords.from + ':' + (new Date(query.value)).getFullYear()))
.to.deep.equals(query, parser.stringify(query));
});
it('Rating search', () => {
check(<MinRatingSearch>{type: SearchQueryTypes.min_rating, value: 10});