mysl-orm

Introduction

A MYSQL model framework for Node.js based on the repository pattern.

  1. 1Overview
    1. 1.1Installation
    2. 1.2Example
  2. 2Connection
  3. 3Schema
    1. 3.1Defining Schemas
    2. 3.2Relations
    3. 3.3Types:
  4. 4Repository
    1. 4.1find
    2. 4.2stream
    3. 4.3findOne
    4. 4.4findById
    5. 4.5new
    6. 4.6persist
    7. 4.7update
    8. 4.8create
    9. 4.9insert
    10. 4.10reload
    11. 4.11remove
    12. 4.12delete
    13. 4.13count
    14. 4.14updateWhere
    15. 4.15removeWhere
    16. 4.16literal
    17. 4.17rawInsert
    18. 4.18rawSelect
    19. 4.19rawUpdate
    20. 4.20didApplyChanges
  5. 5Instances
  6. 6Testing
  7. 7Implementation

1Overview

1.1Installation

NPM Version

Install from npm:

npm install --save loke-mysql-orm

1.2Example

var db = require('loke-mysql-orm').create('mysql://root@localhost/demo');
var petRepository = db.table('Pets', {
  name: { type: String, defaultValue: () => 'Untitled' },
  description: db.Text
});
var userRepository = db.table('Users', {
  firstName: db.String,
  lastName: db.String,
  pets: [petRepository]
});

userRepository.find({firstName: 'Testing'})
.then(function (users) {
  users[0].pets[0].description = 'Hello World!';
  return userRepository.persist(users[0]);
});

2Connection

Create a connection.

var db = require('loke-mysql-orm').create(MYSQL_URI, {
  logging: function (sql) {
    console.log('SQL: ' +  sql);
  }
});

function finish() {
  db.end();
}

3Schema

Repositories allow you to query and update documents in the database.

To create a repository you first must define a schema to describe which columns and database tables to use.

3.1Defining Schemas

The db.table method allows creating repositories with a schema description.

/**
 * Create a repository
 * @param  {String} tableName
 * @param  {} schemaDescription describe keys
 * @return {Repository} model constructor
 */
Connection.prototype.table = function (tableName, schemaDescription) {
  var schema = new Schema(schemaDescription);
  return new SequelizeRepository(sequelize, tableName, schema);
};

Example Usage:

var db = require('loke-mysql-orm').create('mysql://root@localhost/demo');

var userRepo = db.table('Users', {
  firstName: {type: db.String},
  birthdate: {type: db.Date}
});

3.2Relations

Relations are defined by using another repository instance as a type value.

Example Usage:

var db = require('loke-mysql-orm').create('mysql://root@localhost/demo');

var address  = db.table('Addresses', { suburb : String });
var petsRepo = db.table('Pets',      { name   : String });

var userRepo = db.table('Users', {
  address  : { type: address },   // Defines a `HasOne` relation
  pets     : { type: [petsRepo] } // Defines a `HasMany` relation
});

3.3Types:

List of all supported types and their MYSQL translation:

4Repository

All query methods defined on repositories return a Promise, using native promises if available, and otherwise falling back to using the es6-promise library on NPM. If you want it to use a different promise implementation use the require('loke-mysql-orm').setPromiseConstructor(Promise) method.

4.1find

Query documents: find(q, opts).

The first argument is the query description object which is passed to Sequelize as the where parameter. As such, it can contain operators like as $or and $lte. You can find a full list of operators in the Sequelize documentation.

Example:

userRepo.find({added: {$lt: new Date('2014')});

Options:

Option Description
opts.attributes List of field names or Literal objects to select
opts.limit Number of rows to return
opts.offset Number of rows to skip
opts.order Sort. E.g. [['ID', 'ASC']]
opts.raw Whether to bypass casting

4.2stream

Query documents using a streaming interface. Same function signature as find(), but returns a Readable stream.

userRepo.find({added: {$lt: new Date('2014')})
.on('data', user => console.log(user.firstName))
.on('end', () => console.log('END'));

Options:

Option Description
opts.highWaterMark Maximum number of rows to pre‐buffer in memory when there is a slow consumer
opts.attributes List of field names or Literal objects to select
opts.limit Number of rows to return
opts.offset Number of rows to skip
opts.order Sort. E.g. [['ID', 'ASC']]
opts.raw Whether to bypass casting

4.3findOne

Find the first document for a query.

Repository.prototype.findOne = function (q, opts) {
  opts = opts || {};
  opts.limit = 1;
  return this.find(q, opts)
  .then(function (results) {
    return results[0] || null;
  });
};

4.4findById

Find a document by its primary key.

Repository.prototype.findById = function (id, opts) {
  var q = {};
  q[this.schema.primaryField.name] = id;
  return this.findOne(q, opts);
};

4.5new

Create a document without yet persisting it to the database.

Example:

var user = userRepository.new({firstName: 'Testing'});
console.log(user.firstName);

4.6persist

Save a document. Depending on whether the object is already in the database, this will trigger either an INSERT or an UPDATE query. Persisting a document will also persist all of its related child documents (HasOne and HasMany).

var user = userRepository.new({firstName: 'Testing'});
userRepository.persist(user);

4.7update

Update a document by using its primary key.

userRepository.update(user, {firstName: 'ReplacementValue'});

4.8create

Shorthand to build a document and persist it:

Repository.prototype.create = function (o) {
  return this.persist(this.new(o));
};

4.9insert

You would usually not use this directly, but use persist() instead. This inserts values into a table.

Example:

userRepo.insert({x: 3, y: 5});
// INSERT INTO `Users` (`ID`, `x`, `y`)
// VALUES (DEFAULT, 3, 5);

4.10reload

Reload the entire document.

exampleRepo.reload(document);

4.11remove

Delete a document. Creates a DELETE query with the primary key.

Example:

userRepo.remove(user);

4.12delete

Delete a document. By default, this will call .remove, but can be overridden to implement soft‐deleting.

Example:

userRepo.delete(user);

4.13count

Count the number of rows matching a query. Uses the COUNT(*) SQL method.

Example:

userRepo.count({gender: 'FEMALE'})
.then(n => console.log(`There are ${n} female users.`));

4.14updateWhere

Creates an UPDATE query. Returns a promise that resolves to the number of rows affected.

// UPDATE Users SET y = 5 WHERE X = 3
userRepo.updateWhere({x: 3}, {y: 5});

4.15removeWhere

Create a DELETE query. Example

// DELETE FROM Users WHERE X = 3
userRepo.deleteWhere({x: 3});

4.16literal

Create SQL code object Literal for use in attributes parameters. To avoid SQL injections the string should not contain user input.

Example:

exampleRepo.find({}, {
  raw: true,
  attributes: [exampleRepo.literal('COUNT(*) AS n')]
})
.then(row => {
  // { n: 5 }
  console.log(row);
});

4.17rawInsert

Create a custom INSERT query. To avoid SQL injections the string should not contain user input.

Example:

rawSQLString:

INSERT INTO example (a,b,c) VALUES (1,2,0)
  ON DUPLICATE KEY UPDATE c=c+:value;

code:

exampleRepo.rawInsert(rawSQLString, {value: 1});

4.18rawSelect

Create a custom SELECT query. To avoid SQL injections the string should not contain user input.

Example:

rawSQLString:

SELECT * FROM example WHERE a + b < :value

code:

exampleRepo.rawSelect(rawSQLString, {value: 300});

4.19rawUpdate

Create a custom UPDATE query. Returns the number of affected rows. To avoid SQL injections the string should not contain user input.

Example:

rawIncrementString:

UPDATE example SET c = c + 1 WHERE a = :a LIMIT 1

code:

exampleRepo.rawUpdate(rawIncrementString, {a: 239});

4.20didApplyChanges

When you use the repo.update(document, {...changes}) method the change tracking system will update the local document object. However, with custom SQL queries this cannot be done automatically.

Although it is possible to update the in‐memory object simply by setting document.c = document.c + 1, that would cause problems if you were to persist that object later on.

Hence, to update the local value on the document, without triggering the change tracker to overwrite anything, there is a didApplyChanges(document, fieldValues, relationValues) method for this purpose.

Example:

rawIncrementString:

UPDATE example SET c = c + 1 WHERE id = :id LIMIT 1

code:

exampleRepo.rawUpdate(rawIncrementString, {id: document.id})
.then(nUpdated => {
  if (nUpdated) exampleRepo.didApplyChanges(document, {c: document.c + 1});
});

5Instances

To add instance methods, define them on the Repository#prototype property:

var userRepository = db.table('Users', {});

userRepository.prototype.getFullName = function () {
  return this.firstName + ' ' + this.lastName;
};

6Testing

7Implementation