ForgeTotal.js 5
en

Total.js 5

QueryBuilder operations, filters and driver contract

Total.js 5 · total5@0.0.19 total5reference Executed against Total.js 5

This page covers the native total5@0.0.19 QueryBuilder (querybuilder.js). DB() returns a queue controller. Each operation schedules a driver call; append filters and callbacks synchronously before it executes. NEWDB(name, driver) registers a connection. NEWDB(name) unregisters it. There is no implicit database engine or transaction in this queue.

The example driver returns the received options. To execute a database operation, implement that operation in the driver. For actual SQL execution and rollback see query-builder-sqlite.

OperationsDriver receives
find/all(table), read/one(table), list(table)exec, table; read sets first:true, take:1; list defaults to take:100, skip:0
check(table), count(table)check sets first:1, take:1; the driver defines the result value
insert/ins(table, data)payload
update/modify/mod/upd(table, data, upsert)payload, upsert; a zero result triggers a second insert when upsert is enabled
remove/rem(table), truncate/clear(table), drop(table)The corresponding exec; destructive operations need driver support
scalar(table, type, key, key2)scalar:{type,key,key2}; omitted key becomes *
command(table, name)command:name
query(sql, params) or query(connection, sql, params)Raw text in query, values in params, table is empty; SQL binding is the driver's responsibility
Query methodsOptions / behavior
where(name, [comparer], value), equal(object), id(value), userid(value)Equality shorthand; == becomes =, != becomes ; array IDs use in
in/notin(name, values, [idField]), between(name,a,b), or(fn)Filter records; or groups the records added inside fn(builder)
array(name,[comparer],value)Default comparer &&; interpretation belongs to driver
month/day/year/hour/minute(name,[comparer],value)Date-part filters; default comparer =
search(name,value,[where]), contains(name), empty(name)Filter records; matching semantics belong to driver
query(fragment), join(name,table,type,a,b), permit(name,type,values,userid,required)Driver-specific filters; raw fragments must come from trusted application code
fields(csv), returning(csv)Arrays of trimmed field names, not SQL escaping or authorization
sort(name, descending), gridsort(csv,[localized])Sort strings; true or desc select descending
take/limit(n), skip(n), page(page,limit), paginate(page,limit,max)Offsets/limits; paginate caps the limit, page does not
first(), primarykey(name), schema(name), language(value,prefix,skip), debug()Metadata for driver; first also unwraps an array result
gridfilter(name,query,type,[key]), autoquery(query,schema,sort,max)Convert grid query parameters into options. autoquery restricts requested fields and sort keys to its declared schema
gridfields(fields,allowed)Use fields() with your own allowlist: it is the tested way to choose the columns
callback/pipe(fn), promise(), done($,fn,param)Completion; promises reject on driver error
data(fn), fail(fn), error/err(message,reverse)Success/failure hooks; error() rejects empty results, reverse:true rejects truthy results (including an empty array)
set(name), nobind()Name the queued result, or do not replace queue output
insert(fn,param) on an update builderMutate the payload before the fallback insert of an upsert
audit($,message,type)Calls the supplied context's variables() and audit() after a successful driver response

Controller methods: callback/pipe, promise, done, debug, language, load(connection, options) and exec(options, callback). load copies a defined subset of serialized options; exec dispatches supported operation names. next/free are lifecycle internals. A queue stops after a driver error; it does not undo earlier writes.

definitions/contract.js
NEWDB('contract', function(options, callback) {
	if (options.table === 'failure')
		return callback(new Error('driver failed'));
	if (options.table === 'empty')
		return callback(null, []);
	if (options.table === 'upsert') {
		MAIN.upsert.push(JSON.parse(JSON.stringify(options)));
		return callback(null, options.exec === 'update' ? 0 : options.payload);
	}
	if (options.exec === 'list')
		return callback(null, { items: [], count: 12 });
	callback(null, options.first ? [options] : options);
});
MAIN.upsert = [];
controllers/contract.js
exports.install = function() {
	ROUTE('GET /contract/', contract);
};

async function contract($) {
	try {
		var out = {};
		var methods = ['find', 'all', 'read', 'one', 'list', 'check', 'count', 'insert', 'ins', 'update', 'modify', 'mod', 'upd', 'remove', 'rem', 'truncate', 'clear', 'drop'];
		out.operations = {};
		for (var method of methods)
			out.operations[method] = await DB()[method]('contract/items', { name: 'Ann' }).promise();
		out.scalar = await DB().scalar('contract/items', 'sum', 'amount', 'group').promise();
		out.command = await DB().command('contract/items', 'vacuum').promise();
		out.raw = await DB().query('contract', 'SELECT ?', [7]).promise();
		out.filters = await DB().find('contract/items').id([1, 2]).userid({ id: 'u1' }).equal({ active: true }).where('age', '!=', 0).in('team', [{ id: 'a' }], 'id').notin('team', 'b').between('age', 18, 65).or(function(q) { q.where('role', 'a').where('role', 'b'); }).array('tags', ['x']).month('created', 1).day('created', 2).year('created', '>=', 2020).hour('created', 3).minute('created', 4).search('name', 'ann', 'beg').contains('email').empty('deleted').query('trusted_fragment').join('team', 'teams', 'left', 'a.id', 'b.id').permit('permissions', 'RW', ['team'], 'u1', false).promise();
		out.options = await DB().find('contract/items').fields('id, name').returning('id, name').sort('name', true).gridsort('id_asc').limit(5).skip(2).primarykey('uid').schema('public').language({ language: 'es' }, '_').debug().promise();
		out.page = await DB().list('contract/items').paginate('2', '500', 10).promise();
		out.grid = await DB().find('contract/items').autoquery({ fields: 'id,password', sort: 'password_desc,name_desc', page: '2', limit: '500', age: '>18' }, 'id:String,name:String,age:Number', 'id_asc', 10).promise();
		out.gridfields = await DB().find('contract/items').gridfields('id', 'id,name').promise();
		var queue = DB();
		queue.read('contract/items').set('one');
		queue.count('contract/items').set('count');
		queue.find('contract/items').nobind();
		out.queue = await queue.promise();
		var hooks = [];
		await DB().read('contract/empty').data(function(v) { hooks.push(v === null ? 'null' : 'value'); }).promise();
		try { await DB().read('contract/empty').error('missing').fail(function() { hooks.push('fail'); }).promise(); } catch (e) { out.missing = String(e); }
		try { await DB().find('contract/failure').promise(); } catch (e) { out.failure = String(e); }
		out.hooks = hooks;
		out.upsert = await DB().update('contract/upsert', { name: 'new' }, true).where('id', 7).insert(function(payload, role) { payload.role = role; }, 'member').promise();
		out.upsertCalls = MAIN.upsert;
		out.load = await DB().load('contract', { table: 'items', exec: 'find', take: 2, filter: [] }).promise();
		$.json(out);
	} catch (e) { $.invalid(e); }
}