@mas-soft/mas-core-server
Version:
main application
268 lines • 7.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert = require("assert");
const nonWhereFields = [
'fields',
'order',
'limit',
'skip',
'offset',
'include',
];
const filterFields = ['where', ...nonWhereFields];
function isFilter(candidate) {
if (typeof candidate !== 'object')
return false;
for (const key in candidate) {
if (!filterFields.includes(key)) {
return false;
}
}
return true;
}
exports.isFilter = isFilter;
class WhereBuilder {
constructor(w) {
this.where = w || {};
}
add(w) {
for (const k of Object.keys(w)) {
if (k in this.where) {
const where = { and: [this.where, w] };
this.where = where;
return this;
}
}
this.where = Object.assign(this.where, w);
return this;
}
cast(clause) {
return clause;
}
and(...w) {
let clauses = [];
w.forEach(where => {
clauses = clauses.concat(Array.isArray(where) ? where : [where]);
});
return this.add({ and: clauses });
}
or(...w) {
let clauses = [];
w.forEach(where => {
clauses = clauses.concat(Array.isArray(where) ? where : [where]);
});
return this.add({ or: clauses });
}
eq(key, val) {
const w = {};
w[key] = val;
return this.add(w);
}
neq(key, val) {
const w = {};
w[key] = { neq: val };
return this.add(w);
}
gt(key, val) {
const w = {};
w[key] = { gt: val };
return this.add(w);
}
gte(key, val) {
const w = {};
w[key] = { gte: val };
return this.add(w);
}
lt(key, val) {
const w = {};
w[key] = { lt: val };
return this.add(w);
}
lte(key, val) {
const w = {};
w[key] = { lte: val };
return this.add(w);
}
inq(key, val) {
const w = {};
w[key] = { inq: val };
return this.add(w);
}
nin(key, val) {
const w = {};
w[key] = { nin: val };
return this.add(w);
}
between(key, val1, val2) {
const w = {};
w[key] = { between: [val1, val2] };
return this.add(w);
}
exists(key, val) {
const w = {};
w[key] = { exists: !!val || val == null };
return this.add(w);
}
impose(where) {
if (!this.where) {
this.where = where || {};
}
else {
this.add(where);
}
return this;
}
build() {
return this.where;
}
}
exports.WhereBuilder = WhereBuilder;
class FilterBuilder {
constructor(f) {
this.filter = f || {};
}
limit(limit) {
assert(limit >= 1, `Limit ${limit} must a positive number`);
this.filter.limit = limit;
return this;
}
offset(offset) {
this.filter.offset = offset;
return this;
}
skip(skip) {
return this.offset(skip);
}
fields(...f) {
if (!this.filter.fields) {
this.filter.fields = {};
}
const fields = this.filter.fields;
for (const field of f) {
if (Array.isArray(field)) {
field.forEach(i => (fields[i] = true));
}
else if (typeof field === 'string') {
fields[field] = true;
}
else {
Object.assign(fields, field);
}
}
return this;
}
validateOrder(order) {
assert(order.match(/^[^\s]+( (ASC|DESC))?$/), 'Invalid order: ' + order);
}
order(...o) {
if (!this.filter.order) {
this.filter.order = [];
}
o.forEach(order => {
if (typeof order === 'string') {
this.validateOrder(order);
if (!order.endsWith(' ASC') && !order.endsWith(' DESC')) {
order = order + ' ASC';
}
this.filter.order.push(order);
return this;
}
if (Array.isArray(order)) {
order.forEach(this.validateOrder);
order = order.map(i => {
if (!i.endsWith(' ASC') && !i.endsWith(' DESC')) {
i = i + ' ASC';
}
return i;
});
this.filter.order = this.filter.order.concat(order);
return this;
}
for (const i in order) {
this.filter.order.push(`${i} ${order[i]}`);
}
});
return this;
}
include(...i) {
if (this.filter.include == null) {
this.filter.include = [];
}
for (const include of i) {
if (typeof include === 'string') {
this.filter.include.push({ relation: include });
}
else if (Array.isArray(include)) {
for (const inc of include)
this.filter.include.push({ relation: inc });
}
else {
this.filter.include.push(include);
}
}
return this;
}
where(w) {
this.filter.where = w;
return this;
}
impose(constraint) {
if (!this.filter) {
if (!isFilter(constraint)) {
constraint = { where: constraint };
}
this.filter = constraint || {};
}
else {
if (isFilter(constraint)) {
for (const key of Object.keys(constraint)) {
if (nonWhereFields.includes(key)) {
throw new Error('merging strategy for selection, pagination, and sorting not implemented');
}
}
}
this.filter.where = isFilter(constraint)
? new WhereBuilder(this.filter.where).impose(constraint.where).build()
: new WhereBuilder(this.filter.where).impose(constraint).build();
}
return this;
}
build() {
return this.filter;
}
}
exports.FilterBuilder = FilterBuilder;
function getDeepProperty(value, path) {
const props = path.split('.');
for (const p of props) {
value = value[p];
if (value == null) {
return null;
}
}
return value;
}
function filterTemplate(strings, ...keys) {
return function filter(ctx) {
const tokens = [strings[0]];
keys.forEach((key, i) => {
if (typeof key === 'object' ||
typeof key === 'boolean' ||
typeof key === 'number') {
tokens.push(JSON.stringify(key), strings[i + 1]);
return;
}
const value = getDeepProperty(ctx, key);
tokens.push(JSON.stringify(value), strings[i + 1]);
});
const result = tokens.join('');
try {
return JSON.parse(result);
}
catch (e) {
throw new Error('Invalid JSON: ' + result);
}
};
}
exports.filterTemplate = filterTemplate;
//# sourceMappingURL=query.js.map