New Total.js version v2.3.0
New Total.js v2.3.0 is 98% backwards compatible.
News
NoSQL embedded database
I have performed a big step to increase performance with a new great features. NoSQL embedded database can be used as a separated module $ npm install nosql
(it's without dependencies). This database is a great product for small projects.
In-memory mode:
- all documents are loaded into the memory and each change is stored on the file system
- performance is awesome
// For the whole DB
NOSQLMEMORY('products');
// For the view only in DB
NOSQLMEMORY('blogs', 'listing');
Joins:
- we can join documents between different databases
// One to one (each order will contain its user)
NOSQL('orders').find().make(function(builder) {
builder.where('ispaid', true);
builder.join('user', 'users').where('id', 'iduser').fields('name', 'email').first();
builder.callback(function(err, response) {
console.log(response);
});
});
// One to many (each user will contain array of addresses)
NOSQL('users').find().make(function(builder) {
builder.join('addresses', 'addresses').where('iduser', 'id');
builder.callback(function(err, response) {
console.log(response);
});
});
Scalar operations:
sum
, count
, min
, max
, avg
and group
- works with joins
NOSQL('orders').scalar('sum', 'price').callback(function(err, response) {
console.log(response);
});
// OR
NOSQL('orders').scalar('max', 'price').make(function(builder) {
builder.year('created', 2016);
builder.callback(function(err, response) {
console.log(response);
});
});
// OR with JOIN
NOSQL('users').find().make(function(builder) {
builder.join('amount', 'orders').where('iduser', 'id').scalar('sum', 'price');
builder.callback(function(err, response) {
console.log(response);
});
});
// OR group
NOSQL('users').scalar('group', 'gender').callback(function(err, response) {
// { male: 34, female: 59 }
console.log(response);
});
New date filters:
DatabaseBuilder.day(field, [operator], value)
DatabaseBuilder.month(field, [operator], value)
DatabaseBuilder.year(field, [operator], value)
NOSQL('orders').find().make(function(builder) {
builder.year('created', 2016);
builder.callback(function(err, response) {
console.log(response);
});
});
NOSQL('orders').find().make(function(builder) {
builder.year('created', '<', 2014);
builder.callback(function(err, response) {
console.log(response);
});
});
Random documents:
New version supports random
feature. DatabaseBuilder.random()
will return random documents.
NOSQL('products').find().take(20).random().callback(function(err, response) {
console.log(response);
});
Updated insert/update/modify operations:
// Database inserts a new document if the document does not exist with the same `email` field
NOSQL('newsletter').upsert({ email: 'petersirka@gmail.com', created: F.datetime }).where('email', 'petersirka@gmail.com');
// Database inserts `new_model` into the DB while `update_model` won't be updated
var update_model = { ... };
var new_model = { ... };
NOSQL('products').update(update_model, new_model).where('id', update_model.id);
// Database inserts `new_model` into the DB while `modify_model` won't be modified
var modify_model = { ... };
var new_model = { ... };
NOSQL('products').modify(modify_model, new_model).where('id', modify_model.id);
NoSQL Binary:
// Browsing in all stored files
NOSQL('files').binary.all(function(err, response) {
// e.g. [{ name: 'filename.jpg', size: 3498, type: 'image/jpeg', width: 500, height: 500, created: Date }]
console.log(response);
});
// Removing all files
NOSQL('files').binary.clear();
NoSQL events:
var db = NOSQL('products');
// Documents
db.on('insert', function(doc) {});
db.on('update', function(doc) {});
db.on('modify', function(doc) {});
db.on('remove', function(doc) {});
// Binary
db.binary.on('insert', function(id, header) {});
db.binary.on('remove', function(id) {});
db.binary.on('clear', function() {});
// Counter
db.counter.on('hit', function(id, count) {});
db.counter.on('remove', function(id) {});
db.counter.on('clear', function() {});
Workflows
This is a new feature in this version. Workflows describes SchemaBuilder
operations in a file named /workflows
with the same structure as the /sitemap
file. Controller
contains a new schema method controller.$exec(workflow_name, callback)
which executes all workflow
operations in order, e.g. /workflows
:
save : workflow:'notify' --> save (response) --> workflow:'notify'
save (merchant) : workflow:'notify' --> save --> workflow:'notify' --> workflow:'bill' (response)
Controlller:
exports.install = function() {
// Performs "save" workflow automatically
// Output: "json"
F.route('/api/users/create/', ['*User --> save', 'post']);
F.route('/api/clients/create/', ['*Client --> save', 'post']);
// Output: "view" with a model
F.route('/api/merchant/create/', 'success', ['*Merchant --> save', 'post']);
// Output: user defined
F.route('/api/merchant/create/', json_customer_create, ['*Customer', 'post']);
};
function json_customer_create() {
var self = this;
self.$exec('save', function(err, response) {
self.json(err ? err : response);
});
}
IMPORTANT: (response)
keyword means that controller.$exec()
returns only the one result from this operation. Otherwise returns array of results from operations.
New SchemaBuilder types
NEWSCHEMA('Product').make(function(schema) {
// ENUM:
// A value of size has to be according the array below (works with numbers too):
schema.define('size', ['XS', 'S', 'M', 'L', 'XL', 'XXL']);
// KEY & VALUE
// A value can contain "billing" or "postal" value
schema.define('address', { billing: 'Billing address', postal: 'Postal address' });
});
Describing SchemaBuilder operations
schema.addWorkflow('check', function(error, model, options, callback) {
// ... user-defined code ...
}, 'Workflow checks whether user exists or not.');
schema.addTransform('xml', function(error, model, options, callback) {
// ... user-defined code ...
}, 'Transformation transforms the model to XML.');
schema.setSave(function(error, model, options, callback) {
// ... user-defined code ...
}, 'Saving user data into the DB');
How to get all schema descriptions?
// Prints all descriptions in the schema
console.log(schema.meta);
// Prints description of transformation "xml"
console.log(schema.meta['transform#xml']);
// Prints description of workflow "check"
console.log(schema.meta['workflow#check']);
// Prints description of "save"
console.log(schema.meta['save']);
Added a new argument in all SchemaBuilder operations
controller
argument is filled when an operation is executed from a controller
schema.setSave(function(error, model, options, callback, controller) {
// custom code
});
schema.setQuery(function(error, options, callback, controller) {
// custom code
});
schema.addWorkflow('check', function(error, options, callback, controller) {
// custom code
});
Scripting
This new feature is really great solution for user-defined scripts because a scope of the script is secured (require
and global
variables/aliases are not accessible with except RESTBuilder
). It's an experiment and more info in Total.js documentation. This solution doesn't use Node.js vm
.
SCRIPT('next(value + 10)', 10, function(err, response) {
console.log(response); // 20
});
SCRIPT('value.name = value.name.toLowerCase(); next(value);', { name: 'PETER' }, function(err, response) {
console.log(response); // { name: 'peter' }
});
Data validators
You can change built-in Regular Expression validators easily. These validators use Total.js Schemas.
F.validators.email = /your-regexp/;
F.validators.phone = /your-regexp/;
F.validators.zip = /your-regexp/;
F.validators.url = /your-regexp/;
View Engine
- supports
@{continue}
and @{break}
commands in loops
- updated compilation of dynamic views to new methods:
// In a controller:
controller.viewCompile('Hello @{model.title}', { title: 'World!' });
// In-line:
var output = F.viewCompile('Hello @{model.title}', { title: 'World!' });
// Or in views
// @{viewCompile('Hello @{model.title}', model);
Common
Controller
is a global variable, so it's easy to extend its prototype
F.kill([signal])
is a new method for killing instance of Total.js
HttpFile.rename(filename, [callback(err)])
performs rename
operation for an uploaded file
Support Total.js on GitHub and follow us on Twitter: @totalframework. We have prepared another updates, so stay tuned for more.