UNPKG

ottoman

Version:
606 lines 17.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Query = void 0; const ottoman_errors_1 = require("../exceptions/ottoman-errors"); const schema_1 = require("../schema"); const type_helpers_1 = require("../utils/type-helpers"); const base_query_1 = require("./base-query"); const exceptions_1 = require("./exceptions"); const helpers_1 = require("./helpers"); const ottoman_1 = require("../ottoman/ottoman"); class Query extends base_query_1.BaseQuery { /** * @summary Create an instance of Query. * @name Query * @class * @public * * @param conditions List of SELECT clause conditions * @param collection Collection name * @returns Query * * @example * ```ts * const query = new Query({ * $select: [{ $field: 'address' }], * $where: { * $nill: [ * { address: { $like: '%57-59%' } }, * { free_breakfast: true }, * { free_lunch: [1] } * ] * } * }, * 'travel-sample'); * ``` */ constructor(conditions, collection) { super(conditions, collection); if (conditions) { this.compileFromConditions(conditions); } } /** * Add result selectors to SELECT clause. * @method * @public * * @example * ```ts * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]).build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * ``` */ select(value) { if (this.queryType === undefined || this.queryType === 'SELECT') { this.queryType = 'SELECT'; if (!value) { this.selectExpr = '*'; } else if (typeof value === 'string') { this.selectExpr = value; } else if (typeof value === 'object') { this.selectExpr = [...(this.selectExpr || []), ...value]; } return this; } throw new exceptions_1.MultipleQueryTypesException('SELECT', this.queryType); } /** * Add index type and name to INDEX clause. * @method * @public * * @example * ```ts * const result = new Query({}, 'travel-sample') * .index('DROP', 'travel_sample_id_test') * .build(); * console.log(result) * ``` * ```sql * DROP INDEX `travel-sample`.`travel_sample_id_test` * ``` */ index(type, name) { if (this.queryType === undefined) { if (name.search(/^[A-Za-z][A-Za-z0-9#_]*$/g) === -1) { throw new schema_1.ValidationError('Valid GSI index names can contain any of the following characters: A-Z a-z 0-9 # _, and must start with a letter, [A-Z a-z]'); } this.queryType = 'INDEX'; this.indexType = type; this.indexName = name; return this; } throw new exceptions_1.MultipleQueryTypesException('INDEX', this.queryType); } /** * Add items to ON clause in INDEX clause. * @method * @public * * @example * ```ts * const on = [{ name: 'travel-sample.callsing', sort: 'ASC' }]; * const result = new Query({}, 'travel-sample') * .index('CREATE', 'travel_sample_id_test') * .on(on) * .build(); * console.log(result) * ``` * ```sql * CREATE INDEX `travel_sample_id_test` ON `travel-sample` (`travel-sample.callsing`['ASC']) * ``` **/ on(value) { if (this.queryType === 'INDEX' && ['CREATE', 'CREATE PRIMARY', 'BUILD'].includes(this.indexType || '')) { this.indexOn = value; return this; } throw new exceptions_1.IndexParamsOnExceptions(['CREATE', 'CREATE PRIMARY', 'BUILD']); } /** * Create INDEX using General Secondary Index (GSI). * @method * @public * * @example * ```ts * const result = new Query({}, 'travel-sample') * .index('CREATE', 'travel_sample_id_test') * .usingGSI() * .build(); * console.log(result) * ``` * ```sql * CREATE INDEX `travel_sample_id_test` USING GSI * ``` **/ usingGSI() { if (this.queryType === 'INDEX') { this.indexUsingGSI = true; return this; } throw new exceptions_1.IndexParamsUsingGSIExceptions(['CREATE', 'CREATE PRIMARY', 'BUILD']); } /** * Add items to WITH clause in INDEX clause. * @method * @public * * @example * ```ts * const withExpr = { nodes: ['192.168.1.1:8078'], defer_build: true, num_replica: 2 }; * const result = new Query({}, 'travel-sample') * .index('CREATE', 'travel_sample_id_test') * .with(withExpr) * .build(); * console.log(result) * ``` * ```sql * CREATE INDEX `travel_sample_id_test` * WITH {'nodes': ['192.168.1.1:8078'],'defer_build': true,'num_replica': 2} * ``` **/ with(value) { if (this.queryType === 'INDEX') { this.indexWith = value; return this; } throw new ottoman_errors_1.BuildIndexQueryError('The WITH clause is only available for Indexes'); } /** * Add WHERE expression to SELECT clause. * @method * @public * * @example * ```ts * const expr_where = { * $or: [ * { address: { $like: '%57-59%' } }, * { free_breakfast: true }, * { name: { $eq: 'John', $ignoreCase: true } } * ] * }; * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .where(expr_where) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * WHERE (address LIKE "%57-59%" OR free_breakfast = true OR (LOWER(name) = LOWER("John"))) * ``` **/ where(value) { this.whereExpr = value; return this; } /** * Add JOIN expression to SELECT clause. * @method * @public * * @example * ```tS * const query = new Query({}, 'beer-sample brewery'); * const result = query.select([{ $field: 'address' }]) * .plainJoin( * 'JOIN `beer-sample` beer ON beer.brewery_id = LOWER(REPLACE(brewery.name, " ", "_"))' * ) * .build() * console.log(result) * ``` * ```sql * SELECT beer.name * FROM `beer-sample` brewery * JOIN `beer-sample` beer ON beer.brewery_id = LOWER(REPLACE(brewery.name, " ", "_")) * ``` */ plainJoin(value) { this.plainJoinExpr = value; return this; } /** * Add ORDER BY expression to SELECT clause. * @method * @public * * @example * ```ts * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .orderBy({ size: 'DESC' }) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * ORDER BY size = 'DESC' * ``` */ orderBy(value) { this.orderExpr = value; return this; } /** * Add LIMIT expression to SELECT clause. * @method * @public * * @example * ```ts * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .limit(10) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * LIMIT 10 * ``` **/ limit(value) { this.limitExpr = value; return this; } /** * Add OFFSET expression to SELECT clause. * @method * @public * * @example * ```ts * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .offset(10) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * OFFSET 10 * ``` **/ offset(value) { this.offSetExpr = value; return this; } /** * Add LET expression to SELECT clause. * @method * @public * * @example * * ```ts * // SELECT expression definition * const selectExpr = 't1.airportname, t1.geo.lat, t1.geo.lon, t1.city, t1.type'; * * // LET expression definition * const letExpr: LetExprType = { * min_lat: 71, * max_lat: 'ABS(t1.geo.lon)*4+1', * place: '(SELECT RAW t2.country FROM `travel-sample` t2 WHERE t2.type = "landmark")', * }; * * // WHERE expression definition * const whereExpr: LogicalWhereExpr = { * $and: [ * { 't1.type': 'airport' }, * { 't1.geo.lat': { $gt: { $field: 'min_lat' } } }, * { 't1.geo.lat': { $lt: { $field: 'max_lat' } } }, * { 't1.country': { $in: { $field: 'place' } } }, * ], * }; * * // QUERY creation * const query = new Query({}, 'travel-sample t1') * .select(selectExpr) * .let(letExpr) * .where(whereExpr) * .build(); * * console.log(query); * * // QUERY output * ``` * ```sql * SELECT t1.airportname, * t1.geo.lat, * t1.geo.lon, * t1.city, * t1.type * FROM `travel-sample` t1 * LET min_lat=71, * max_lat=ABS(t1.geo.lon)*4+1, * place=( * SELECT RAW t2.country * FROM `travel-sample` t2 * WHERE t2.type = "landmark") * WHERE (t1.type="airport" * AND t1.geo.lat>min_lat * AND t1.geo.lat<max_lat * AND t1.country IN place); * ``` * * ```ts * // OTTOMAN initialization * const ottoman = getDefaultInstance(); * await startInTest(ottoman); * * // QUERY execution * const { rows } = await ottoman.query(query); * * // RESULTS * console.log(rows) * ``` * ```sh * [ * { * airportname: 'Wiley Post Will Rogers Mem', * city: 'Barrow', * lat: 71.285446, * lon: -156.766003, * type: 'airport', * }, * { * airportname: 'Dillant Hopkins Airport', * city: 'Keene', * lat: 72.270833, * lon: 42.898333, * type: 'airport', * }, * ] * ``` */ let(value) { this.letExpr = value; return this; } /** * Add GROUP BY expression to SELECT clause. * @method * @public * * @example * ```ts * const groupByExpr = [{ expr: 'COUNT(amount_val)', as: 'amount' }]; * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .groupBy(groupByExpr) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * GROUP BY COUNT(amount) AS amount * ``` */ groupBy(value) { this.groupByExpr = value; return this; } /** * Add LETTING expression to GROUP BY clause. * @method * @public * * @example * ```ts * const groupByExpr = [{ expr: 'COUNT(amount_val)', as: 'amount' }]; * const letExpr = { amount_val: 10 }; * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .groupBy(groupByExpr) * .let(letExpr) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * GROUP BY COUNT(amount) AS amount LETTING amount = 10 * ``` */ letting(value) { this.lettingExpr = value; return this; } /** * Add HAVING expression to GROUP BY clause. * @method * @public * * @example * ```ts * const groupByExpr = [{ expr: 'COUNT(amount_val)', as: 'amount' }]; * const having = { address: { $like: '%58%' } }; * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .groupBy(groupByExpr) * .having(having) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * GROUP BY COUNT(amount) AS amount * HAVING address LIKE '%58%' * ``` */ having(value) { this.havingExpr = value; return this; } /** * Add USE KEYS expression to SELECT clause. * @method * @public * * @example * ```ts * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .useKeys(['airlineR_8093']) * .build() * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` USE KEYS ['airlineR_8093'] * ``` */ useKeys(value) { this.useKeysExpr = value; return this; } /** * Converts the conditional parameters passed to the constructor to the properties of the N1QL Query. * @method * @public * */ compileFromConditions(conditionals) { Object.keys(conditionals).forEach((value) => { switch (value) { case 'select': this.select(conditionals[value]); break; case 'let': !!conditionals[value] && this.let(conditionals[value]); break; case 'where': !!conditionals[value] && this.where(conditionals[value]); break; case 'plainJoin': !!conditionals[value] && this.plainJoin(conditionals[value]); break; case 'groupBy': !!conditionals[value] && this.groupBy(conditionals[value]); break; case 'letting': !!conditionals[value] && this.letting(conditionals[value]); break; case 'having': !!conditionals[value] && this.having(conditionals[value]); break; case 'orderBy': !!conditionals[value] && this.orderBy(conditionals[value]); break; case 'limit': { const limit = conditionals[value]; (0, type_helpers_1.isNumber)(limit) && this.limit(limit); break; } case 'offset': !!conditionals[value] && this.offset(conditionals[value]); break; case 'use': !!conditionals[value] && this.useKeys(conditionals[value]); break; } }); } /** * Build a N1QL query from the defined parameters. * * Can also use `ignoreCase` as part of the `build` method, this will always prioritize the `$ignoreCase` value defined in clause. * @example * ```ts * const expr_where = { * $or: [ * { address: { $like: '%57-59%', $ignoreCase: false } }, // ignoreCase will not be applied * { free_breakfast: true }, * { name: 'John' } // ignoreCase will be applied * ], * }; * const query = new Query({}, 'travel-sample'); * const result = query.select([{ $field: 'address' }]) * .where(expr_where) * .build({ ignoreCase: true }); // ignore case is enabled for where clause elements * console.log(result) * ``` * ```sql * SELECT address * FROM `travel-sample` * WHERE (address LIKE "%57-59%" OR free_breakfast = true OR `(LOWER(name) = LOWER("John"))`) * ``` * * @method * @public * */ build(options = { ignoreCase: false }) { switch (this.queryType) { case 'INDEX': switch (this.indexType) { case 'BUILD': case 'CREATE': case 'CREATE PRIMARY': if (this.indexOn) { return (0, helpers_1.buildIndexExpr)(this.collection, this.indexType, this.indexName || '', this.indexOn, this.whereExpr, this.indexUsingGSI, this.indexWith); } case 'DROP': return (0, helpers_1.buildIndexExpr)(this.collection, this.indexType, this.indexName || '', undefined, undefined, this.indexUsingGSI); } case 'SELECT': if (this.selectExpr) { return (0, helpers_1.selectBuilder)(this.collection, this.selectExpr, this.letExpr, this.whereExpr, this.orderExpr, this.limitExpr, this.offSetExpr, this.useKeysExpr, this.groupByExpr, this.lettingExpr, this.havingExpr, this.plainJoinExpr, options.ignoreCase); } } return ''; } execute(options = { ignoreCase: false }, queryOptions = {}, ottomanInstance = (0, ottoman_1.getDefaultInstance)()) { return ottomanInstance.query(this.build(options), queryOptions); } get conditions() { return this._conditions; } set conditions(value) { this._conditions = value; } get collection() { return this._collection; } set collection(value) { this._collection = value; } } exports.Query = Query; //# sourceMappingURL=query.js.map