"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>; 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 { // 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").execute(); else response = await this.cast("ThreadSearchQuery").execute(); return response; } public cast(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 }