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
JavaScript
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;
}
}