@naturalcycles/db-lib
Version:
Lowest Common Denominator API to supported Databases
203 lines (202 loc) • 5.85 kB
JavaScript
import { _truncate } from '@naturalcycles/js-lib/string/string.util.js';
import { _objectAssign } from '@naturalcycles/js-lib/types';
export const dbQueryFilterOperatorValues = [
'<',
'<=',
'==',
'!=',
'>=',
'>',
'in',
'not-in',
'array-contains',
'array-contains-any',
];
/**
* Lowest Common Denominator Query object.
* To be executed by CommonDao / CommonDB.
*
* Fluent API (returns `this` after each method).
*
* Methods do MUTATE the query object, be careful.
*
* <DBM> is the type of **queried** object (so e.g `key of DBM` can be used), not **returned** object.
*/
export class DBQuery {
table;
constructor(table) {
this.table = table;
}
/**
* Convenience method.
*/
static create(table) {
return new DBQuery(table);
}
static fromPlainObject(obj) {
return Object.assign(new DBQuery(obj.table), obj);
}
_filters = [];
_limitValue = 0; // 0 means "no limit"
_offsetValue = 0; // 0 means "no offset"
_orders = [];
_startCursor;
_endCursor;
/**
* If defined - only those fields will be selected.
* In undefined - all fields (*) will be returned.
*/
_selectedFieldNames;
_groupByFieldNames;
_distinct = false;
filter(name, op, val) {
this._filters.push({ name, op, val });
return this;
}
filterEq(name, val) {
this._filters.push({ name, op: '==', val });
return this;
}
filterIn(name, val) {
this._filters.push({ name, op: 'in', val });
return this;
}
/**
* Passing 0 means "no limit".
*/
limit(limit) {
this._limitValue = limit;
return this;
}
offset(offset) {
this._offsetValue = offset;
return this;
}
order(name, descending) {
this._orders.push({
name,
descending,
});
return this;
}
select(fieldNames) {
this._selectedFieldNames = fieldNames;
return this;
}
groupBy(fieldNames) {
this._groupByFieldNames = fieldNames;
return this;
}
distinct(distinct = true) {
this._distinct = distinct;
return this;
}
startCursor(startCursor) {
this._startCursor = startCursor;
return this;
}
endCursor(endCursor) {
this._endCursor = endCursor;
return this;
}
clone() {
return _objectAssign(new DBQuery(this.table), {
_filters: [...this._filters],
_limitValue: this._limitValue,
_offsetValue: this._offsetValue,
_orders: [...this._orders],
_selectedFieldNames: this._selectedFieldNames && [...this._selectedFieldNames],
_groupByFieldNames: this._groupByFieldNames && [...this._groupByFieldNames],
_distinct: this._distinct,
_startCursor: this._startCursor,
_endCursor: this._endCursor,
});
}
pretty() {
return this.prettyConditions().join(', ');
}
prettyConditions() {
const tokens = [];
// if (this.name) {
// tokens.push(`"${this.name}"`)
// }
if (this._selectedFieldNames) {
tokens.push(`select${this._distinct ? ' distinct' : ''}(${this._selectedFieldNames.join(',')})`);
}
tokens.push(...this._filters.map(f => `${f.name}${f.op}${f.val}`), ...this._orders.map(o => `order by ${o.name}${o.descending ? ' desc' : ''}`));
if (this._groupByFieldNames) {
tokens.push(`groupBy(${this._groupByFieldNames.join(',')})`);
}
if (this._offsetValue) {
tokens.push(`offset ${this._offsetValue}`);
}
if (this._limitValue) {
tokens.push(`limit ${this._limitValue}`);
}
if (this._startCursor) {
tokens.push(`startCursor ${_truncate(this._startCursor, 8)}`);
}
if (this._endCursor) {
tokens.push(`endCursor ${_truncate(this._endCursor, 8)}`);
}
return tokens;
}
}
/**
* DBQuery that has additional method to support Fluent API style.
*/
export class RunnableDBQuery extends DBQuery {
dao;
/**
* Pass `table` to override table.
*/
constructor(dao, table) {
super(table || dao.cfg.table);
this.dao = dao;
}
async runQuery(opt) {
return await this.dao.runQuery(this, opt);
}
async runQuerySingleColumn(opt) {
return await this.dao.runQuerySingleColumn(this, opt);
}
async runQueryAsDBM(opt) {
return await this.dao.runQueryAsDBM(this, opt);
}
async runQueryExtended(opt) {
return await this.dao.runQueryExtended(this, opt);
}
async runQueryExtendedAsDBM(opt) {
return await this.dao.runQueryExtendedAsDBM(this, opt);
}
async runQueryCount(opt) {
return await this.dao.runQueryCount(this, opt);
}
async patchByQuery(patch, opt) {
return await this.dao.patchByQuery(this, patch, opt);
}
async streamQueryForEach(mapper, opt) {
await this.dao.streamQueryForEach(this, mapper, opt);
}
async streamQueryAsDBMForEach(mapper, opt) {
await this.dao.streamQueryAsDBMForEach(this, mapper, opt);
}
streamQuery(opt) {
return this.dao.streamQuery(this, opt);
}
streamQueryAsDBM(opt) {
return this.dao.streamQueryAsDBM(this, opt);
}
async queryIds(opt) {
return await this.dao.queryIds(this, opt);
}
streamQueryIds(opt) {
return this.dao.streamQueryIds(this, opt);
}
async streamQueryIdsForEach(mapper, opt) {
await this.dao.streamQueryIdsForEach(this, mapper, opt);
}
async deleteByQuery(opt) {
return await this.dao.deleteByQuery(this, opt);
}
}