UNPKG

huddle-record

Version:

Record is a Object-Relational Mapping (ORM) TypeScript plugin that makes it easy to work with data.

169 lines (168 loc) 6.99 kB
import QueryOperator from '../../src/orm/enums/queryOperator.js'; export default class Query { repository; key; queryResult = []; constructor(repository, key) { this.repository = repository; this.key = key; this.setup(); } setup() { if (!this.repository.datasetExists(this.key)) { this.queryResult = []; } else { this.queryResult = this.repository.data[this.key]; } } // filter the query result by function or key value pair with optional operator where(field, operator, value) { if (typeof field === 'string') { // operator can be an actual operator or the value, depending on if a operator is provided // if no operator is provided, the operator is assumed to be QueryOperator.Equal const actualValue = typeof value === 'undefined' ? operator : value; const actualOperator = typeof value === 'undefined' ? QueryOperator.EQUAL : operator; // set the query result equal to the current query result plus where the field is equal to the value this.queryResult = this.queryResult.filter(function (item) { switch (actualOperator) { case QueryOperator.EQUAL: return item[field] === actualValue; case QueryOperator.NOT_EQUAL: return item[field] !== actualValue; case QueryOperator.GREATER_THAN: return item[field] > actualValue; case QueryOperator.GREATER_THAN_OR_EQUAL: return item[field] >= actualValue; case QueryOperator.LESS_THAN: return item[field] < actualValue; case QueryOperator.LESS_THAN_OR_EQUAL: return item[field] <= actualValue; case QueryOperator.IN: return actualValue.includes(item[field]); case QueryOperator.NOT_IN: return !actualValue.includes(item[field]); case QueryOperator.CONTAINS: return item[field].includes(actualValue); case QueryOperator.DOES_NOT_CONTAIN: return !item[field].includes(actualValue); case QueryOperator.IS_NULL: return item[field] === null; case QueryOperator.IS_NOT_NULL: return item[field] !== null; default: return false; } }); } else { // return value of the filter might be false, in case the query is being extended let hasReturnValues = true; // set the query result equal to the current query result plus where function returns true const filteredResult = this.queryResult.filter((item) => { // call the function with the item and the query as parameters const filteredItemResult = field(item, this); // if we don't have a return value, the query is being extended if (typeof filteredItemResult === 'undefined') { hasReturnValues = false; } return field(item, this); }); // if we have return values, we can set the query result to the filtered result // otherwise, we can't set the query result to the filtered result, because the query is being extended if (hasReturnValues) { this.queryResult = filteredResult; } } // filter out duplicates this.queryResult = this.filterDuplicates(this.queryResult); return this; } // or where the query result orWhere(field, operator, value) { // get the current query result const currentQueryResult = this.queryResult; // temporarily reset query results to the repository data so that we can filter through all items this.setup(); // get the new query result const newQueryResult = this.where(field, operator, value).get(); // set the query result equal to the current query result plus the new query result const concattedQueryResult = currentQueryResult.concat(newQueryResult); // filter out duplicates this.queryResult = this.filterDuplicates(concattedQueryResult); return this; } // return the query result get() { return this.queryResult; } // return the first item in the query result first(amount = 1) { if (this.queryResult.length === 0) { return null; } return this.queryResult[0]; } // return the last item in the query result last(amount = 1) { if (this.queryResult.length === 0) { return null; } return this.queryResult[this.queryResult.length - 1]; } // only grab the first x items in the query result limit(amount = 1) { this.queryResult = this.queryResult.slice(0, amount); return this; } update(data) { // update the query result by the given data this.queryResult.forEach((item) => { item.beforeUpdate(data); this.repository.deepUpdate(item, data); item.afterUpdate(item); }); return this.queryResult; } // delete the query result from the repository by mapping the id's to the repositories delete method delete() { this.repository.delete(this.queryResult.map((item) => item.id)); } // return whether a query result exists exists() { return this.queryResult.length > 0; } // return the count of the query result count() { return this.queryResult.length; } // filter out duplicates by id and return the result filterDuplicates(items) { return items.filter((v, i, a) => a.findIndex((v2) => v2.id === v.id) === i); } // order by the given field and direction orderBy(field, direction = 'asc') { // if the field is a function, we can't sort the query result if (typeof field === 'string') { this.queryResult = [...this.queryResult].sort((a, b) => { if (direction === 'asc') { return a[field] > b[field] ? 1 : -1; } else { return a[field] < b[field] ? 1 : -1; } }); } else { this.queryResult = [...this.queryResult].sort((a, b) => { if (direction === 'asc') { return field(a) > field(b) ? 1 : -1; } else { return field(a) < field(b) ? 1 : -1; } }); } return this; } }