Schema
Schema - adapter-specific classes factory.
All classes in single schema shares same adapter type and
one database connection
param name - type of schema adapter (mysql, mongoose, sequelize, redis)
param settings - any database-specific settings which we need to
establish connection (of course it depends on specific adapter)
- host
- port
- username
- password
- database
- debug Boolean = false
example Schema creation, waiting for connection callback
var schema = new Schema('mysql', { database: 'myapp_test' });
schema.define(...);
schema.on('connected', function () {
// work with database
});
Source codefunction Schema(name, settings) {
var schema = this;
// just save everything we get
this.name = name;
this.settings = settings;
// create blank models pool
this.models = {};
this.definitions = {};
// and initialize schema using adapter
// this is only one initialization entry point of adapter
// this module should define `adapter` member of `this` (schema)
var adapter;
if (path.existsSync(__dirname + '/adapters/' + name + '.js')) {
adapter = require('./adapters/' + name);
} else {
try {
adapter = require(name);
} catch (e) {
throw new Error('Adapter ' + name + ' is not defined, try\n npm install ' + name);
}
}
adapter.initialize(this, function () {
// we have an adaper now?
if (!this.adapter) {
throw new Error('Adapter is not defined correctly: it should create `adapter` member of schema');
}
this.adapter.log = function (query, start) {
schema.log(query, start);
};
this.adapter.logger = function (query) {
var t1 = Date.now();
var log = this.log;
return function (q) {
log(q || query, t1);
};
};
this.connected = true;
this.emit('connected');
}.bind(this));
};
Instance methods Helper methods Schema.prototype.define
Declared as function defineClass(className, properties, settings)
Define class
param String className
param Object properties - hash of class properties in format
{property: Type, property2: Type2, ...}
or
{property: {type: Type}, property2: {type: Type2}, ...}
param Object settings - other configuration of class
return newly created class
example simple case
var User = schema.defind('User', {
email: String,
password: String,
birthDate: Date,
activated: Boolean
});
example more advanced case
var User = schema.defind('User', {
email: { type: String, limit: 150, index: true },
password: { type: String, limit: 50 },
birthDate: Date,
registrationDate: {type: Date, default: function () { return new Date }},
activated: { type: Boolean, default: false }
});
Source codefunction defineClass(className, properties, settings) {
var schema = this;
var args = slice.call(arguments);
if (!className) throw new Error('Class name required');
if (args.length == 1) properties = {}, args.push(properties);
if (args.length == 2) settings = {}, args.push(settings);
standartize(properties, settings);
// every class can receive hash of data as optional param
var newClass = function ModelConstructor(data) {
if (!(this instanceof ModelConstructor)) {
return new ModelConstructor(data);
}
AbstractClass.call(this, data);
};
hiddenProperty(newClass, 'schema', schema);
hiddenProperty(newClass, 'modelName', className);
hiddenProperty(newClass, 'cache', {});
hiddenProperty(newClass, 'mru', []);
// setup inheritance
newClass.__proto__ = AbstractClass;
util.inherits(newClass, AbstractClass);
// store class in model pool
this.models[className] = newClass;
this.definitions[className] = {
properties: properties,
settings: settings
};
// pass controll to adapter
this.adapter.define({
model: newClass,
properties: properties,
settings: settings
});
return newClass;
function standartize(properties, settings) {
Object.keys(properties).forEach(function (key) {
var v = properties[key];
if (typeof v === 'function') {
properties[key] = { type: v };
}
});
// TODO: add timestamps fields
// when present in settings: {timestamps: true}
// or {timestamps: {created: 'created_at', updated: false}}
// by default property names: createdAt, updatedAt
}
};
Schema.prototype.defineProperty
Declared as function (model, prop, params)
Define single property named prop
on model
param String model - name of model
param String prop - name of propery
param Object params - property settings
Source codefunction (model, prop, params) {
this.definitions[model].properties[prop] = params;
if (this.adapter.defineProperty) {
this.adapter.defineProperty(model, prop, params);
}
};
Schema.prototype.automigrate
Declared as function (cb)
Drop each model table and re-create.
This method make sense only for sql adapters.
Warning! All data will be lost! Use autoupdate if you need your data.
Source codefunction (cb) {
this.freeze();
if (this.adapter.automigrate) {
this.adapter.automigrate(cb);
} else if (cb) {
cb();
}
};
Schema.prototype.autoupdate
Declared as function (cb)
Update existing database tables.
This method make sense only for sql adapters.
Source codefunction (cb) {
this.freeze();
if (this.adapter.autoupdate) {
this.adapter.autoupdate(cb);
} else if (cb) {
cb();
}
};
Schema.prototype.isActual
Declared as function (cb)
Check whether migrations needed
This method make sense only for sql adapters.
Source codefunction (cb) {
this.freeze();
if (this.adapter.isActual) {
this.adapter.isActual(cb);
} else if (cb) {
cb(null, true);
}
};
Schema.prototype.log
Declared as function (sql, t)
Log benchmarked message. Do not redefine this method, if you need to grab
chema logs, use schema.on('log', ...)
emitter event
private used by adapters
Source codefunction (sql, t) {
this.emit('log', sql, t);
};
Schema.prototype.freeze
Declared as function freeze()
Freeze schema. Behavior depends on adapter
Source codefunction freeze() {
if (this.adapter.freezeSchema) {
this.adapter.freezeSchema();
}
}
Schema.prototype.tableName
Declared as function (modelName)
Return table name for specified modelName
param String modelName
Source codefunction (modelName) {
return this.definitions[modelName].settings.table = this.definitions[modelName].settings.table || modelName
};
Schema.prototype.defineForeignKey
Declared as function defineForeignKey(className, key)
Define foreign key
param String className
param String key - name of key field
Source codefunction defineForeignKey(className, key) {
// return if already defined
if (this.definitions[className].properties[key]) return;
if (this.adapter.defineForeignKey) {
this.adapter.defineForeignKey(className, key, function (err, keyType) {
if (err) throw err;
this.definitions[className].properties[key] = {type: keyType};
}.bind(this));
} else {
this.definitions[className].properties[key] = {type: Number};
}
};
Schema.prototype.disconnect
Declared as function disconnect()
Close database connection
Source codefunction disconnect() {
if (typeof this.adapter.disconnect === 'function') {
this.adapter.disconnect();
}
};
hiddenProperty
Declared as function hiddenProperty(where, property, value)
Source codefunction hiddenProperty(where, property, value) {
Object.defineProperty(where, property, {
writable: false,
enumerable: false,
configurable: false,
value: value
});
}