mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
improving date autocomplete and parsing for search #58
This commit is contained in:
parent
f30d6988ec
commit
5405a6f9d3
@ -48,6 +48,41 @@ export class SearchQueryParser {
|
|||||||
constructor(private keywords: QueryKeywords) {
|
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 {
|
public parse(str: string, implicitOR = true): SearchQueryDTO {
|
||||||
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();
|
||||||
@ -136,13 +171,13 @@ export class SearchQueryParser {
|
|||||||
if (str.startsWith(this.keywords.from + ':')) {
|
if (str.startsWith(this.keywords.from + ':')) {
|
||||||
return <FromDateSearch>{
|
return <FromDateSearch>{
|
||||||
type: SearchQueryTypes.from_date,
|
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 + ':')) {
|
if (str.startsWith(this.keywords.to + ':')) {
|
||||||
return <ToDateSearch>{
|
return <ToDateSearch>{
|
||||||
type: SearchQueryTypes.to_date,
|
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) {
|
if (!(<FromDateSearch>query).value) {
|
||||||
return '';
|
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:
|
case SearchQueryTypes.to_date:
|
||||||
if (!(<ToDateSearch>query).value) {
|
if (!(<ToDateSearch>query).value) {
|
||||||
return '';
|
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:
|
case SearchQueryTypes.min_rating:
|
||||||
return this.keywords.minRating + ':' + (isNaN((<RangeSearch>query).value) ? '' : (<RangeSearch>query).value);
|
return this.keywords.minRating + ':' + (isNaN((<RangeSearch>query).value) ? '' : (<RangeSearch>query).value);
|
||||||
case SearchQueryTypes.max_rating:
|
case SearchQueryTypes.max_rating:
|
||||||
@ -261,13 +298,7 @@ export class SearchQueryParser {
|
|||||||
return (<DistanceSearch>query).distance + '-' + this.keywords.kmFrom + ':' + (<DistanceSearch>query).from.text;
|
return (<DistanceSearch>query).distance + '-' + this.keywords.kmFrom + ':' + (<DistanceSearch>query).from.text;
|
||||||
|
|
||||||
case SearchQueryTypes.any_text:
|
case SearchQueryTypes.any_text:
|
||||||
if ((<TextSearch>query).matchType === TextSearchQueryMatchTypes.exact_match) {
|
return SearchQueryParser.stringifyText((<TextSearch>query).text, (<TextSearch>query).matchType);
|
||||||
return '"' + (<TextSearch>query).text + '"';
|
|
||||||
|
|
||||||
} else if ((<TextSearch>query).text.indexOf(' ') !== -1) {
|
|
||||||
return '(' + (<TextSearch>query).text + ')';
|
|
||||||
}
|
|
||||||
return (<TextSearch>query).text;
|
|
||||||
|
|
||||||
case SearchQueryTypes.person:
|
case SearchQueryTypes.person:
|
||||||
case SearchQueryTypes.position:
|
case SearchQueryTypes.position:
|
||||||
@ -278,13 +309,8 @@ export class SearchQueryParser {
|
|||||||
if (!(<TextSearch>query).text) {
|
if (!(<TextSearch>query).text) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if ((<TextSearch>query).matchType === TextSearchQueryMatchTypes.exact_match) {
|
return (<any>this.keywords)[SearchQueryTypes[query.type]] + ':' +
|
||||||
return (<any>this.keywords)[SearchQueryTypes[query.type]] + ':"' + (<TextSearch>query).text + '"';
|
SearchQueryParser.stringifyText((<TextSearch>query).text, (<TextSearch>query).matchType);
|
||||||
|
|
||||||
} 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;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown type: ' + query.type);
|
throw new Error('Unknown type: ' + query.type);
|
||||||
|
@ -6,6 +6,7 @@ import {SearchQueryParserService} from './search-query-parser.service';
|
|||||||
import {BehaviorSubject} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
import {SearchQueryTypes, TextSearchQueryTypes} from '../../../../../common/entities/SearchQueryDTO';
|
import {SearchQueryTypes, TextSearchQueryTypes} from '../../../../../common/entities/SearchQueryDTO';
|
||||||
import {QueryParams} from '../../../../../common/QueryParams';
|
import {QueryParams} from '../../../../../common/QueryParams';
|
||||||
|
import {SearchQueryParser} from '../../../../../common/SearchQueryParser';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AutoCompleteService {
|
export class AutoCompleteService {
|
||||||
@ -31,6 +32,15 @@ export class AutoCompleteService {
|
|||||||
this.keywords.push(i + this._searchQueryParserService.keywords.NSomeOf);
|
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 => {
|
TextSearchQueryTypes.forEach(t => {
|
||||||
this.textSearchKeywordsMap[(<any>this._searchQueryParserService.keywords)[SearchQueryTypes[t]]] = t;
|
this.textSearchKeywordsMap[(<any>this._searchQueryParserService.keywords)[SearchQueryTypes[t]]] = t;
|
||||||
});
|
});
|
||||||
@ -69,14 +79,6 @@ export class AutoCompleteService {
|
|||||||
return items;
|
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 {
|
public getPrefixLessSearchText(text: string): string {
|
||||||
const tokens = text.split(':');
|
const tokens = text.split(':');
|
||||||
if (tokens.length !== 2) {
|
if (tokens.length !== 2) {
|
||||||
@ -89,6 +91,14 @@ export class AutoCompleteService {
|
|||||||
return tokens[1];
|
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 {
|
private ACItemToRenderable(item: IAutoCompleteItem): RenderableAutoCompleteItem {
|
||||||
if (!item.type) {
|
if (!item.type) {
|
||||||
return {text: item.text, queryHint: item.text};
|
return {text: item.text, queryHint: item.text};
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
MinResolutionSearch,
|
MinResolutionSearch,
|
||||||
OrientationSearch,
|
OrientationSearch,
|
||||||
ORSearchQuery,
|
ORSearchQuery,
|
||||||
|
RangeSearch,
|
||||||
SearchQueryDTO,
|
SearchQueryDTO,
|
||||||
SearchQueryTypes,
|
SearchQueryTypes,
|
||||||
SomeOfSearchQuery,
|
SomeOfSearchQuery,
|
||||||
@ -66,8 +67,21 @@ describe('SearchQueryParser', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Date search', () => {
|
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(<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', () => {
|
it('Rating search', () => {
|
||||||
check(<MinRatingSearch>{type: SearchQueryTypes.min_rating, value: 10});
|
check(<MinRatingSearch>{type: SearchQueryTypes.min_rating, value: 10});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user