From 69039945d64f59e2591a94bffdb31519c7966dd5 Mon Sep 17 00:00:00 2001 From: Aneurin Barker Snook Date: Tue, 18 Oct 2022 21:27:50 +0100 Subject: [PATCH] build dist --- dist/lib/index.d.ts | 160 +++++++++++++++++++++++++++ dist/lib/index.js | 262 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 dist/lib/index.d.ts create mode 100644 dist/lib/index.js diff --git a/dist/lib/index.d.ts b/dist/lib/index.d.ts new file mode 100644 index 0000000..9dc702f --- /dev/null +++ b/dist/lib/index.d.ts @@ -0,0 +1,160 @@ +import { Document } from 'arangojs/documents'; +import { DocumentCollection } from 'arangojs/collection'; +import { GeneratedAqlQuery } from 'arangojs/aql'; +import { Database } from 'arangojs'; +/** + * A `CountFn` function returns the number of documents in a single collection matching search `terms` given. + * + * `count()` provides the standard implementation. + */ +export declare type CountFn = (terms?: Terms>>) => Promise; +/** + * Recursively renders all of a complex object's properties required, non-null, and non-undefined. + * + * Note: this type recurses through objects only, not arrays. + */ +export declare type DeepNonNullable = NonNullable extends object ? { + [P in keyof T]-?: DeepNonNullable; +} : T; +/** Query sort direction. */ +export declare type Direction = 'ASC' | 'DESC'; +/** + * Simple search filter type in which any number of conditions can be specified for a presumed parameter. + * + * Multiple operators can be used in combination. + * By default they will be combined into an `AND` filter. + * Specify `mode: "OR"` to switch to an `OR` filter. + * + * Note: `LIKE`, `NOT LIKE`, and regular expression operators are only available if `T` extends `string`. + */ +export declare type Filter = { + mode?: 'AND' | 'OR'; + eq?: T | null; + gt?: T; + gte?: T; + in?: T[]; + like?: T & string; + lt?: T; + lte?: T; + neq?: T | null; + nin?: T[]; + nlike?: T & string; + nreg?: T & string; + reg?: T & string; +}; +/** + * A `FindFn` function returns the first document in a single collection matching search `terms` given. + * A `sort` can be specified to control the result. + * + * `find()` provides the standard implementation. + */ +export declare type FindFn = (terms?: Terms>>, sort?: Sort>[] | Sort>) => Promise | undefined>; +/** + * Query limit. + * Always a tuple, but the second value can be omitted. + */ +export declare type Limit = [number, number?]; +/** + * Searchable data type. + * Essentially, any object is valid. + */ +export declare type Searchable = Record; +/** + * Formulate search terms based on a complex data type. + * Nested object types are preserved, while scalar properties are converted to `Filter` representations. + */ +export declare type Terms = { + [P in keyof T]?: NonNullable extends object ? Terms : Filter>; +}; +/** + * A `SearchFn` function matches documents in a single collection and returns a `SearchResult` based on the given + * `terms`, `limit`, and `sort`. + */ +export declare type SearchFn = (terms?: Terms>>, limit?: Limit, sort?: Sort>[] | Sort>) => Promise>; +/** + * Search results are a tuple of three values: + * 1. The **total** number of matching documents in the searched collection, ignoring limit + * 2. The documents matched within the searched collection, respecting limit + * 3. The AQL query object for the latter (for debugging purposes) + */ +export declare type SearchResult = [number, Document[], GeneratedAqlQuery]; +/** Query sort order. */ +export declare type Sort = [keyof T, Direction]; +/** Format scalar or scalar array data for use in AQL. */ +export declare const formatData: (data: T | T[]) => string; +/** Format scalar data for use in AQL. */ +export declare const formatValue: (data: T) => string; +/** Map of search operator properties to AQL equivalents. */ +export declare const operatorMap: Record, 'mode'>, string>; +/** Search operators. */ +export declare const operators: string[]; +/** + * Parse a search limit to a string AQL limit. + * + * Note: `LIMIT` is not prepended. + */ +export declare const parseLimit: (l: Limit) => string | number; +/** + * Parse a search filter to a string of AQL filters. + * + * Note: `FILTER` is not prepended. + */ +export declare const parseFilter: (param: string, search: Filter) => string; +/** + * Parse query sort(s) to an array of string AQL sorts. + * + * Note: `SORT` is not prepended. + */ +export declare const parseSort: (s: Sort | Sort[], parent: string) => string[]; +/** + * Parse search terms to a flat array of search filters. + * The `parent` argument refers to the current document, and is prefixed to each filter. + */ +export declare const parseTerms: (s: Terms, parent: string) => string[]; +/** + * Build and execute a count query that matches documents in a single collection. + * Returns the total number of matches. + * + * This example resembles the generated AQL query: + * + * ```aql + * FOR {i} IN {c} {FILTER ...} COLLECT WITH COUNT INTO {n} RETURN {n} + * ``` + */ +export declare const count: (db: Database, c: DocumentCollection, i?: string, n?: string) => CountFn; +/** + * Build and execute a find query that returns the first matching document in a single collection. + * + * This example resembles the generated AQL query: + * + * ```aql + * FOR {i} IN {collection} {FILTER ...} {SORT ...} LIMIT 1 RETURN {i} + * ``` + */ +export declare const find: (db: Database, c: DocumentCollection, i?: string) => FindFn; +/** + * Build and execute a search query across a single collection. + * Returns a `SearchResult` tuple containing the total number of matches (ignoring limit), all matching documents + * (respecting limit), and the AQL query. + * + * This example resembles the generated AQL query: + * + * ```aql + * FOR {i} IN {collection} {FILTER ...} {SORT ...} {LIMIT ...} RETURN {i} + * ``` + */ +export declare const search: (db: Database, c: DocumentCollection, i?: string, n?: string) => SearchFn; +declare const _default: { + count: (db: Database, c: DocumentCollection, i?: string, n?: string) => CountFn; + find: (db: Database, c: DocumentCollection, i?: string) => FindFn; + formatData: (data: T_2 | T_2[]) => string; + formatValue: (data: T_3) => string; + operatorMap: Record<"eq" | "gt" | "gte" | "in" | "like" | "lt" | "lte" | "neq" | "nin" | "nlike" | "nreg" | "reg", string>; + operators: string[]; + parseFilter: (param: string, search: Filter) => string; + parseLimit: (l: Limit) => string | number; + parseSort: (s: Sort | Sort[], parent: string) => string[]; + parseTerms: (s: Terms, parent: string) => string[]; + search: (db: Database, c: DocumentCollection, i?: string, n?: string) => SearchFn; +}; +export default _default; diff --git a/dist/lib/index.js b/dist/lib/index.js new file mode 100644 index 0000000..fa15405 --- /dev/null +++ b/dist/lib/index.js @@ -0,0 +1,262 @@ +"use strict"; +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.search = exports.find = exports.count = exports.parseTerms = exports.parseSort = exports.parseFilter = exports.parseLimit = exports.operators = exports.operatorMap = exports.formatValue = exports.formatData = void 0; +var arangojs_1 = require("arangojs"); +/** Format scalar or scalar array data for use in AQL. */ +var formatData = function (data) { + return data instanceof Array ? "[".concat(data.map(exports.formatValue).join(','), "]") : (0, exports.formatValue)(data); +}; +exports.formatData = formatData; +/** Format scalar data for use in AQL. */ +var formatValue = function (data) { + if (typeof data === 'string') + return "\"".concat(data, "\""); + if (data === null) + return 'null'; + return "".concat(data); +}; +exports.formatValue = formatValue; +/** Map of search operator properties to AQL equivalents. */ +exports.operatorMap = { + eq: '==', + gt: '>', + gte: '>=', + "in": 'IN', + like: 'LIKE', + lt: '<', + lte: '<=', + neq: '!=', + nin: 'NOT IN', + nlike: 'NOT LIKE', + nreg: '!~', + reg: '=~' +}; +/** Search operators. */ +exports.operators = Object.keys(exports.operatorMap); +/** + * Parse a search limit to a string AQL limit. + * + * Note: `LIMIT` is not prepended. + */ +var parseLimit = function (l) { return l.length > 1 ? "".concat(l[0], ", ").concat(l[1]) : l[0]; }; +exports.parseLimit = parseLimit; +/** + * Parse a search filter to a string of AQL filters. + * + * Note: `FILTER` is not prepended. + */ +var parseFilter = function (param, search) { return parseFilterOps(search) + .map(function (_a) { + var op = _a[0], data = _a[1]; + return "".concat(param, " ").concat(op, " ").concat((0, exports.formatData)(data)); +}) + .join(" ".concat(search.mode || 'AND', " ")); }; +exports.parseFilter = parseFilter; +/** Parse search parameter object to FILTER statement(s). */ +var parseFilterOps = function (search) { + return Object.keys(search).map(function (key) { + if (key === 'mode' || search[key] === undefined) + return undefined; + if (exports.operatorMap[key] === undefined) + throw new Error('unrecognised search operator'); + return [exports.operatorMap[key], search[key]]; + }).filter(Boolean); +}; +/** + * Parse query sort(s) to an array of string AQL sorts. + * + * Note: `SORT` is not prepended. + */ +var parseSort = function (s, parent) { + if (s[0] instanceof Array) + return s.map(function (ss) { return "".concat(parent, ".").concat(String(ss[0]), " ").concat(ss[1]); }); + return ["".concat(parent, ".").concat(String(s[0]), " ").concat(s[1])]; +}; +exports.parseSort = parseSort; +/** + * Parse search terms to a flat array of search filters. + * The `parent` argument refers to the current document, and is prefixed to each filter. + */ +var parseTerms = function (s, parent) { return Object.keys(s) + .reduce(function (filters, param) { + var f = s[param]; + if (!f) + return filters; + if (Object.keys(f).find(function (k) { return k !== 'mode' && !exports.operators.includes(k); })) { + // object is nested + filters.push.apply(filters, (0, exports.parseTerms)(f, "".concat(parent, ".").concat(String(param)))); + } + else { + // object resembles a search parameter + filters.push((0, exports.parseFilter)("".concat(parent, ".").concat(String(param)), f)); + } + return filters; +}, []); }; +exports.parseTerms = parseTerms; +/** + * Build and execute a count query that matches documents in a single collection. + * Returns the total number of matches. + * + * This example resembles the generated AQL query: + * + * ```aql + * FOR {i} IN {c} {FILTER ...} COLLECT WITH COUNT INTO {n} RETURN {n} + * ``` + */ +var count = function (db, c, i, n) { + if (i === void 0) { i = 'i'; } + if (n === void 0) { n = 'n'; } + return function (terms) { return __awaiter(void 0, void 0, void 0, function () { + var filters, filterStr, l, countQuery; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + filters = terms && (0, exports.parseTerms)(terms, i); + filterStr = arangojs_1.aql.literal(filters ? filters.map(function (f) { return "FILTER ".concat(f); }).join(' ') : ''); + l = { i: arangojs_1.aql.literal(i), n: arangojs_1.aql.literal(n) }; + countQuery = (0, arangojs_1.aql)(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n FOR ", " IN ", "\n ", "\n COLLECT WITH COUNT INTO ", "\n RETURN ", "\n "], ["\n FOR ", " IN ", "\n ", "\n COLLECT WITH COUNT INTO ", "\n RETURN ", "\n "])), l.i, c, filterStr, l.n, l.n); + return [4 /*yield*/, db.query(countQuery)]; + case 1: return [4 /*yield*/, (_a.sent()).next()]; + case 2: return [2 /*return*/, _a.sent()]; + } + }); + }); }; +}; +exports.count = count; +/** + * Build and execute a find query that returns the first matching document in a single collection. + * + * This example resembles the generated AQL query: + * + * ```aql + * FOR {i} IN {collection} {FILTER ...} {SORT ...} LIMIT 1 RETURN {i} + * ``` + */ +var find = function (db, c, i) { + if (i === void 0) { i = 'i'; } + return function (terms, sort) { + if (sort === void 0) { sort = ['_key', 'ASC']; } + return __awaiter(void 0, void 0, void 0, function () { + var filters, filterStr, sortStr, l, query, data; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + filters = terms && (0, exports.parseTerms)(terms, 'i'); + filterStr = arangojs_1.aql.literal(filters ? filters.map(function (f) { return "FILTER ".concat(f); }).join(' ') : ''); + sortStr = arangojs_1.aql.literal(sort ? "SORT ".concat((0, exports.parseSort)(sort, 'i').join(', ')) : ''); + l = { i: arangojs_1.aql.literal(i) }; + query = (0, arangojs_1.aql)(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n FOR ", " IN ", "\n ", "\n ", "\n LIMIT 1\n RETURN ", "\n "], ["\n FOR ", " IN ", "\n ", "\n ", "\n LIMIT 1\n RETURN ", "\n "])), l.i, c, filterStr, sortStr, l.i); + return [4 /*yield*/, db.query(query)]; + case 1: return [4 /*yield*/, (_a.sent()).next()]; + case 2: + data = _a.sent(); + return [2 /*return*/, data]; + } + }); + }); + }; +}; +exports.find = find; +/** + * Build and execute a search query across a single collection. + * Returns a `SearchResult` tuple containing the total number of matches (ignoring limit), all matching documents + * (respecting limit), and the AQL query. + * + * This example resembles the generated AQL query: + * + * ```aql + * FOR {i} IN {collection} {FILTER ...} {SORT ...} {LIMIT ...} RETURN {i} + * ``` + */ +var search = function (db, c, i, n) { + if (i === void 0) { i = 'i'; } + if (n === void 0) { n = 'n'; } + return function (terms, limit, sort) { + if (sort === void 0) { sort = ['_rev', 'ASC']; } + return __awaiter(void 0, void 0, void 0, function () { + var filters, filterStr, limitStr, sortStr, l, count, countQuery, query, data; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + filters = terms && (0, exports.parseTerms)(terms, 'i'); + filterStr = arangojs_1.aql.literal(filters ? filters.map(function (f) { return "FILTER ".concat(f); }).join(' ') : ''); + limitStr = arangojs_1.aql.literal(limit ? "LIMIT ".concat((0, exports.parseLimit)(limit)) : ''); + sortStr = arangojs_1.aql.literal(sort ? "SORT ".concat((0, exports.parseSort)(sort, 'i').join(', ')) : ''); + l = { i: arangojs_1.aql.literal(i), n: arangojs_1.aql.literal(n) }; + count = 0; + if (!limit) return [3 /*break*/, 3]; + countQuery = (0, arangojs_1.aql)(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n FOR ", " IN ", "\n ", "\n COLLECT WITH COUNT INTO ", "\n RETURN ", "\n "], ["\n FOR ", " IN ", "\n ", "\n COLLECT WITH COUNT INTO ", "\n RETURN ", "\n "])), l.i, c, filterStr, l.n, l.n); + return [4 /*yield*/, db.query(countQuery)]; + case 1: return [4 /*yield*/, (_a.sent()).next()]; + case 2: + count = _a.sent(); + _a.label = 3; + case 3: + query = (0, arangojs_1.aql)(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n FOR ", " IN ", "\n ", "\n ", "\n ", "\n RETURN ", "\n "], ["\n FOR ", " IN ", "\n ", "\n ", "\n ", "\n RETURN ", "\n "])), l.i, c, filterStr, sortStr, limitStr, l.i); + return [4 /*yield*/, db.query(query)]; + case 4: return [4 /*yield*/, (_a.sent()).all()]; + case 5: + data = _a.sent(); + if (data.length > count) + count = data.length; + return [2 /*return*/, [count, data, query]]; + } + }); + }); + }; +}; +exports.search = search; +exports["default"] = { + count: exports.count, + find: exports.find, + formatData: exports.formatData, + formatValue: exports.formatValue, + operatorMap: exports.operatorMap, + operators: exports.operators, + parseFilter: exports.parseFilter, + parseLimit: exports.parseLimit, + parseSort: exports.parseSort, + parseTerms: exports.parseTerms, + search: exports.search +}; +var templateObject_1, templateObject_2, templateObject_3, templateObject_4;