Skip to content

Files

Latest commit

f60f824 · Mar 24, 2015

History

History
1131 lines (880 loc) · 32 KB

README.md

File metadata and controls

1131 lines (880 loc) · 32 KB

persist is not maintained anymore. If you would like to take it over from me please contact me.

persist

persist is an ORM framework for node.js.

The following databases are currently supported:

Quick Examples

var persist = require("persist");
var type = persist.type;

// define some model objects
Phone = persist.define("Phone", {
  "number": type.STRING
});

Person = persist.define("Person", {
  "name": type.STRING
}).hasMany(this.Phone);

persist.connect({
  driver: 'sqlite3',
  filename: 'test.db',
  trace: true
}, function(err, connection) {
  Person.using(connection).all(function(err, people) {
    // people contains all the people
  });
});

Download

You can install using Node Package Manager (npm):

npm install persist

Index

persist

Connection

Model

Query

Transaction

Results Set

Connection Pooling

# database.json

If the current working directory contains a file called database.json this file will be loaded upon requiring persist. The file should follow a format like this:

{
  "default": "dev",

  "dev": {
    "driver": "sqlite3",
    "filename": ":memory:"
  },

  "test": {
    "driver": "sqlite3",
    "filename": ":memory:"
  },

  "prod": {
    "driver": "sqlite3",
    "filename": "prod.db",
    "sqlDir": "./prodSql",
    "pooling": {
      "name": "testPool",
      "max": 2,
      "min": 1,
      "idleTimeoutMillis": 30000
    }
  }
}

"default" specifies which environment to load.

## persist ### persist.env

The environment to read from the database.json file. If not set will use the value of default from the database.json.

Example

persist.env = 'prod';
### persist.connect([options], callback)

Connects to a database.

Arguments

  • options - (optional) Options used to connect to the database. If options are not specified the default connect options are used. see database.json and SetDefaultConnectOptions
  • driver - The driver to use to connect (ie sqlite3, mysql, oracle, or postgresql).
  • db - If db is specified this parameter will be assumed to be an already open connection to the database.
  • other - see the documentation for your driver. The options hash will be passed to that driver.
  • callback(err, connection) - Callback to be called when the connection is established.

Example

persist.connect({
  driver: 'sqlite3',
  filename: 'test.db',
  trace: true
}, function(err, connection) {
  // connnection esablished
});
### persist.define(modelName, properties, [opts]): Model

Defines a model object for use in persist.

The primary key column does not need to be specified and will default to the name 'id' with the attributes dbColumnName='id', type='integer'. You can override the database name using dbColumnName or setting the primaryKey attribute on any column.

Arguments

  • modelName - The name of the model. This name will map to the database name.
  • properties - Hash of properties (or columns). The value of each property can simply be the type name (ie type.STRING) or it can be a hash of more options.
  • type - type of the property (ie type.STRING)
  • defaultValue - this can be a value or a function that will be called each time this model object is created
  • dbColumnName - the name of the database column. (default: name of the property, all lower case, seperated by '_')
  • primaryKey - Marks this column as being the primary key column. You can have only one primary key column.
  • opts - Options for this column.
  • tableName - The name of the table (default: modelName pluralized).

Returns

A model class. Events can also be registered with model instances - see Model Events

Example

Person = persist.define("Person", {
  "name": type.STRING,
  "createdDate": { type: type.DATETIME, defaultValue: function() { return self.testDate1 }, dbColumnName: 'new_date' },
  "lastUpdated": { type: type.DATETIME }
})
### persist.defineAuto(modelName, dbConfig, callback): Model

Defines a model object for use in persist. Columns are defined by the program in this method. Uses an existing database connection to retrieve column data.

Arguments

  • modelName - The name of the model. This name will map to the table name.
  • dbConfig - Hash of dbConfig. Should contain the driver, as well as the database name.
  • database - The database connection to use.
  • driver - The name of the database driver to use.

Returns

A model class. Events can also be registered with model instances - see Model Events

Example

persist.defineAuto("Person",{driver:dbDriver, db:self.connection.db},function(err,model){
  Person = model.hasMany(Phone)
    .on('beforeSave', function (obj) {
      obj.lastUpdated = testDate;
    })
    .on('afterSave', function (obj) {
      if (!obj.updateCount) obj.updateCount = 0;
      obj.updateCount++;
    });
});
### persist.setDefaultConnectOptions(options)

Sets the default connection options to be used on future connect calls. see database.json

Arguments

  • options - See connect for the description of options

Example

persist.setDefaultConnectOptions({
  driver: 'sqlite3',
  filename: 'test.db',
  trace: true});
### persist.shutdown([callback])

Shutdown persist. This is currently only required if you are using connection pooling. see generic-pool.

Arguments

  • [callback] - Optional callback on successful shutdown.

Example

persist.shutdown(function() {
  console.log('persist shutdown');
});
## Connection ### connection.chain(chainables, callback)

Chains multiple statements together in order and gets the results.

Arguments

  • chainables - An array of chainable queries. These can be save, updates, selects, or deletes. Each item in the array will be executed, wait for the results, and then execute the next. This can also be a hash of queries in which case the results will contain a hash of results where each key corresponds to a key in the results.
  • callback(err, results) - Callback called when all the items have been executed.

Example

// array chaining
connection.chain([
  person3.save,
  Person.min('age'),
  Person.max('age'),
  phone3.delete,
  person2.delete,
  Person.orderBy('name').all,
  Phone.orderBy('number').first,
  Phone.count,
  Phone.deleteAll,
  Phone.all,
  Person.getById(1),
  persist.runSql('SELECT * FROM Person')
], function(err, results) {
  // results[0] = person3
  // results[1] = 21
  // results[2] = 25
  // results[3] = []
  // results[4] = []
  // results[5] = -- all people ordered by name
  // results[6] = -- first phone ordered by number
  // results[7] = 100
  // results[8] = []
  // results[9] = [] -- nobody left
  // results[10] = -- the person with id 1
  // results[11] = Results of select.
});

// mapped chaining
connection.chain({
  minAge: Person.min('age'),
  maxAge: Person.max('age')
}, function(err, results) {
  // results.minAge = 21
  // results.maxAge = 25
});
### connection.tx(callback)

Begins a transaction on the connection.

Arguments

  • callback(err, tx) - Callback called when the transaction has started. tx is a transaction object which you can call commit or rollback

Example

connection.tx(function(err, tx) {
  person1.save(connection, function(err) {
    tx.commit(function(err) {
      // person1 saved and committed to database
    });
  });
});
### connection.runSql(sql, values, callback)

Runs a sql statement that does not return results (INSERT, UPDATE, etc).

Arguments

  • sql - The SQL statement to run.
  • values - The values to substitute in the SQL statement. This is DB specific but typically you would use "?".
  • callback(err, results) - Callback called when SQL statement completes. results will contain the number of affected rows or last insert id.

Example

connection.runSql("UPDATE people SET age = ?", [32], function(err, results) {
  // people updated
});
### connection.runSqlAll(sql, values, callback)

Runs a sql statement that returns results (ie SELECT).

Arguments

  • sql - The SQL statement to run.
  • values - The values to substitute in the SQL statement. This is DB specific but typically you would use "?".
  • callback(err, results) - Callback called when SQL statement completes. results will contain the row data.

Example

connection.runSqlAll("SELECT * FROM people WHERE age = ?", [32], function(err, people) {
  // people contains all the people with age 32
});
### connection.runSqlEach(sql, values, callback, doneCallback)

Runs a sql statement that returns results (ie SELECT). This is different from runSqlAll in that it returns each row in a seperate callback.

Arguments

  • sql - The SQL statement to run.
  • values - The values to substitute in the SQL statement. This is DB specific but typically you would use "?".
  • callback(err, row) - Callback called for each row returned.
  • doneCallback(err) - Callback called after all the rows have returned.

Example

connection.runSqlEach("SELECT * FROM people WHERE age = ?", [32], function(err, person) {
  // a single person
}, function(err) {
  // all done
});
### connection.runSqlFromFile(filename, values, callback) ### connection.runSqlAllFromFile(filename, values, callback) ### connection.runSqlEachFromFile(filename, values, callback, doneCallback)

Same as runSql, runSqlAll, runSqlEach except the first parameter is a filename of where to load the SQL from.

Example

connection.runSqlFromFile('report.sql', [32], function(err, person) {
  // a single person
}, function(err) {
  // all done
});
## Model ### Model.hasMany(AssociatedModel, [options]): Model

Adds a has many relationship to a model. This will automatically add a property to the associated model which links to this model. It will also define a property on instances of this model to get the releated objects - see Associated Object Properties

Arguments

  • AssociatedModel - The name of the model to associate to.
  • options - (optional) An hash of options.
  • through - creates a many to many relationship using the value of through as the join table.
  • name - the name of the property to expose.

Returns

The model class object suitable for chaining.

Example

Phone = persist.define("Phone", {
  "number": persist.String
});

Person = persist.define("Person", {
  "name": persist.String
}).hasMany(Phone);
### Model.hasOne(AssociatedModel, [options]): Model

Adds a has one relationship to a model. This will automatically add a property to the associated model which links to this model. It will also define a property on instances of this model to get the releated objects - see Associated Object Properties

Arguments

  • AssociatedModel - The name of the model to associate to.
  • options - (optional) An hash of options.
  • foreignKey - The foreign key to use for the relationship
  • name - the name of the property to expose.
  • createHasMany - true/false to create the other side of the relationship.
  • hasManyName - The name of the property on the other side of the relationship.

Returns

The model class object suitable for chaining.

Example

Phone = persist.define("Phone", {
  "number": persist.String
}).hasOne(Person);

Person = persist.define("Person", {
  "name": persist.String
});
### Model.using(connection): query

Gets a query object bound to a connection object.

Arguments

  • connection - The connection to bind the query object to.

Returns

A new Query object.

Example

Person.using(connection).first(...);
### Model.save(connection, callback)

Saves the model object to the database

Arguments

  • connection - The connection to use to save the object with.
  • callback(err) - The callback to be called when the save is complete

Example

person1.save(connection, function() {
  // person1 saved
});
### modelInstance.update(connection, params, callback)

Updates the model object to the database

Arguments

  • connection - The connection to use to update the object with.
  • params - Object containing properties to update.
  • callback(err) - The callback to be called when the update is complete

Example

person1.update(connection, { name: 'Tom' }, function() {
  // person1 saved
});
### Model.update(connection, id, params, callback)

Updates the model object specified with id to the database. This will only update the values specified and will not retreive the item from the database first.

Arguments

  • connection - The connection to use to update the object with.
  • id - The id of the row you would like to update.
  • params - Object containing properties to update.
  • callback(err) - The callback to be called when the update is complete

Example

Person.update(connection, 5, { name: 'Tom' }, function() {
  // person with id = 5 updated with name 'Tom'.
});

// or chaining
connection.chain([
  Person.update(5, { name: 'Tom' })
], function(err, results) {
  // person with id = 5 updated with name 'Tom'.
});
### Model.delete(connection, callback)

Deletes the model object from the database

Arguments

  • connection - The connection to use to delete the object with.
  • callback(err) - The callback to be called when the delete is complete

Example

person1.delete(connection, function() {
  // person1 deleted
});
### Model.getById(connection, id, callback)

Gets an object from the database by id.

Arguments

  • connection - The connection to use to delete the object with.
  • id - The if of the item to get.
  • callback(err, obj) - The callback to be called when the delete is complete

Example

Person.getById(connection, 1, function(err, person) {
  // person is the person with id equal to 1. Or null if not found
});
### Model.defineClause(clauseName, clauses)

Creates a custom method that is a composition of clauses. this is set to refer to the query. you're constructing.

Arguments

  • clauseName - The name of the clause to be attached to the model
  • clauses - The function that describes the clause composition using a query.

Example

Person.defineClause('clauseName', function(arg1, arg2, ...) {
  return this.where('id < ?', arg1).orderBy('id').limit(5);
});

Person.clauseName(5).all(connection, function(err, people) {
  // All the people with id < 5, ordered by id and limited to 5
});

Person.defineClause('clauseName2', function(connection, callback) {
  return this
  .where('id < 5')
  .orderBy('id')
  .limit(5)
  .all(connection, callback);
});

Person.clauseName2(connection, function(err, people) {
  // All the people with id < 5, ordered by id and limited to 5
});
### Model.onSave(obj, connection, callback)

If preset this function will be called when an update or save occures. You would typically create this method in your model file.

Arguments

  • obj - The object or partial object, in the case of update, being saved.
  • connection - The connection persist is currently using to do the save
  • callback() - The callback to be called when the onSave is complete

Example

Person.onSave = function(obj, connection, callback) {
  obj.lastUpdated = new Date();
  callback();
};
### Model.onLoad(obj)

If preset this function will be called after an object is loaded from the database. You would typically create this method in your model file.

Arguments

  • obj - The object that was just loaded from the database.
  • connection - The connection persist is currently using to do the save
  • callback() - The callback to be called when the onLoad is complete

Example

Person.onLoad = function(obj, connection, callback) {
  obj.fullName = obj.firstName + ' ' + obj.lastName;
  callback();
};
### Model.validate(obj, callback, connection)

Model validation is loosely implemented. Instead, it's left to the developers to integrate any valiation library that fits their needs. If present this function will be called during a save or update operation.

Arguments

  • obj - The model object
  • connection - The connection persist is currently using to do the save or update
  • callback(success, message) - The callback to be called when the validate is complete.
  • success - False if validation failed, True otherwise
  • message - A string containing an error message, or a custom-defined object containing vaidation information

Example

Person = persist.define("Person", {
  "name": type.STRING,
  "age": type.INTEGER
};

//Single message validation
Person.validate = function (obj, connection, callback) {
  if (obj.name === 'bad name') {
    return callback(false, 'You had a bad name');
  }
  return callback(true);
};

//Multiple message validation
Person.validate = function (obj, connection, callback) {
  var errors = [];

  if (obj.name === 'bad name') {
    errors.push({name:'You had a bad name'});
  }
  
  if (obj.age < 0) {
    errors.push({age:'Age must be greater than 0'});  
  }
  
  if(errors.length > 0) {
    return callback(false, errors);
  }
  
  return callback(true);
};
### Associated Object Properties

If you have setup an associated property using hasMany instances of your model will have an additional property which allows you to get the associated data. This property returns a Query object which you can further chain to limit the results.

Example

Phone = persist.define("Phone", {
  "number": persist.String
});

Person = persist.define("Person", {
  "name": persist.String
}).hasMany(Phone);

Person.using(connection).first(function(err, person) {
  person.phones.orderBy('number').all(function(err, phones) {
    // all the phones of the first person
  });
});
### Model Events

The following events can be registered when defining a new model:

  • beforeCreate and afterCreate: fired before/after a new object is being added to the DB
  • beforeUpdate and afterUpdate: fired before/after an existing object is being updated in the DB
  • beforeSave and afterSave: fired before/after an object is either created or updated.
  • beforeDelete and afterDelete: fired before/after an object is removed from the DB

Each event function has the signature: function(obj) where 'obj' is the model instance that fired the event.

Limitation

Events are currently fired ONLY when invoking save/update/delete functions on model instances. So for instance, Model.update(connection,id,data) will not fire the save & update events.

Example

Phone = persist.define("Phone", {
  "number": persist.String,
  "created_at": persist.DATETIME,
  "updated_at": persist.DATETIME
})
.on("beforeCreate", function(obj){
  obj.created_at = new Date();
})
.on("beforeSave", function(obj){
  // updated when creating or updating the model instance
  obj.updated_at = new Date();
});
## Query ### query.all([connection], callback)

Gets all items from a query as a single array of items. The array returned will have additional methods see here for documentation.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • callback(err, items) - Callback to be called after the rows have been fetched. items is an array of model instances.

Example

Person.all(connection, function(err, people) {
  // all the people
});
### query.each([connection], callback, doneCallback)

Gets items from a query calling the callback for each item returned.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • callback(err, item) - Callback to be called after each row has been fetched. item is a model instance.
  • doneCallback(err) - Callback called after all rows have been retrieved.

Example

Person.each(connection, function(err, person) {
  // a person
}, function() {
  // all done
});
### query.first([connection], callback)

Gets the first item from a query.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • callback(err, item) - Callback to be called after the row has been fetched. item is a model instance.

Example

Person.first(connection, function(err, person) {
  // gets the first person
});
### query.last([connection], callback)

Gets the last item from a query.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • callback(err, item) - Callback to be called after the row has been fetched. item is a model instance.

Example

Person.last(connection, function(err, person) {
  // gets the last person
});
### query.orderBy(propertyName, direction): query

Orders the results of a query.

Arguments

  • propertyName - Name of the property to order by.
  • direction - The direction to orderBy. Can be persist.Ascending or persist.Descending.

Returns

The query object suitable for chaining.

Example

Person.orderBy('name').all(connection, function(err, people) {
  // all the people ordered by name
});
### query.limit(count, [offset]): query

Limits the number of results of a query.

Arguments

  • count - Number of items to return.
  • offset - (Optional) The number of items to skip.

Returns

The query object suitable for chaining.

Example

Person.orderBy('name').limit(5, 5).all(connection, function(err, people) {
  // The 5-10 people ordered by name
});
### query.where(clause, [values...]): query ### query.where(hash): query

Filters the results by a where clause.

Arguments

  • clause - A clause to filter the results by.
  • values - (Optional) A single value or array of values to substitute in for '?'s in the clause.
  • hash - A hash of columns and values to match on (see example)

Returns

The query object suitable for chaining.

Example

Person.where('name = ?', 'bob').all(connection, function(err, people) {
  // All the people named 'bob'
});

Person.where('name = ? or age = ?', ['bob', 23]).all(connection, function(err, people) {
  // All the people named 'bob' or people with age 23
});

Person.where({'name': 'bob', 'age': 23}).all(connection, function(err, people) {
  // All the people named 'bob' with the age of 23
});
### query.whereIn(property, [values...]): query Filters the results by a where clause using an IN clause.

Arguments

  • property - The property to invoke the IN clause on.
  • values - An array of values to include in the IN clause.

Returns

The query object suitable for chaining.

Example

Person.whereIn('name', ['bob', 'alice', 'cindy']).all(connection, function(err,people) {
  // All the people named 'bob', 'alice', or 'cindy'
});

Person.include("phones").whereIn('phones.number', ['111-2222','333-4444']).all(connection, function(err,people){
  // All the people whose phone numbers are '111-2222' or '333-4444'
});
### query.count([connection], callback)

Counts the number of items that would be returned by the query.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • callback(err, count) - Callback with the count of items.

Example

Person.where('name = ?', 'bob').count(connection, function(err, count) {
  // count = the number of people with the name bob
});
### query.min([connection], fieldName, callback)

Gets the minimum value in the query of the given field.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • fieldName - The field name of the value you would like to get the minimum for.
  • callback(err, min) - Callback with the minimum value.

Example

Person.where('name = ?', 'bob').min(connection, 'age', function(err, min) {
  // the minimum age of all bobs
});
### query.max([connection], fieldName, callback)

Gets the maximum value in the query of the given field.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • fieldName - The field name of the value you would like to get the maximum for.
  • callback(err, min) - Callback with the maximum value.

Example

Person.where('name = ?', 'bob').max(connection, 'age', function(err, min) {
  // the maximum age of all bobs
});
### query.sum([connection], fieldName, callback)

Gets the sum of all values in the query of the given field.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • fieldName - The field name you would like to sum.
  • callback(err, sum) - Callback with the sum value.

Example

Person.where('name = ?', 'bob').sum(connection, 'age', function(err, sum) {
  // the sum of all ages whos name is bob
});
### query.deleteAll([connection], callback)

Deletes all the items specified by the query.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • callback(err) - Callback called upon completion.

Example

Person.where('name = ?', 'bob').deleteAll(connection, function(err) {
  // all people name 'bob' have been deleted.
});
### query.updateAll([connection], data, callback)

Updates all the items specified by the query.

Arguments

  • connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
  • data - A hash of properties to update. Key is the property name to update. Value is the value to update the property to.
  • callback(err) - Callback called upon completion.

Example

Person.where('name = ?', 'bob').updateAll(connection, { age: 25 }, function(err) {
  // all people name 'bob' have their age set to 25.
});
### query.include(propertyName): query

Includes the associated data linked by (hasMany or hasMany(through)) the propertyName when retrieving data from the database. This will replace obj.propertyName with an array of results as opposed to the default before which is a query.

Internally this will do a join to the associated table in the case of a one to many. And will do a join to the associated through table and the associated table in the case of a many to many.

Arguments

  • propertyName - This can be a single property name or an array of property names to include.

Example

Person.include("phones").where('name = ?', 'bob').all(connection, function(err, people) {
  // all people named 'bob' and all their phone numbers
  // so you can do... people[0].phones[0].number
  // as opposed to... people[0].phones.all(function(err, phones) {});
});
## Transaction ### tx.commit(callback)

Commits a transaction.

Arguments

  • callback(err) - Callback called when the transaction has committed.

Example

connection.tx(function(err, tx) {
  person1.save(connection, function(err) {
    tx.commit(function(err) {
      // person1 saved and committed to database
    });
  });
});
### tx.rollback(callback)

Rollsback a transaction.

Arguments

  • callback(err) - Callback called when the transaction has rolledback.

Example

connection.tx(function(err, tx) {
  person1.save(connection, function(err) {
    tx.rollback(function(err) {
      // person1 not saved. Transaction rolledback.
    });
  });
});
## Result Set ### rs.getById(id)

Gets an item from the result set by id.

Arguments

  • id - The id of the item to get.

Example

Person.all(connection, function(err, people) {
  var person2 = people.getById(2);
});
## Connection Pooling ### Using

Persist uses generic-pool to manage the connection pool. If you specify "pooling" in your configuration you must specify a pool name. See generic-pool for other options. To cleanly shutdown the connection pool you must also call persist.shutdown.

Example database.json to enable pooling:

{
  "default": "dev",

  "dev": {
    "driver": "sqlite3",
    "filename": ":memory:",
    "pooling": {
      "name": "myDatabasePool"
    }
  }
}