F95API/src/scripts/classes/query/handiwork-search-query.ts

177 lines
5.6 KiB
TypeScript

"use strict";
import { AxiosResponse } from 'axios';
// Public modules from npm
import validator from 'class-validator';
// Module from files
import { IQuery, TCategory, TQueryInterface } from "../../interfaces.js";
import { GenericAxiosError, UnexpectedResponseContentType } from '../errors.js';
import { Result } from '../result.js';
import LatestSearchQuery, { TLatestOrder } from './latest-search-query.js';
import ThreadSearchQuery, { TThreadOrder } from './thread-search-query.js';
// Type definitions
/**
* Method of sorting results. Try to unify the two types of
* sorts in the "Latest" section and in the "Thread search"
* section. Being dynamic research, if a sorting type is not
* available, the replacement sort is chosen.
*
* `date`: Order based on the latest update
*
* `likes`: Order based on the number of likes received. Replacement: `replies`.
*
* `relevance`: Order based on the relevance of the result (or rating).
*
* `replies`: Order based on the number of answers to the thread. Replacement: `views`.
*
* `title`: Order based on the growing alphabetical order of the titles.
*
* `views`: Order based on the number of visits. Replacement: `replies`.
*/
type THandiworkOrder = "date" | "likes" | "relevance" | "replies" | "title" | "views";
type TExecuteResult = Result<GenericAxiosError, AxiosResponse<any>>;
export default class HandiworkSearchQuery implements IQuery {
//#region Private fields
static MIN_PAGE = 1;
//#endregion Private fields
//#region Properties
/**
* Keywords to use in the search.
*/
public keywords: string = "";
/**
* The results must be more recent than the date indicated.
*/
public newerThan: Date = null;
/**
* The results must be older than the date indicated.
*/
public olderThan: Date = null;
public includedTags: string[] = [];
/**
* Tags to exclude from the search.
*/
public excludedTags: string[] = [];
public includedPrefixes: string[] = [];
public category: TCategory = null;
/**
* Results presentation order.
*/
public order: THandiworkOrder = "relevance";
@validator.IsInt({
message: "$property expect an integer, received $value"
})
@validator.Min(HandiworkSearchQuery.MIN_PAGE, {
message: "The minimum $property value must be $constraint1, received $value"
})
public page: number = 1;
itype: TQueryInterface = "HandiworkSearchQuery";
//#endregion Properties
//#region Public methods
/**
* Select what kind of search should be
* performed based on the properties of
* the query.
*/
public selectSearchType(): "latest" | "thread" {
// Local variables
const MAX_TAGS_LATEST_SEARCH = 5;
const DEFAULT_SEARCH_TYPE = "latest";
// If the keywords are set or the number
// of included tags is greather than 5,
// we must perform a thread search
if (this.keywords || this.includedTags.length > MAX_TAGS_LATEST_SEARCH) return "thread";
return DEFAULT_SEARCH_TYPE;
}
public validate(): boolean { return validator.validateSync(this).length === 0; }
public async execute(): Promise<TExecuteResult> {
// Local variables
let response: TExecuteResult = null;
// Check if the query is valid
if (!this.validate()) {
throw new Error(`Invalid query: ${validator.validateSync(this).join("\n")}`);
}
// Convert the query
if (this.selectSearchType() === "latest") response = await this.cast<LatestSearchQuery>("LatestSearchQuery").execute();
else response = await this.cast<ThreadSearchQuery>("ThreadSearchQuery").execute();
return response;
}
public cast<T extends IQuery>(type: TQueryInterface): T {
// Local variables
let returnValue = null;
// Convert the query
if (type === "LatestSearchQuery") returnValue = this.castToLatest();
else if (type === "ThreadSearchQuery") returnValue = this.castToThread();
else returnValue = this as HandiworkSearchQuery;
// Cast the result to T
return returnValue as T;
}
//#endregion Public methods
//#region Private methods
private castToLatest(): LatestSearchQuery {
// Cast the basic query object and copy common values
const query: LatestSearchQuery = new LatestSearchQuery;
Object.keys(this).forEach(key => {
if (query.hasOwnProperty(key)) {
query[key] = this[key];
}
});
// Adapt order filter
let orderFilter = this.order as string;
if (orderFilter === "relevance") orderFilter = "rating";
else if (orderFilter === "replies") orderFilter = "views";
query.order = orderFilter as TLatestOrder;
// Adapt date
if (this.newerThan) query.date = query.findNearestDate(this.newerThan);
return query;
}
private castToThread(): ThreadSearchQuery {
// Cast the basic query object and copy common values
const query: ThreadSearchQuery = new ThreadSearchQuery;
Object.keys(this).forEach(key => {
if (query.hasOwnProperty(key)) {
query[key] = this[key];
}
});
// Set uncommon values
query.onlyTitles = true;
// Adapt order filter
let orderFilter = this.order as string;
if (orderFilter === "likes" || orderFilter === "title") orderFilter = "relevance";
query.order = orderFilter as TThreadOrder;
return query;
}
//#endregion
}