UNPKG

@bentoboxes/nova-utils

Version:

This set of utilities are intended to

534 lines (470 loc) 15 kB
import { HttpClient } from "../http-client/index"; import { DateUtils, INPUT_FORMATS } from "./date-utils"; const SORT = { ASC: "asc", DESC: "desc" }; const LOGICAL_OPEATORS = { AND: "AND", OR: "OR", NOT: "!", PROHIBIT: "-", REQUIRED: "+" }; const NOVA_SOLR_DEFAULTS = { get: "/myservices/search-service/doRequest/dp-content/dprender", post: "" }; const SOLR_FORMAT_DATE = "YYYY-MM-DDThh:mm:ss"; const STRICT_ROLES_PARAM = "targetingRoles=true"; class Solr { /** * It receives the base url to Solr * @param baseUrl */ constructor(baseUrl) { this.baseUrl = baseUrl; this.queryString = ""; this.parseMap = {}; this.usePost = false; this.postQuery = { query: [], filter: [] }; this.isQueryIncluded = false; this.restrictRoles = false; }; static get FIELDS() { return { ID: "id", LIBRARY_LOCALE: "documentlibrarylocale", TITLE: "title", SHORT_TITLE: "shorttitle", NAME: "name", DESCRIPTION: "description", SUMMARY: "summary", IMG_CAPTION: "imagecaption", THUMB_CAPTION: "thumbnailcaption", WCM_PATH: "wcmpath", WCM_TITLE_PATH: "wcmtitlepath", AUTH_TEMPLATE: "authtemplate", REFERENCE_URL_NAME: "referenceurlname", CONTENT_TYPE: "contenttype", CONTENT_TYPE_TITLE: "contenttypetitle", CONTENT_TYPE_PATH: "contenttypepath", CONTENT_TYPE_PATH_TITLE: "contenttypepathtitle", AUDIENCE_COUNTRY: "audiencecountry", AUDIENCE_COUNTRY_TITLE: "audiencecountrytitle", AUDIENCE_COUNTRY_PATH: "audiencecountrypath", AUDIENCE_COUNTRY_PATH_TITLE: "audiencecountrypathtitle", AUDIENCE_BRAND: "audiencebrand", AUDIENCE_BRAND_TITLE: "audiencebrandtitle", RELATED_HUBS: "relatedHubs", RELATED_HUBS_TITLE: "relatedHubstitle", RELATED_HUBS_PATH: "relatedHubspath", RELATED_HUBS_PATH_TITLE: "relatedHubspathtitle", TARGET_ROLE: "targetingRole", TARGET_ROLE_TITLE: "targetingRoletitle", TARGET_ROLE_PATH: "targetingRolepath", TARGET_ROLE_PATH_TITLE: "targetingRolepathtitle", PUBLISH_DATE: "publishdate", LIBRARY_NAME: "documentlibraryname", ICON: "icon", LINK: "link", MORE_INFO: "moreInformation", THUMBNAIL: "thumbnail", IMAGE: "image", PORTAL_LOCATION: "portallocation", PORTAL_LOCATION_TITLE: "portallocationtitle", PORTAL_LOCATION_PATH: "portallocationpath", PORTAL_LOCATION_PATH_TITLE: "portallocationpathtitle", CATEGORIES: "categories", CATEGORIES_TITLE: "categoriestitle", CATEGORIES_PATH: "categoriespath", CATEGORIES_PATH_TITLE: "categoriespathtitle", ANSWER: "answer", TOPICS: "topics", TOPICS_TITLE: "topicstitle", TOPICS_PATH: "topicspath", TOPICS_TITLE_PATH: "topicstitlepath", START_DATE: "startDateAndTime", END_DATE: "endDateAndTime", QUESTION_TYPE: "questionType", LOCATION: "location", NAME_SORT: "namesorting", TITLE_SORT: "titlesorting" }; }; /** * @method strictString: this function makes strict an string. * @param clause * @returns {string} */ static strictString(clause) { return this.prototype.__validString(clause) ? `"${clause}"` : ""; } /** * @method and: The AND operator matches documents where both terms exist anywhere * in the text of a single document. * @param leftClause * @param rightClause * @param useParentheses * @returns {string} */ static and(leftClause, rightClause, useParentheses = true) { return this.prototype.__addMultiClause(leftClause, rightClause, LOGICAL_OPEATORS.AND, useParentheses); } /** * @method or: The OR operator links two terms and finds a matching document if either * of the terms exist in a document. * @param {string} leftClause * @param {string | Array<string>} rightClause * @param {Boolean} useParentheses * @returns {string} */ static or(leftClause, rightClause, useParentheses = true) { return this.prototype.__addMultiClause(leftClause, rightClause, LOGICAL_OPEATORS.OR, useParentheses); } /** * @method not: The NOT operator excludes documents that contain the term after NOT. * @param {string} clause * @param useParentheses * @returns {string} */ static not(clause, useParentheses = true) { return this.prototype.__addSingleClause(clause, LOGICAL_OPEATORS.NOT, useParentheses); } /** * @method prohibit: Prohibits the following term (that is, matches on fields or documents * that do not include that term). * @param {string} clause * @param useParentheses * @returns {string} */ static prohibit(clause, useParentheses = true) { return this.prototype.__addSingleClause(clause, LOGICAL_OPEATORS.PROHIBIT, useParentheses); } /** * @method required: requires that the term after required symbol exist somewhere in a field * in at least one document in order for the query to return a match. * @param {string} clause * @param useParentheses * @returns {string} */ static required(clause, useParentheses = true) { return this.prototype.__addSingleClause(clause, LOGICAL_OPEATORS.REQUIRED, useParentheses); } /** * @method date: Add date value parsing the inputs to Solr valid format. * @param {string} startDate * @param {string} inputFormatDate | default: YYYY-MM-DD HH:mm:ss * @param {string} finishDate | default: * * @returns {string} */ static date(startDate, inputFormatDate = INPUT_FORMATS.ISO, finishDate = "*") { if (!this.prototype.__validString(startDate) && !this.prototype.__validString(finishDate)) return ""; startDate = DateUtils.formatCustomDate(startDate, SOLR_FORMAT_DATE, inputFormatDate); if (finishDate !== "*") { finishDate = DateUtils.formatCustomDate(finishDate, SOLR_FORMAT_DATE, inputFormatDate); } return `[${startDate}Z TO ${finishDate}]`; } /** * @method dateFromNow: Add value to filter from today to an specific date or to any date. * @param {string} finishDate | default: * * @param {string} inputFormatDate | default: YYYY-MM-DD HH:mm:ss * @returns {string} */ static dateFromNow(finishDate = "*", inputFormatDate = INPUT_FORMATS.ISO) { if (!this.prototype.__validString(finishDate)) return ""; if (finishDate !== "*") { finishDate = DateUtils.formatCustomDate(finishDate, SOLR_FORMAT_DATE, inputFormatDate); } return `[NOW/DAY TO ${finishDate}]`; } /** * @method post: this method allow the POST request. * @returns {Solr} */ post() { this.usePost = true; return this; } /** * @method addMapperKeyValue: Add a pair (key, value) with the equivalences * that you expect to receive after the query. * @param key * @param value */ addMapperKeyValue(key, value) { if (!this.__validString(key) && !this.__validString(value)) return this; this.parseMap[key] = value; return this; } /** * @method addMapperObject: Add a object with the equivalences that you expect * to receive after the query. * @param {Object} obj */ addMapperObject(obj) { if (!this.__validObj(obj)) return this; this.parseMap = Object.assign(this.parseMap, obj); return this; } /** * @method fq: fq is used to reduce the superset of documents that can be returned. * @param params * @returns {Solr} */ fq(params) { if (!this.__validString(params)) return this; if (this.usePost) { this.postQuery.filter.push(params); } else { this.queryString += this.__addQueryParam(params ? `fq=${params}` : ""); } return this; } /** * @method q: q is used to restrict Solr query. * @param params * @returns {Solr} */ q(params) { console.log(params); if (!this.__validString(params)) return this; if (this.usePost) { this.postQuery.query.push(params); } else { this.queryString += this.__addQueryParam(params ? `q=${params}` : ""); } this.isQueryIncluded = true; return this; } /** * @method fl: fields list is used to limit the fields that you are expecting from Solr. * @param elements * @returns {Solr} */ fl(elements) { if (this.usePost) { this.postQuery.fields = elements; } else { if (Array.isArray(elements) && elements.length) { this.queryString += this.__addQueryParam(`fl=${elements.join(",")}`); } } return this; } /** * @method sort: Add sort parameter to query. * @param paramName * @param asc * @returns {Solr} */ sort(paramName, asc = true) { if (!Array.isArray(paramName) && !Array.isArray(asc)) { paramName = [paramName]; asc = [asc]; } if (this.usePost) { this.postQuery.sort = this.__addMultipleSort(paramName, asc); } else { this.queryString += this.__addQueryParam(`sort=${this.__addMultipleSort(paramName, asc)}`); } return this; } /** * @method: start: Add start number to return documents from that index. * @param {number} start * @returns {Solr} */ start(start = 0) { if (!this.__validNumber(start)) return this; if (this.usePost) { this.postQuery.start = start; } else { this.queryString += this.__addQueryParam(`start=${start}`); } return this; } /** * @method: limit: Add end number to return documents until that index. * @param {number} limit * @returns {Solr} */ limit(limit) { if (!this.__validNumber(limit)) return this; if (this.usePost) { this.postQuery.limit = limit; } else { this.queryString += this.__addQueryParam(`rows=${limit}`); } return this; } /** * @method strictByRoles * @returns {Solr} */ restrictByRoles() { this.restrictRoles = true; return this; } /** * @method execute: this function make an ajax call to Solr and retrieve the response. * @returns {Promise|*|Promise<T | Array>|*} */ execute() { const baseUrl = this.baseUrl ? this.baseUrl : this.usePost ? NOVA_SOLR_DEFAULTS.post : NOVA_SOLR_DEFAULTS.get; let query = this.usePost ? HttpClient.post(baseUrl + `${this.restrictRoles ? "?" + STRICT_ROLES_PARAM : ""}`, this.postQuery) : HttpClient.get(baseUrl + this.queryString + `${this.restrictRoles ? this.__addQueryParam(STRICT_ROLES_PARAM) : ""}`); return query .then((response) => response.json()) .then(jsonResponse => { return this.__parseData(jsonResponse); }) .catch((error) => { console.error(error); return []; }); } /*testRoles(){ const baseUrl = this.baseUrl ? this.baseUrl : this.usePost ? NOVA_SOLR_DEFAULTS.post : NOVA_SOLR_DEFAULTS.get; let query = this.usePost ? baseUrl + `${this.strictRoles ? '?' + STRICT_ROLES_PARAM : ''}`: baseUrl + this.queryString + `${this.strictRoles ? this.__addQueryParam(STRICT_ROLES_PARAM) : '' }` return query; }*/ /** * @method __addMultipleSort: this method allows add multiple instance of sort to Solr query * @param {Array} params * @returns {string} sorting * @private */ __addMultipleSort(params, sorting) { return params.map((param, i) => `${param} ${sorting[i] ? SORT["ASC"] : SORT["DESC"]}`).join(", "); } /** * @method __parseData: this method allows the parse of the information retrieving for Solr, * if a parse map was added it will be use to return this data. * @param {Array} data * @returns {Array} response * @private */ __parseData(data) { if ( data && data.response && data.response.docs ) { let response = data.response.docs; const keys = Object.keys(this.parseMap); if (keys.length) { response = response.reduce((acc, curr) => { keys.forEach(key => { if (Array.isArray(keys[key])) { for (let item of keys[key]) { curr[item] = curr[key]; } } else { curr[this.parseMap[key]] = curr[key]; } }); acc.push(curr); return acc; }, []); } return response; } return []; } /** * @method __addQueryParam: use this method to determine if the query string should use & or ?. * @param {string} param * @returns {string} * @private */ __addQueryParam(param) { if (param) { return this.queryString ? `&${param}` : `?${param}`; } return ""; } /** * @method __addMultiClause: Allows add logical operations that depends of 2 or more elements * @param {string} leftClause * @param {string | Array<string>}rightClause * @param {string} type * @param {Boolean} useParentheses * @returns {string} * @private */ __addMultiClause(leftClause, rightClause, type, useParentheses) { if (this.__validArray(rightClause)) { return this.__addParentheses(`${leftClause}:${this.__addParentheses(rightClause.join(" "), true)}`, useParentheses); } else if (!this.__validString(leftClause) && !this.__validString(rightClause)) return ""; return this.__addParentheses(`${leftClause} ${type} ${rightClause}`, useParentheses); } /** * @method __addSingleClause: Allows add logical operations that depends of 1 element * @param {string} clause * @param {string} type * @param {Boolean} useParentheses * @returns {string} * @private */ __addSingleClause(clause, type, useParentheses) { if (!this.__validString(clause)) return ""; return this.__addParentheses(`${type} ${clause}`, useParentheses); } /** * @method __addParentheses: Add parentheses to a string * @param {string} clause * @param {Boolean} shouldInclude * @return {string} * @private */ __addParentheses(clause, shouldInclude) { return `${shouldInclude ? "(" : ""}${clause}${shouldInclude ? ")" : ""}`; } /** * @method __validString: validation to avoid add empty strings * @param input * @returns {boolean} * @private */ __validString(input) { return typeof input === "string" && input.trim() !== ""; } /** * @method __validObj: validation to avoid empty objects. * @param obj * @returns {Boolean} * @private */ __validObj(obj) { const keys = Object.keys(obj); return typeof obj === "object" && keys.length && !keys.some(k => !this.__validString(obj[k])); } /** * @method __validNumber: validation to avoid no numbers. * @param {Number} number * @returns {Boolean} * @private * */ __validNumber(number) { return !Number.isNaN(number) || !Number.isNaN(+number); } /** * @method __validArray: validation to avoid no Arrays or empty Arrays. * @param {Array} array * @returns {Boolean} * @private */ __validArray(array) { return Array.isArray(array) && array.length; } } export { Solr };