Model is a datastore-agnostic ORM in JavaScript. It serves as the model-component for the Geddy MVC Web framework for NodeJS.
Apache License, Version 2
Model requires version 0.6.x of Node.js or higher. If you want to run the tests, or work on Model, you'll want the Jake JavaScript build-tool.
Installing with NPM
npm install model
Model currently implements adapters for:
- Postgres
- Riak
- MongoDB
Model uses a pretty simple syntax for defining a model. (It should look familiar to anyone who has used an ORM like ActiveRecord, DataMapper, Django's models, or SQLAlchemy.)
var User = function () {
this.property('login', 'string', {required: true});
this.property('password', 'string', {required: true});
this.property('lastName', 'string');
this.property('firstName', 'string');
this.validatesPresent('login');
this.validatesFormat('login', /[a-z]+/, {message: 'Subdivisions!'});
this.validatesLength('login', {min: 3});
this.validatesConfirmed('password', 'confirmPassword');
this.validatesWithFunction('password', function (s) {
// Something that returns true or false
return s.length > 0;
});
// Can define methods for instances like this
this.someMethod = function () {
// Do some stuff
};
};
// Can also define them on the prototype
User.prototype.someOtherMethod = function () {
// Do some other stuff
};
User = model.registerModel('User', User);
Alternatively, you can use the defineProperties
method to lay out your model's
properties in one go:
var User = function () {
this.defineProperties({
login: {type: 'string', required: true}
, password: {type: 'string', required: true}
, lastName: {type: 'string'}
, firstName: {type: 'string'}
});
}
Creating an instance of one of these models is easy:
var params = {
login: 'alex'
, password: 'lerxst'
, lastName: 'Lifeson'
, firstName: 'Alex'
};
var user = User.create(params);
Data-validation happens on the call to create
, and any validation errors show
up inside an errors
property on the instance, keyed by field name. Instances
have an isValid
method that returns a Boolean indicating whether the instance is
valid.
// Leaving out the required password field
var params = {
login: 'alex'
};
var user = User.create(params);
// Prints 'false'
console.log(user.isValid());
// Prints 'Field "password" is required'
console.log(user.errors.password);
After creating the instance, call the save
method on the instance. This method
takes a callbak in the familiar (err, data) format for Node.
if (user.isValid()) {
user.save(function (err, data) {
if (err) {
throw err;
}
console.log('New item saved!');
});
}
Use the updateProperties
method to update the values of the properties on an
instance with the appropriate validations. Then call save
on the instance.
user.updateProperties({
login: 'alerxst'
});
if (user.isValid()) {
user.save(function (err, data) {
if (err) {
throw err;
}
console.log('Item updated!');
});
}
Model uses a simple API for finding and sorting items. Again, it should look familiar to anyone who has used a similar ORM for looking up records. The only wrinkle with Model is that the API is (as you might expect for a NodeJS library) asynchronous.
Methods for querying are static methods on each model constructor.
Use the first
method to find a single item. You can pass it an id, or a set of
query parameters in the form of an object-literal. In the case of a query, it
will return the first item that matches, according to whatever sort you've
specified.
var user;
User.first({login: 'alerxst'}, function (err, data) {
if (err) {
throw err;
}
user = data;
console.log('Found user');
console.dir(user);
});
Use the all
method to find lots of items. Pass it a set of query parameters in
the form of an object-literal, where each key is a field to compare, and the
value is either a simple value for comparison (equal to), or another
object-literal where the key is the comparison-operator, and the value is the
value to use for the comparison.
var users
, dt;
dt = new Date();
dt.setHours(dt.getHours() - 24);
// Find all the users created since yesterday
User.all({createdAt: {gt: dt}, function (err, data) {
if (err) {
throw err;
}
users = data;
console.log('Found users');
console.dir(users);
});
Here are some more examples of queries:
// Where "foo" is 'BAR' and "bar" is not null
{foo: 'BAR', bar: {ne: null}}
// Where "foo" begins with 'B'
{foo: {'like': 'B'}}
// Where foo is less than 2112, and bar is 'BAZ'
{foo: {lt: 2112}, bar: 'BAZ'}
Here is the list of comparison operators currently supported:
eql: equal to ne: not equal to gt: greater than lt: less than gte: greater than or equal lte: less than or equal like: like
A simple string-value for a query parameter is the same as 'eql'. {foo: 'bar'}
is the same as {foo: {eql: 'bar'}}
.
Model supports combining queries with OR and negating queries with NOT.
To perform an 'or' query, use an object-literal with a key of 'or', and an array of query-objects to represent each set of alternative conditions:
// Where "foo" is 'BAR' OR "bar" is 'BAZ'
{or: [{foo: 'BAR'}, {bar: 'BAZ'}]}
// Where "foo" is not 'BAR' OR "bar" is null OR "baz" is less than 2112
{or: [{foo {ne: 'BAR'}}, {bar: null}, {baz: {lt: 2112}}]}
To negate a query with 'not', simply use a query-object where 'not' is the key, and the value is the set of conditions to negate:
// Where NOT ("foo" is 'BAR' and "bar" is 'BAZ')
{not: {foo: 'BAR', bar: 'BAZ'}}
// Where NOT ("foo" is 'BAZ' and "bar" is less than 1001)
{not: {foo: 'BAZ', bar: {lt: 1001}}}
These OR and NOT queries can be nested and combined:
// Where ("foo" is like 'b' OR "foo" is 'foo') and NOT "foo" is 'baz'
{or: [{foo: {'like': 'b'}}, {foo: 'foo'}], not: {foo: 'baz'}}
The all
API-call for querying accepts an optional options-object after the
query-conditions. Set a 'sort' in that options-object to specifiy properties to
sort on, and the sort-direction for each one.
var users
// Find all the users who have ever been updated, and sort by
// creation-date, ascending, then last name, descending
User.all({updatedAt: {ne: null}}, {sort: {createdAt: 'asc', lastName: 'desc'}},
function (err, data) {
if (err) {
throw err;
}
users = data;
console.log('Updated users');
console.dir(users);
});
You can use a simplified syntax for specifying the sort. The default sort-direction is ascending ('asc'), so you can specify a property to sort on (or multiple properties as an array) if you want all sorts to be ascending:
// Sort by createdAt, ascending
{sort: 'createdAt'}
// Sort by createdAt, then updatedAt, then lastName,
// then firstName -- all ascending
{sort: ['createdAt', 'updatedAt', 'lastName', 'firstName']}
Model JavaScript ORM copyright 2112 mde@fleegix.org.