UNPKG

blow-query

Version:

Programmatically build queries which can be returned as JSON Object and used to fetch data from many sources.

275 lines (245 loc) 6.79 kB
'use strict'; import {isString, isObject, isUndefined, isArray} from 'util'; import {QueryRaw, QuerySort, QueryWhere} from './interfaces'; /** * Query object allows to to programmatically build queries which can be returned * as JSON Object and used to fetch data from database, api endpoint etc. */ export class Query { protected _where: QueryWhere; protected _limit: number; protected _skip: number; protected _sort: QuerySort; protected _select: string[]; /** * Create instance of Query. */ constructor(query?: QueryRaw | Query) { if (!isUndefined(query)) { if (query instanceof Query) { query = (<Query>query).toJSON(); } } const q: QueryRaw = <QueryRaw>(query || {}); this._where = q.where || {}; this._limit = q.limit || -1; this._skip = q.skip || 0; this._sort = q.sort || {}; this._select = q.select || []; } protected _addCondition(field: string, condition: string, value: any): Query { if (isUndefined(this._where[field]) || isString(this._where[field])) { this._where[field] = {}; } this._where[field][condition] = value; return this; } protected _sortItem(field, direction): QuerySort { const item: QuerySort = {}; item[field] = direction; return item; } /** * Add condition which requires that field's value * to be equal to provided value */ equal(field: string, value: any): Query { this._where[field] = value; return this; } /** * Add condition which requires that field's value * to be not equal to provided value */ notEqual(field: string, value: any): Query { return this._addCondition(field, '$neq', value); } /** * Add condition which requires that field's value * to be less than provided value */ lessThan(field: string, value: any): Query { return this._addCondition(field, '$lt', value); } /** * Add condition which requires that field's value * to be less than or equal to provided value */ lessThanOrEqual(field: string, value: any): Query { return this._addCondition(field, '$lte', value); } /** * Add condition which requires that field's value * to be greater than provided value */ greaterThan(field: string, value: any): Query { return this._addCondition(field, '$gt', value); } /** * Add condition which requires that field's value * to be greater than or equal to provided value */ greaterThanOrEqual(field: string, value: any): Query { return this._addCondition(field, '$gte', value); } /** * Add condition which requires that field's value * to be contained in list of provided values */ containedIn(field: string, values: any[]): Query { return this._addCondition(field, '$in', values); } /** * Add condition which requires that field's value * to be not contained in list of provided values */ notContainedIn(field: string, values: any[]): Query { return this._addCondition(field, '$nin', values); } /** * Add condition which requires that field's value * match provided regular expression */ regex(field: string, value: RegExp): Query { return this._addCondition(field, '$regex', value); } /** * Add condition which requires that field's value * contains provided value */ contains(field: string, value: string): Query { return this._addCondition(field, '$regex', value); } /** * Add condition which requires that field's value * starts with provided value */ startsWith(field: string, value: string): Query { return this._addCondition(field, '$regex', `^${value}`); } /** * Add condition which requires that field's value * ends with provided value */ endsWith(field: string, value: string): Query { return this._addCondition(field, '$regex', `${value}$`); } /** * Set field's name which will be used for sort. Results * will be sort in ascending order */ ascending(field: string): Query { Object.assign(this._sort, this._sortItem(field, 1)); return this; } /** * Set field's name which will be used for sort. Results * will be sort in descending order */ descending(field: string): Query { Object.assign(this._sort, this._sortItem(field, -1)); return this; } /** * Set number of results to skip before return any result. */ skip(skip: number): Query { this._skip = skip; return this; } /** * Set limit of results to return. */ limit(limit: number): Query { this._limit = limit; return this; } /** * Set fields which will be returned. */ select(fields: string[] | string): Query { if (!isArray(fields)) { fields = <string[]>[fields]; } this._select = this._select.concat(<string[]>fields || []); return this; } /** * Concat two queries with OR */ or(query: Query): Query { this._where = { $or: [this._where, query.toJSON().where] }; return this; } /** * Return JSON Object */ toJSON(): QueryRaw { const query: QueryRaw = {}; if (Object.keys(this._where).length) { query.where = this._where; } if (this._skip) { query.skip = this._skip; } if (this._limit > 0) { query.limit = this._limit; } if (this._select.length > 0) { query.select = this._select; } if (Object.keys(this._sort).length) { query.sort = this._sort; } return Object.assign({}, query); } static create() { return new Query(); } static from(input: any): Query { const query: QueryRaw = { where: {} }; if (isString(input)) { try { input = JSON.parse(input); } catch (e) { throw new Error('Invalid input data for query.'); } } if (isObject(input)) { if (input.where) { try { input.where = JSON.parse(input.where); } catch (e) { throw new Error('Invalid input data for query(where).'); } if (isObject(input.where)) { query.where = input.where; } } if (input.limit) { query.limit = parseInt(input.limit); } if (input.skip) { query.skip = parseInt(input.skip); } if (input.sort) { try { input.sort = JSON.parse(input.sort); } catch (e) { throw new Error('Invalid input data for query(sort).'); } if (isObject(input.sort)) { query.sort = {}; query.sort = Object.keys(input.sort).reduce((s, c) => { s[c] = parseInt(input.sort[c]); return s; }, query.sort); } } if (input.select) { query.select = input.select; } } return new Query(query); } }