UNPKG

@esri/arcgis-rest-portal

Version:

ArcGIS Online and Enterprise content and user helpers for @esri/arcgis-rest-request

344 lines 10.3 kB
"use strict"; /* Copyright (c) 2018-2021 Environmental Systems Research Institute, Inc. * Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SearchQueryBuilder = void 0; const arcgis_rest_request_1 = require("@esri/arcgis-rest-request"); /** * `SearchQueryBuilder` can be used to construct the `q` param for {@linkcode searchItems} or {@linkcode searchGroups}. * * By chaining methods, it helps build complex search queries. * * ```js * const startDate = new Date("2020-01-01"); * const endDate = new Date("2020-09-01"); * const query = new SearchQueryBuilder() * .match("Patrick") * .in("owner") * .and() * .from(startDate) * .to(endDate) * .in("created") * .and() * .startGroup() * .match("Web Mapping Application") * .in("type") * .or() * .match("Mobile Application") * .in("type") * .or() * .match("Application") * .in("type") * .endGroup() * .and() * .match("Demo App"); * * searchItems(query).then((res) => { * console.log(res.results); * }); * ``` * * Will search for items matching * ``` * "owner: Patrick AND created:[1577836800000 TO 1598918400000] AND (type:"Web Mapping Application" OR type:"Mobile Application" OR type:Application) AND Demo App" * ``` */ class SearchQueryBuilder { /** * @param q An existing query string to start building from. */ constructor(q = "") { this.termStack = []; this.rangeStack = []; this.openGroups = 0; this.q = q; } /** * Defines strings to search for. * * ```js * const query = new SearchQueryBuilder() * .match("My Layer") * ``` * * @param terms strings to search for. */ match(...terms) { this.termStack = this.termStack.concat(terms); return this; } /** * Defines fields to search in. You can pass `"*"` or call this method without arguments to search a default set of fields * * ```js * const query = new SearchQueryBuilder() * .match("My Layer") * .in("title") * ``` * * @param field The field to search for the previous match in. */ in(field) { const fn = `\`in(${field ? `"${field}"` : ""})\``; if (!this.hasRange && !this.hasTerms) { (0, arcgis_rest_request_1.warn)( // apparently-p-rettier-ignore causes some `${fn} was called with no call to \`match(...)\` or \`from(...)\`/\`to(...)\`. Your query was not modified.`); return this; } if (field && field !== "*") { this.q += `${field}:`; } return this.commit(); } /** * Starts a new search group. * * ```js * const query = new SearchQueryBuilder() * .startGroup() * .match("Lakes") * .in("title") * .endGroup() * .or() * .startGroup() * .match("Rivers") * .in("title") * .endGroup() * ``` */ startGroup() { this.commit(); if (this.openGroups > 0) { this.q += " "; } this.openGroups++; this.q += "("; return this; } /** * Ends a search group. * * ```js * const query = new SearchQueryBuilder() * .startGroup() * .match("Lakes") * .in("title") * .endGroup() * .or() * .startGroup() * .match("Rivers") * .in("title") * .endGroup() * ``` */ endGroup() { if (this.openGroups <= 0) { (0, arcgis_rest_request_1.warn)(`\`endGroup(...)\` was called without calling \`startGroup(...)\` first. Your query was not modified.`); return this; } this.commit(); this.openGroups--; this.q += ")"; return this; } /** * Joins two sets of queries with an `AND` clause. * * ```js * const query = new SearchQueryBuilder() * .match("Lakes") * .in("title") * .and() * .match("Rivers") * .in("title") * ``` */ and() { return this.addModifier("and"); } /** * Joins two sets of queries with an `OR` clause. * * ```js * const query = new SearchQueryBuilder() * .match("Lakes") * .in("title") * .or() * .match("Rivers") * .in("title") * ``` */ or() { return this.addModifier("or"); } /** * Joins two sets of queries with a `NOT` clause. Another option for filtering results is the [prohibit operator '-'](https://developers.arcgis.com/rest/users-groups-and-items/search-reference.htm#ESRI_SECTION1_5C6C35DB9E4A4F4492C5B937BDA2BF67). * * ```js * // omit results with "Rivers" in their title * const query = new SearchQueryBuilder() * .not() * .match("Rivers") * .in("title") * * // equivalent * const query = new SearchQueryBuilder() * .match("Rivers") * .in("-title") * ``` */ not() { return this.addModifier("not"); } /** * Begins a new range query. * * ```js * * const NEWYEARS = new Date("2020-01-01") * const TODAY = new Date() * * const query = new SearchQueryBuilder() * .from(NEWYEARS) * .to(TODAY) * .in("created") * ``` */ from(term) { if (this.hasTerms) { (0, arcgis_rest_request_1.warn)( // apparently-p*rettier-ignore causes prettier to strip *all* comments O_o `\`from(...)\` is not allowed after \`match(...)\` try using \`.from(...).to(...).in(...)\`. Optionally, you may see this because dates are incorrectly formatted. Dates should be a primative Date value, aka a number in milliseconds or Date object, ie new Date("2020-01-01"). Your query was not modified.`); return this; } this.rangeStack[0] = term; return this; } /** * Ends a range query. * * ```js * const query = new SearchQueryBuilder() * .from(yesterdaysDate) * .to(todaysDate) * .in("created") * ``` */ to(term) { if (this.hasTerms) { (0, arcgis_rest_request_1.warn)( // apparently-p*rettier-ignore causes prettier to strip *all* comments O_o `\`to(...)\` is not allowed after \`match(...)\` try using \`.from(...).to(...).in(...)\`. Optionally, you may see this because dates are incorrectly formatted. Dates should be a primative Date value, aka a number in milliseconds or Date object, ie new Date("2020-01-01"). Your query was not modified.`); return this; } this.rangeStack[1] = term; return this; } /** * Boosts the previous term to increase its rank in the results. * * ```js * const query = new SearchQueryBuilder() * .match("Lakes") * .in("title") * .or() * .match("Rivers") * .in("title") * .boost(3) * ``` */ boost(num) { this.commit(); this.q += `^${num}`; return this; } /** * Returns the current query string. Called internally when the request is made. */ toParam() { this.commit(); this.cleanup(); return this.q; } /** * Returns a new instance of `SearchQueryBuilder` based on the current instance. */ clone() { this.commit(); this.cleanup(); return new SearchQueryBuilder(this.q + ""); } addModifier(modifier) { if (this.currentModifer) { (0, arcgis_rest_request_1.warn)( // apparently-p*rettier-ignore causes prettier to strip *all* comments O_o `You have called \`${this.currentModifer}()\` after \`${modifier}()\`. Your current query was not modified.`); return this; } this.commit(); if (this.q === "" && modifier !== "not") { (0, arcgis_rest_request_1.warn)(`You have called \`${modifier}()\` without calling another method to modify your query first. Try calling \`match()\` first.`); return this; } this.currentModifer = modifier; this.q += this.q === "" ? "" : " "; this.q += `${modifier.toUpperCase()} `; return this; } hasWhiteSpace(s) { return /\s/g.test(s); } formatTerm(term) { if (term instanceof Date) { return term.getTime(); } if (typeof term === "string" && this.hasWhiteSpace(term)) { return `"${term}"`; } return term; } commit() { this.currentModifer = undefined; if (this.hasRange) { this.q += `[${this.formatTerm(this.rangeStack[0])} TO ${this.formatTerm(this.rangeStack[1])}]`; this.rangeStack = [undefined, undefined]; } if (this.hasTerms) { this.q += this.termStack .map((term) => { return this.formatTerm(term); }) .join(" "); this.termStack = []; } return this; } get hasTerms() { return this.termStack.length > 0; } get hasRange() { return this.rangeStack.length && this.rangeStack[0] && this.rangeStack[1]; } cleanup() { // end a group if we have started one if (this.openGroups > 0) { (0, arcgis_rest_request_1.warn)( // apparently-p*rettier-ignore causes prettier to strip *all* comments O_o `Automatically closing ${this.openGroups} group(s). You can use \`endGroup(...)\` to remove this warning.`); while (this.openGroups > 0) { this.q += ")"; this.openGroups--; } } const oldQ = this.q; this.q = oldQ.replace(/( AND ?| NOT ?| OR ?)*$/, ""); if (oldQ !== this.q) { (0, arcgis_rest_request_1.warn)(`\`startGroup(...)\` was called without calling \`endGroup(...)\` first. Your query was not modified.`); } // clear empty groups this.q = this.q.replace(/(\(\))*/, ""); } } exports.SearchQueryBuilder = SearchQueryBuilder; //# sourceMappingURL=SearchQueryBuilder.js.map