Socket
Socket
Sign inDemoInstall

ee-orm

Package Overview
Dependencies
Maintainers
2
Versions
156
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ee-orm - npm Package Compare versions

Comparing version 0.2.11 to 0.3.0

lib/Entity.js

91

lib/Database.js
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, Model = require('./Model')
, Transaction = require('./Transaction');
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, argv = require('ee-argv')
, Entity = require('./Entity')
, Transaction = require('./Transaction');
var dev = argv.has('dev-orm');
module.exports = new Class({
inherits: EventEmitter
, init: function(options) {
// remove deprecated parent property
delete this.parent;
// orm
this._setProperty('_orm', options.orm);
this._setProperty('_database', options.database);
this._setProperty('_queryBuilders', {});
module.exports = new Class({
inherits: EventEmitter
// initialize the orm
this._initialize(options.definition);
, init: function(options) {
if (dev) log.warn('initialize new db instance for «'+options.definition.getDatabaseName()+'»...');
// emit load not before the next main loop execution
process.nextTick(function(){
this.emit('load');
}.bind(this));
}
Class.define(this, '_orm', Class(options.orm));
Class.define(this, '_database', Class(options.database));
Class.define(this, '_queryBuilders', Class({}));
// initialize the orm
this._initialize(options.definition);
// emit load not before the next main loop execution
process.nextTick(function(){
this.emit('load');
}.bind(this));
}
, createTransaction: function() {
return new Transaction(this);
}
, createTransaction: function() {
return new Transaction(this);
}
, executeQuery: function(mode, query, callback) {
this._database.query(mode, query, callback);
}
, executeQuery: function(mode, query, callback) {
this._database.query(mode, query, callback);
}
, _setProperty: function(name, value){
Object.defineProperty(this, name, {value: value, writable: true});
}
, _getDatabase: function(){
return this;
}
, _getDatabase: function(){
return this;
}
, _initialize: function(definition){
Object.keys(definition).forEach(function(tablename){
if (this[tablename]) next(new Error('Failed to load ORM for database «'+definition.getDatabaseName()+'», the tablename «'+tablename+'» is reserved for the orm.').setName('ORMException'));
, _initialize: function(definition){
Object.keys(definition).forEach(function(tablename){
if (this[tablename]) next(new Error('Failed to load ORM for database «'+definition.getDatabaseName()+'», the tablename «'+tablename+'» is reserved for the orm.').setName('ORMException'));
if (dev) log.debug('['+definition.getDatabaseName()+'] initializing new model «'+tablename+'» ...');
this[tablename] = new Entity({
orm : this._orm
, definition : definition[tablename]
, queryBuilders : this._queryBuilders
, getDatabase : this._getDatabase.bind(this)
});
}.bind(this));
}
this[tablename] = new Model({
orm : this._orm
, definition : definition[tablename]
, queryBuilders : this._queryBuilders
, getDatabase : this._getDatabase.bind(this)
});
}.bind(this));
}
});
});
}();
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log');
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log');

@@ -10,45 +10,45 @@

module.exports = new Class({
module.exports = new Class({
unformatted: true
unformatted: true
, init: function(options) {
this.type = options.type || 'inner';
this.source = options.source;
this.target = options.target;
}
, init: function(options) {
this.type = options.type || 'inner';
this.source = options.source;
this.target = options.target;
}
, reverseFormat: function(aliasSuffix, secondAliasSuffix) {
return {
type : this.type
, source : {
table : this.target.table + (secondAliasSuffix === undefined ? '' : secondAliasSuffix)
, column : this.target.column
}
, target : this.source
, alias : this.source.table + (aliasSuffix === undefined ? '' : aliasSuffix)
};
}
, reverseFormat: function(aliasSuffix, secondAliasSuffix) {
return {
type : this.type
, source : {
table : this.target.table + (secondAliasSuffix === undefined ? '' : secondAliasSuffix)
, column : this.target.column
}
, target : this.source
, alias : this.source.table + (aliasSuffix === undefined ? '' : aliasSuffix)
};
}
, format: function(aliasSuffix, secondAliasSuffix) {
return {
type : this.type
, source : this.source
, target : {
table : this.target.table + (secondAliasSuffix === undefined ? '' : secondAliasSuffix)
, column : this.target.column
}
, alias : this.target.table + (aliasSuffix === undefined ? '' : aliasSuffix)
};
}
, format: function(aliasSuffix, secondAliasSuffix) {
return {
type : this.type
, source : this.source
, target : {
table : this.target.table + (secondAliasSuffix === undefined ? '' : secondAliasSuffix)
, column : this.target.column
}
, alias : this.target.table + (aliasSuffix === undefined ? '' : aliasSuffix)
};
}
, _addAlias: function(config, alias) {
config.table = config.table +(alias === undefined ? '' : alias);
return config;
}
});
, _addAlias: function(config, alias) {
config.table = config.table +(alias === undefined ? '' : alias);
return config;
}
});
}();
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, QueryBuilder = require('./QueryBuilder')
, DefaultModel = require('./DefaultModel')
, RelatingSet = require('./RelatingSet')
, clone = require('clone')
, log = require('ee-log');
var Class = require('ee-class')
, Arguments = require('ee-arguments')
, async = require('ee-async')
, RelatingSet = require('./RelatingSet')
, EventEmitter = require('ee-event-emitter')
, type = require('ee-types')
, log = require('ee-log');
// model initializer
module.exports = new Class({
init: function(_options){
var that = this;
module.exports = new Class({
inherits: EventEmitter
this._definition = _options.definition;
this._options = _options;
this._orm = _options.orm;
, init: function(options) {
// add properties for this specific model
this.Model = this.createModel(DefaultModel, _options);
this.QueryBuilder = new QueryBuilder({
orm : _options.orm
, queryBuilders : _options.queryBuilders
, definition : _options.definition
, getDatabase : _options.getDatabase
});
Class.define(this, '_defintion' , Class(options.definition));
Class.define(this, '_orm' , Class(options.orm));
Class.define(this, '_values' , Class({}));
Class.define(this, '_changedValues' , Class([]));
Class.define(this, '_mappings' , Class({}));
Class.define(this, '_belongsTo' , Class({}));
Class.define(this, '_references' , Class({}));
Class.define(this, '_changedReferences' , Class([]));
Class.define(this, '_mappingIds' , Class([]));
Class.define(this, '_hasChanges' , Class(false).Writable());
Class.define(this, '_relatingSets' , Class(options.relatingSets));
Class.define(this, '_getDatabase' , Class(options.getDatabase));
Class.define(this, '_fromDb' , Class(options.isFromDB || false).Writable());
_options.queryBuilders[_options.definition.getTableName()] = this.QueryBuilder;
// check for changes
//this.on('change', this._setChanged.bind(this));
if (options.parameters) this._setValues(options.parameters);
}
// constructor to expose
var Constructor = function(options, relatingSets) {
if (this instanceof Constructor) {
, _setChanged: function() {
this._hasChanges = true;
}
// new model instance
var instance = new that.Model({
parameters : options
, orm : _options.orm
, definition : _options.definition
, isFromDB : options && options._isFromDB
, relatingSets : relatingSets
, getDatabase : _options.getDatabase
});
// clone events from global model
/*if (this.$$$$_events) {
Object.keys(this.$$$$_events).forEach(function(event){
this.$$$$_events[event].forEach(function(listener){
if (!instance.$$$$_events[event]) nstance.$$$$_events[event] = [];
instance.$$$$_events[event].push(listener);
});
});
}*/
, getDefinition: function() {
return this._defintion;
}
return instance;
}
else {
// return a querybuilder
var qb = new that.QueryBuilder({
parameters: Array.prototype.slice.call(arguments)
});
, isModel: function() {
return true;
}
// call the specific method on the querybuilder
// qb[_options.definition.getTableName()].apply(qb, Array.prototype.slice.call(arguments));
return qb;
}
};
, getEntityName: function() {
return this._defintion.name;
}
// the constructor implements the event interface
// the events are global listeners for all model instances
//Constructor.__proto__ = new EventEmitter();
// the model definition must be accesible publicly
Constructor.definition = _options.definition;
, _setValues: function(values){
if (!type.undefined(values) && !type.object(values)) throw new Error('expecting an object contining value sfor a new model instance, got «'+type(values)+'»!');
Object.keys(values).forEach(function(property){
var item = values[property];
// expose if its a mapping table
if (this._definition.isMapping) Constructor.isMapping = true;
if (type.object(item) && this._addQueryObject(property, item)) return;
else if (type.array(item)) {
item.forEach(function(obj){
if (type.object(obj) && this._addQueryObject(property, obj)) return;
else throw new Error('Expected a Query or a Model for the key «'+property+'»!');
}.bind(this));
}
else if (property === '____id____') this._mappingIds.push(item);
else this._values[property] = item;
}.bind(this));
}
// let the user define accessornames
Constructor.setMappingAccessorName = this.setMappingAccessorName.bind(this);
Constructor.setReferenceAccessorName = this.setReferenceAccessorName.bind(this);
Constructor.getDefinition = this.getDefinition.bind(this);
return Constructor;
}
, _addQueryObject: function(propertyName, item) {
var name = propertyName;
if (type.object(item) && !type.null(item) && ((item.isModel && item.isModel()) || item.isQuery)) {
// we got a model
// name = item.getEntityName();
// log.wtf(name, this._columns[name].type);
, getDefinition: function() {
return this._definition;
}
if (this._columns[name] && (this._columns[name].type === 'mapping' || this._columns[name].type === 'belongsTo')) this[name].push(item);
else if (this._columns[name] && this._columns[name].type === 'reference') this[name] = item;
else if (this._genericAccessors[name]) throw new Error('Cannot add «'+name+'» model to «'+this.getEntityName()+'» model! please use the generic accessor!');
else throw new Error('Cannot create a relation between the «'+name+'» and «'+this.getEntityName()+'» models! there is no relation between the two models!');
return true;
}
return false;
}
, setMappingAccessorName: function(mappingName, name) {
if (!this.Model[name]) {
this._mappingMap[mappingName].definition.name = this._orm._getPluralAccessorName(name);
this._mappingMap[mappingName].definition.useGenericAccessor = false;
this.Model = this.createModel(DefaultModel, this._options);
}
else throw new Error('The mapping accessor «'+name+'» on the model «'+this._model.name+'» is already in use!');
}
, _createMappingGetter: function(mappingName, options) {
return {
enumerable: true
, get: function() {
// create referencing set only when used
if (!this._mappings[mappingName]) {
this._mappings[mappingName] = new RelatingSet({
orm: options.orm
, definition: options.definition
, column: options.column
, related: this
, database: options.databaseName
, isMapping: true
});
}
this._mappings[mappingName].on('change', function(){
this._setChanged();
}.bind(this));
return this._mappings[mappingName];
}
};
}
, loadAll: function(callback){
return this.reload(callback);
}
, isFromDatabase: function(){
return this._fromDb;
}
, setReferenceAccessorName: function(referenceName, name) {
if (!this.Model[name]) {
this._referenceMap[referenceName].aliasName = name;
this._referenceMap[referenceName].useGenericAccessor = false;
this.Model = this.createModel(DefaultModel, this._options);
}
else throw new Error('The reference accessor «'+name+'» on the model «'+this._model.name+'» is already in use!');
}
, isSaved: function() {
this.isFromDatabase() && !this._hasChanges;
}
, createModel: function(model, options, additionalProperties){
var CustomModel = clone(model.definition)
, databaseName = this._definition.getDatabaseName()
, properties = additionalProperties || {}
, mappingMap = this._mappingMap = {}
, belongsToMap = this._belongsToMap = {}
, referenceMap = this._referenceMap = {};
, reload: function(callback, transaction){
if (!this.isFromDatabase()) return callback(new Error('Cannot reload record «'+this.getEntityName()+'» without saving it first!'));
// make sure the instantiated model can get the correct orm instance (support for transactions)
CustomModel.getDatabase = options.getDatabase;
var query = {
select : ['*']
, from : this._defintion.getTableName()
, database : this._defintion.getDatabaseName()
, filter : {}
};
callback = callback || function(){};
properties._columns = {
value: {}
};
this._defintion.primaryKeys.forEach(function(key){
query.filter[key] = this[key];
}.bind(this));
(transaction || this._getDatabase()).executeQuery(query, function(err, data){
if (err) callback(err);
else {
if (!data.length) callback(new Error('Failed to load data from database, record doesn\'t exist!'));
else {
this._setValues(data[0]);
this._fromDb = true;
// build model
Object.keys(this._definition.columns).forEach(function(columnName){
var column = this._definition.columns[columnName]
, definition = properties[columnName] = {}
, referenceDefinition
, referenceName;
this._reloadRelated(function(err){
callback(err, this);
}.bind(this), transaction);
}
}
}.bind(this));
return this;
}
// mappings
if (column.mapsTo){
column.mapsTo.filter(function(mapping){
mappingMap[mapping.via.model.name] = {
column: column
, definition: mapping
};
return !mapping.useGenericAccessor;
}).forEach(function(mapping){
var mappingName = mapping.via.model.name;
, _reloadRelated: function(callback, transaction) {
async.each(Object.keys(this._mappings), function(keyName, next){
this._mappings[keyName].reload(next, transaction);
}.bind(this), callback);
/*
Object.keys(this._belongsTo).forEach(function(keyName){
}.bind(this));
properties._columns.value[mappingName] = {
type : 'mapping'
, column : column
};
Object.keys(this._references).forEach(function(keyName){
}.bind(this));*/
}
properties[mapping.name] = this._createMappingGetter(mappingName, {
orm: options.orm
, definition: mapping
, column: column
, database: databaseName
});
}.bind(this));
}
// belongs to
if (column.belongsTo){
column.belongsTo.filter(function(belongs){
belongsToMap[belongs.model.name] = {
column: column
, definition: belongs
};
return !belongs.useGenericAccessor;
}).forEach(function(belongs){
var relationName = belongs.model.name;
, _getChangedValues: function() {
var data = {};
properties._columns.value[relationName] = {
type : 'belongsTo'
, column : column
};
this._changedValues.forEach(function(key){
data[key] = this[key];
}.bind(this));
properties[belongs.name] = {
enumerable: true
, get: function() {
// create referencing set only when used
if (!this._belongsTo[relationName]) {
this._belongsTo[relationName] = new RelatingSet({
orm: options.orm
, definition: belongs
, column: column
, related: this
, database: databaseName
, isMapping: false
});
return data;
}
this._belongsTo[relationName].on('chnage', function(){
this._setChanged();
}.bind(this));
}
return this._belongsTo[relationName];
}
};
}.bind(this));
}
// references
if (column.referencedModel) {
referenceMap[column.name] = column;
if (!column.useGenericAccessor) {
referenceName = column.aliasName || column.referencedModel.name;
referenceDefinition = properties[referenceName] = {enumerable: true};
properties._columns.value[referenceName] = {
type : 'reference'
, column : column
};
, delete: function(callback) {
var args = new Arguments(arguments)
, callback = args.getFunction(function(){})
, transaction = args.getObject();
referenceDefinition.get = function(){
return this._references[referenceName];
};
if (transaction) {
this._delete(transaction, callback);
}
else {
transaction = this._getDatabase().createTransaction();
referenceDefinition.set = function(newValue){
if (this._references[referenceName] !== newValue) {
this._changedReferences.push(referenceName);
this._references[referenceName] = newValue;
this._setChanged();
}
};
}
}
else {
definition.enumerable = true;
properties._columns.value[columnName] = {
type : 'scalar'
, column : column
};
}
this._delete(transaction, function(err){
if (err) {
transaction.rollback(function(transactionErr){
if (transactionErr) callback(transactionErr);
else callback(err);
}.bind(this));
}
else {
transaction.commit(function(err){
if (err) callback(err);
else {
this._fromDb = false;
callback(null, this);
}
}.bind(this));
}
}.bind(this));
}
return this;
}
definition.get = function(){
return this._values[columnName];
};
definition.set = function(value){
if (this._values[columnName] !== value) {
this._changedValues.push(columnName);
this._values[columnName] = value;
this._setChanged();
}
};
}.bind(this));
, _delete: function(transaction, callback) {
// generic belongsTo, mapping, refernce accessor
properties.get = {
value: function(targetName) {
var query = {
from : this._defintion.getTableName()
, database : this._defintion.getDatabaseName()
, filter : {}
, limit : 1
};
}
}
// icannot delete a model not loaded from the database
if (this._fromDb){
this._defintion.primaryKeys.forEach(function(key){
query.filter[key] = this[key];
}.bind(this));
// generic accessor method for mappings
properties.getMapping = {
value: function(mappingName){
if (mappingMap[mappingName]) {
// create referencing set only when used
if (!this._mappings[mappingName]) {
this._mappings[mappingName] = new RelatingSet({
orm: options._orm
, definition: mappingMap[mappingName].definition
, column: mappingMap[mappingName].column
, related: this
, database: databaseName
});
if (!Object.keys(query.filter).length) {
log.dir(query);
throw new Error('Failed to create proper delete query, no filter was created (see query definition above)');
}
else transaction.executeQuery('delete', query, callback);
}
else callback(new Error('Cannot delete model, it wasn\'t loaded from the database!'));
}
this._mappings[mappingName].on('chnage', function(){
this._setChanged();
}.bind(this));
}
return this._mappings[mappingName];
}
else throw new Error('Mapping via «'+mappingName+'» on entity «'+options.definition.name+'» doesn\'t exist!');
}
};
// generic accessor method for belongTo
properties.getBelongsTo = {
value: function(belongingName){
if (belongsToMap[belongingName]) {
// create referencing set only when used
if (!this._belongsTo[belongingName]) {
this._belongsTo[belongingName] = new RelatingSet({
orm: options._orm
, definition: belongsToMap[belongingName].definition
, column: belongsToMap[belongingName].column
, related: this
, database: databaseName
});
this._belongsTo[belongingName].on('chnage', function(){
this._setChanged();
}.bind(this));
}
return this._belongsTo[belongingName];
}
else throw new Error('Belongs to «'+belongingName+'» on entity «'+options.definition.name+'» doesn\'t exist!');
}
};
, clone: function() {
var clonedInstance = new (this._getDatabase()[this._defintion.model.name])();
}
// generic accessor method for reference
properties.getReference = {
value: function(referenceName){
if (referenceMap[referenceName]) {
return this._references[referenceName];
}
else throw new Error('Reference on «'+referenceName+'» on entity «'+options.definition.name+'» doesn\'t exist!');
}
};
properties.setReference = {
value: function(referenceName, newReferenceModel, existing){
if (referenceMap[referenceName]) {log.wtf(this._references[referenceName] !== newReferenceModel);
if (!existing && this._references[referenceName] !== newReferenceModel) {
this._changedReferences.push(referenceName);
this._references[referenceName] = newReferenceModel;
this._setChanged();
}
}
else throw new Error('Reference on «'+referenceName+'» on entity «'+options.definition.name+'» doesn\'t exist!');
}
};
return new Class(CustomModel, properties);
}
, save: function() {
var args = new Arguments(arguments)
, callback = args.getFunction(function(){})
, noReload = args.getBoolean(false)
, transaction = args.getObject();
// transactio management
if (transaction) {
this._save(transaction, noReload, callback);
}
else {
transaction = this._getDatabase().createTransaction();
this._save(transaction, noReload, function(err){
if (err) {
transaction.rollback(function(transactionErr){
if (transactionErr) callback(transactionErr);
else callback(err);
}.bind(this));
}
else {
transaction.commit(function(err){
if (err) callback(err);
else {
this._fromDb = true;
this.reload(function(err){
if (err) callback(err);
else callback(null, this);
}.bind(this));
}
}.bind(this));
}
}.bind(this));
}
, capitalize: function(input) {
return input[0].toUpperCase() + input.slice(1);
}
});
return this;
}
, _save: function(transaction, noReload, callback) {
this._saveReferences(transaction, noReload, function(err){
if (err) callback(err);
else {
var query = {
from : this._defintion.getTableName()
, database : this._defintion.getDatabaseName()
, filter : {}
, values : this._fromDb ? this._getChangedValues() : this._values
};
if (this._defintion.primaryKeys.length) {
query.returning = this._defintion.primaryKeys;
}
//log.error(this._defintion.getTableName());
//log(query);
if (this._fromDb){
if (this._changedValues.length) {
this._defintion.primaryKeys.forEach(function(key){
query.filter[key] = this[key];
}.bind(this));
transaction.executeQuery('update', query, function(err){
if (err) callback(err);
else this._saveChildren(transaction, noReload, callback);
}.bind(this));
}
else this._saveChildren(transaction, noReload, callback);
}
else {
transaction.executeQuery('insert', query, function(err, result){
if (err) callback(err);
else {
if (result.type === 'id'){
if(result.id) {
if (this._defintion.primaryKeys.length === 1){
this[this._defintion.primaryKeys[0]] = result.id;
}
else throw new Error('Cannot load record with more than one primarykey when at least on of the primary keys has an autoincermented value!');
}
this._saveChildren(transaction, noReload, callback);
}
else throw new Error('not implemented!');
}
}.bind(this));
}
}
}.bind(this));
}
, _saveChildren: function(transaction, noReload, callback) {
async.wait(function(done) {
this._saveMappings(transaction, noReload, done);
}.bind(this)
, function(done) {
this._saveBelongsTo(transaction, noReload, done);
}.bind(this), callback);
}
, _saveBelongsTo: function(transaction, noReload, callback) {
async.each(Object.keys(this._belongsTo), function(belongsToId, next){
this._belongsTo[belongsToId].save(transaction, noReload, next);
}.bind(this), callback);
}
, _saveMappings: function(transaction, noReload, callback) {
async.each(Object.keys(this._mappings), function(mappingId, next){
this._mappings[mappingId].save(transaction, noReload, next);
}.bind(this), callback);
}
, _saveReferences: function(transaction, noReload, callback) {
async.each(this._changedReferences, function(key, next){
var value = this._references[key]
, column = this._columns[key].column;
// a query was set as reference
if (value.isQuery) {
value.limit(1);
value.findOne(function(err, model) {
if (err) next(err);
else if (!model) {
this[column.name] = null;
this[value.tableName] = null;
next();
}
else {
this[column.name] = model[column.referencedColumn];
this[value.tableName] = model;
next();
}
}.bind(this));
}
else {
if (value.isSaved()) {
this[column.name] = value[column.referencedColumn];
next();
}
else value._save(transaction, noReload, function(err){
if (err) next(err);
else {
this[column.name] = value[column.referencedColumn];
next();
}
}.bind(this));
}
}.bind(this), callback);
}
, dir: function(returnResult) {
var obj = {};
Object.keys(this._columns).forEach(function(keyName) {
var column = this._columns[keyName];
switch (column.type) {
case 'mapping':
if (this._mappings[keyName]) obj[keyName] = this[keyName].dir(true);
break;
case 'belongsTo':
if (this._belongsTo[keyName]) obj[keyName] = this[keyName].dir(true);
break;
case 'reference':
if (this._references[keyName]) obj[keyName] = (typeof this[keyName] === 'object' && typeof this[keyName].dir === 'function' ? this[keyName].dir(true) : this[keyName]);
break;
case 'scalar':
if (!type.undefined(this[keyName])) obj[keyName] = this[keyName];
break;
default:
throw new Exception('Column type «'+column.type+'» is not supported!');
}
}.bind(this));
if (returnResult) return obj;
else log(obj);
}
, toJSON: function(){
var obj = {};
Object.keys(this._columns).forEach(function(keyName) {
var column = this._columns[keyName];
switch (column.type) {
case 'mapping':
if (this._mappings[keyName]) obj[keyName] = this[keyName];
break;
case 'belongsTo':
if (this._belongsTo[keyName]) obj[keyName] = this[keyName];
break;
case 'reference':
if (this._references[keyName]) obj[keyName] = this[keyName];
break;
case 'scalar':
if (!type.undefined(this[keyName])) obj[keyName] = this[keyName];;
break;
default:
throw new Exception('Column type «'+column.type+'» is not supported!');
}
}.bind(this));
return obj;
}
});
}();
!function(){
var Class = require('ee-class')
, log = require('ee-log')
, type = require('ee-types')
, EventEmitter = require('ee-event-emitter')
, arg = require('ee-arguments')
, async = require('ee-async')
, DBCluster = require('ee-db-cluster'); //*/require('../../ee-db-cluster');
var Class = require('ee-class')
, log = require('ee-log')
, type = require('ee-types')
, EventEmitter = require('ee-event-emitter')
, debug = require('ee-argv').has('debug-sql')
, async = require('ee-async')
, argv = require('ee-argv')
, DBCluster = require('ee-db-cluster'); //*/require('../../ee-db-cluster');
var Database = require('./Database')
, StaticORM = require('./StaticORM')
, staticORM;
var Database = require('./Database')
, StaticORM = require('./StaticORM')
, staticORM;
var dev = argv.has('dev-orm');
var ORM = new Class({
inherits: EventEmitter
, init: function(options) {
this._setProperty('_options', options);
this._setProperty('_dbNames', []);
this._setProperty('_databases', {});
var ORM = new Class({
inherits: EventEmitter
// db connectivity
this._initializeDatabases(options);
, init: function(options) {
Class.define(this, '_options', Class(options));
Class.define(this, '_dbNames', Class([]));
Class.define(this, '_databases', Class({}));
this._initializeOrm(function(err){
this.emit('load', err);
}.bind(this));
}
// db connectivity
this._initializeDatabases(options);
this._initializeOrm(function(err){
this.emit('load', err);
}.bind(this));
}
, _setProperty: function(name, value){
Object.defineProperty(this, name, {value: value});
}
// reload definitions
, reload: function(callback) {
this._initializeOrm(callback);
}
, _initializeOrm: function(callback) {
async.each(this._dbNames
// get definition from database
, function(databaseName, next){
this._databases[databaseName].describe([databaseName], function(err, databases){
if (err) next(err);
else {
// push config to next step
next(null, databaseName, databases[databaseName]);
}
}.bind(this));
}.bind(this)
// initialize orm per databse
, function(databaseName, definition, next){
if (this[databaseName]) next(new Error('Failed to load ORM for database «'+databaseName+'», the name is reserved for the orm.').setName('ORMException'));
else {
, _initializeOrm: function(callback) {
if (dev) log.debug('initializing ORM ...');
// create names for mapping / reference accessor, handle duplicates
this._manageAccessorNames(definition);
async.each(this._dbNames
this[databaseName] = new Database({
orm: this
, definition: definition
, database: this._databases[databaseName]
, on: {
load: next
}
});
}
}.bind(this)
// remove existing
, function(databaseName, next){
if (this[databaseName] && this[databaseName].createTransaction) {
if (dev) log.debug('removing existing db instance «'+databaseName+'»...');
delete this[databaseName];
}
// check for errors
, function(err, results){
if (err) callback(err);
else callback();
}.bind(this));
}
next(null, databaseName);
}.bind(this)
// get definition from database
, function(databaseName, next){
this._databases[databaseName].describe([databaseName], function(err, databases){
if (dev) log.debug('got db definition for «'+databaseName+'»...');
if (err) next(err);
else {
// push config to next step
next(null, databaseName, databases[databaseName]);
}
}.bind(this));
}.bind(this)
, _manageAccessorNames: function(definition) {
Object.keys(definition).forEach(function(tablename){
var model = definition[tablename]
, usedNames = {};
// initialize orm per databse
, function(databaseName, definition, next){
if (this[databaseName]) next(new Error('Failed to load ORM for database «'+databaseName+'», the name is reserved for the orm.').setName('ORMException'));
else {
Object.keys(model.columns).forEach(function(columnName){
var column = model.columns[columnName]
, name;
// create names for mapping / reference accessor, handle duplicates
this._manageAccessorNames(definition);
if (column.mapsTo) {
column.mapsTo.forEach(function(mapping){
name = mapping.name;
if (dev) log.debug('creating new db instance for «'+databaseName+'»...');
if (model.columns[name]) {
// the name is used by a column, cannot reference directly
log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
mapping.useGenericAccessor = true;
}
else if (usedNames[name]) {
// the name was used before by either a mapping or a reference
// we cannot use it
log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
usedNames[name].useGenericAccessor = true;
mapping.useGenericAccessor = true;
}
else usedNames[name] = mapping;
}.bind(this));
}
if (column.belongsTo) {
column.belongsTo.forEach(function(beloning){
name = beloning.name;
this[databaseName] = new Database({
orm: this
, definition: definition
, database: this._databases[databaseName]
});
if (model.columns[name]) {
log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name is used by a column, cannot reference directly
beloning.useGenericAccessor = true;
}
else if (usedNames[name]) {
log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name was used before by either a mapping or a reference
// we cannot use it
usedNames[name].useGenericAccessor = true;
beloning.useGenericAccessor = true;
}
else usedNames[name] = beloning;
}.bind(this));
}
if (column.referencedModel) {
name = column.referencedModel.name;
if (model.columns[name]) {
log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name is used by a column, cannot reference directly
column.useGenericAccessor = true;
}
else if (usedNames[name]) {
log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name was used before by either a mapping or a reference
// we cannot use it
usedNames[name].useGenericAccessor = true;
column.useGenericAccessor = true;
}
else usedNames[name] = column;
}
}.bind(this));
}.bind(this));
}
this[databaseName].on('load', next);
}
}.bind(this)
// check for errors
, function(err, results){
if (dev) log.warn('all dbs loaded ...');
, getDatabase: function(id){
if (!type.string(id) || !id.length) throw new Error('cannot return a db without knowing which on to return (argument 0 must be the db id!)');
return this._databases[id];
}
if (err) callback(err);
else callback();
}.bind(this));
}
, _initializeDatabases: function(options){
if (type.object(options)) {
Object.keys(options).forEach(function(databaseName){
if (!type.string(options[databaseName].type)) throw new Error('['+databaseName+'] > Database type not in config specified (type: \'mysql\' / \'postgres\')!');
if (!type.array(options[databaseName].hosts) || !options[databaseName].hosts.length) throw new Error('['+databaseName+'] > Please add at least one host per db in the config!');
this._dbNames.push(databaseName);
this._databases[databaseName] = new DBCluster({type: options[databaseName].type});
, _manageAccessorNames: function(definition) {
Object.keys(definition).forEach(function(tablename){
var model = definition[tablename]
, usedNames = {};
options[databaseName].hosts.forEach(function(config){
config.database = config.database || databaseName;
this._databases[databaseName].addNode(config);
}.bind(this));
}.bind(this));
}
else throw new Error('no database configuration present!');
}
});
Object.keys(model.columns).forEach(function(columnName){
var column = model.columns[columnName]
, name;
if (column.mapsTo) {
column.mapsTo.forEach(function(mapping){
name = mapping.name;
staticORM = new StaticORM();
Object.keys(Object.getPrototypeOf(staticORM)).forEach(function(key){
if(!ORM[key]) ORM[key] = staticORM[key];
});
module.exports = ORM;
if (model.columns[name]) {
// the name is used by a column, cannot reference directly
if (debug) log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
mapping.useGenericAccessor = true;
}
else if (usedNames[name]) {
// the name was used before by either a mapping or a reference
// we cannot use it
if (debug) log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
usedNames[name].useGenericAccessor = true;
mapping.useGenericAccessor = true;
}
else usedNames[name] = mapping;
}.bind(this));
}
if (column.belongsTo) {
column.belongsTo.forEach(function(beloning){
name = beloning.name;
if (model.columns[name]) {
if (debug) log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name is used by a column, cannot reference directly
beloning.useGenericAccessor = true;
}
else if (usedNames[name]) {
if (debug) log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name was used before by either a mapping or a reference
// we cannot use it
usedNames[name].useGenericAccessor = true;
beloning.useGenericAccessor = true;
}
else usedNames[name] = beloning;
}.bind(this));
}
if (column.referencedModel) {
name = column.referencedModel.name;
if (model.columns[name]) {
if (debug) log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name is used by a column, cannot reference directly
column.useGenericAccessor = true;
}
else if (usedNames[name]) {
if (debug) log.warn('«'+model.name+'» cannot use the accessor «'+name+'», it\'s already used by another property');
// the name was used before by either a mapping or a reference
// we cannot use it
usedNames[name].useGenericAccessor = true;
column.useGenericAccessor = true;
}
else usedNames[name] = column;
}
}.bind(this));
}.bind(this));
}
, getDatabase: function(id){
if (!type.string(id) || !id.length) throw new Error('cannot return a db without knowing which on to return (argument 0 must be the db id!)');
return this._databases[id];
}
, _initializeDatabases: function(options){
if (type.object(options)) {
Object.keys(options).forEach(function(databaseName){
if (!type.string(options[databaseName].type)) throw new Error('['+databaseName+'] > Database type not in config specified (type: \'mysql\' / \'postgres\')!');
if (!type.array(options[databaseName].hosts) || !options[databaseName].hosts.length) throw new Error('['+databaseName+'] > Please add at least one host per db in the config!');
this._dbNames.push(databaseName);
this._databases[databaseName] = new DBCluster({type: options[databaseName].type});
options[databaseName].hosts.forEach(function(config){
config.database = config.database || databaseName;
this._databases[databaseName].addNode(config);
}.bind(this));
}.bind(this));
}
else throw new Error('no database configuration present!');
}
});
// set static methods on the ORM constructor
Class.implement(new StaticORM(), ORM);
module.exports = ORM;
}();
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log');
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log');

@@ -10,26 +10,26 @@

module.exports = new Class({
module.exports = new Class({
init: function(options) {
this.filter = options.filter || {};
this.select = options.select || [];
this.from = options.from || 'undefined';
this.database = options.database || 'undefined';
this.join = options.join || [];
this.group = options.group || [];
}
init: function(options) {
this.filter = options.filter || {};
this.select = options.select || [];
this.from = options.from || 'undefined';
this.database = options.database || 'undefined';
this.join = options.join || [];
this.group = options.group || [];
}
, addSeleted: function(select) {
this.select = this.select.concat(select);
}
, addSeleted: function(select) {
this.select = this.select.concat(select);
}
, formatJoins: function() {
this.join = this.join.map(function(join){
return join.unformatted ? join.format() : join;
});
}
});
, formatJoins: function() {
this.join = this.join.map(function(join){
return join.unformatted ? join.format() : join;
});
}
});
}();
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, clone = require('clone')
, args = require('ee-arguments')
, type = require('ee-types')
, Set = require('./Set')
, Query = require('./Query')
, Resource = require('./Resource')
, JoinStatement = require('./JoinStatement')
, QueryCompiler = require('./QueryCompiler')
, ORM;
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, clone = require('clone')
, Arguments = require('ee-arguments')
, type = require('ee-types')
, Set = require('./Set')
, Query = require('./Query')
, Resource = require('./Resource')
, JoinStatement = require('./JoinStatement')
, QueryCompiler = require('./QueryCompiler')
, ORM;

@@ -20,673 +20,429 @@

var QueryBuilder = {
module.exports = new Class({
isQuery: true
isQuery: true
, getEntityName: function() {
return this.tableName;
}
, getEntityName: function() {
return this.tableName;
}
, init: function(options) {
, init: function(options) {
// remove deprecated parent property (ee-class implementation)
delete this.parent;
// load circular depency if not alread loaded
if (!ORM) ORM = require('./ORM');
// load circular depency if not alread loaded
if (!ORM) ORM = require('./ORM');
// the mapping that us got here
Class.define(this, '_mapping', Class(options.mapping));
// this will give us acces to the orm, the querybuilders and the
// table definition, this method is set by the exported class below
this._setProperties();
// we need to access this properts often
this.tableName = this._definition.getTableName();
this.databaseName = this._definition.getDatabaseName();
// the mapping that us got here
this._setProperty('_mapping', options.mapping);
// subjoins used for eager loading
Class.define(this, '_joins', Class(options.joins || []));
// we need to access this properts often
this.tableName = this._definition.getTableName();
this.databaseName = this._definition.getDatabaseName();
// set or define the root resource
if (options.rootResource) Class.define(this, '_rootResource', Class(options.rootResource));
else {
Class.define(this, '_rootResource', Class(new Resource({
Model : this._orm[this.databaseName][this.tableName]
, name : this.tableName
, primaryKeys : this._definition.primaryKeys
, rootFiltered : true
, query : new Query({
from: this.tableName
, database: this.databaseName
})
})));
this._addPrimarySelect();
}
// subjoins used for eager loading
this._setProperty('_joins', options.joins || []);
// the query we're currently working on
Class.define(this, '_resource', Class(options.resource || this._rootResource));
// set or define the root resource
if (options.rootResource) this._setProperty('_rootResource', options.rootResource);
else {
this._setProperty('_rootResource', new Resource({
Model : this._orm[this.databaseName][this.tableName]
, name : this.tableName
, primaryKeys : this._definition.primaryKeys
, rootFiltered : true
, query : new Query({
from: this.tableName
, database: this.databaseName
})
}));
this._addPrimarySelect();
}
// parse passed parameters (happens only on the root of the query)
if (options.parameters) {
this._parseFilter(this._resource.query, this.tableName, this._getFilters(options.parameters));
this._parseSelect(this._resource.query, this.tableName, this._getSelect(options.parameters));
}
}
// the query we're currently working on
this._setProperty('_resource', options.resource || this._rootResource);
/**
* the _handleReference method is called when the user selects
* a referenced subresource on the querybuilder
*
* column <Object> definition of the column of this
* table which refrences the other table
* targetModel <Object> definition of the targeted model of
* the reference
* queryParameters <Array> parameters passed like the filter &
* selcet statements
* returnTarget <Boolean> optional, if we're returning the reference
* for this model or the reference of the
* targeted model
*/
, _handleReference: function(column, targetModel, queryParameters, returnTarget) {
var select = this._getSelect(queryParameters)
, filter = this._getFilters(queryParameters)
, targetModelName = targetModel.name
, group = []
, resource
, joins;
// parse passed parameters (happens only on the root of the query)
if (options.parameters) {
this._parseFilter(this._resource.query, this.tableName, this._getFilters(options.parameters));
this._parseSelect(this._resource.query, this.tableName, this._getSelect(options.parameters));
}
}
// create basic join definition, it will be converted to more specific join
// statetments later on
joins = [new JoinStatement({
source : {
table : column.referencedTable
, column : column.referencedColumn
}
, target: {
table : this.tableName
, column : column.name
}
})];
targetModel.primaryKeys.forEach(function(key){
group.push({
table : targetModelName
, column : key
});
});
/**
* the _handleReference method is called when the user selects
* a referenced subresource on the querybuilder
*
* column <Object> definition of the column of this
* table which refrences the other table
* targetModel <Object> definition of the targeted model of
* the reference
* queryParameters <Array> parameters passed like the filter &
* selcet statements
* returnTarget <Boolean> optional, if we're returning the reference
* for this model or the reference of the
* targeted model
*/
, _handleReference: function(column, targetModel, queryParameters, returnTarget){
var select = this._getSelect(queryParameters)
, filter = this._getFilters(queryParameters)
, targetModelName = targetModel.name
, group = []
, resource
, joins;
// create basic join definition, it will be converted to more specific join
// statetments later on
joins = [new JoinStatement({
source : {
table : column.referencedTable
, column : column.referencedColumn
}
, target: {
table : this.tableName
, column : column.name
}
})];
// create a child tree node for the querybuilder
resource = new Resource({
name : targetModelName
, selected : !!Object.keys(select).length
, parentResource : this._resource
, Model : this._orm[this.databaseName][targetModelName]
, referencedParentColumn : column.name
, referencedParentTable : this.tableName
, joins : joins
, filters : filter
, rootResource : this._rootResource
, primaryKeys : targetModel.primaryKeys
, type : 'reference'
, loaderId : column.name
, query : new Query({
join : joins.concat(this._joins)
, database : this.databaseName
, from : targetModelName
, filter : clone(this._resource.query.filter)
, group : group
})
});
targetModel.primaryKeys.forEach(function(key){
group.push({
table : targetModelName
, column : key
});
});
// add primary keys to default seleczt
targetModel.primaryKeys.forEach(function(key){resource.defaultSelect.push(key);});
// process options / select on subquery
this._parseSelect(resource.query, targetModelName, select);
// store the subquery on the current query
this._resource.children.push(resource);
// create a child tree node for the querybuilder
resource = new Resource({
name : targetModelName
, selected : !!Object.keys(select).length
, parentResource : this._resource
, Model : this._orm[this.databaseName][targetModelName]
, referencedParentColumn : column.name
, referencedParentTable : this.tableName
, joins : joins
, filters : filter
, rootResource : this._rootResource
, primaryKeys : targetModel.primaryKeys
, type : 'reference'
, loaderId : column.name
, query : new Query({
join : joins.concat(this._joins)
, database : this.databaseName
, from : targetModelName
, filter : clone(this._resource.query.filter)
, group : group
})
});
// we may stay at the same level (e.g. fetchModel vs. getModel)
if (returnTarget) {
return new this._queryBuilders[targetModelName]({
resource : resource
, rootResource : this._rootResource
, joins : joins.concat(this._joins)
});
}
else {
return this;
}
}
// add primary keys to default seleczt
targetModel.primaryKeys.forEach(function(key){resource.defaultSelect.push(key);});
// process options / select on subquery
this._parseSelect(resource.query, targetModelName, select);
// store the subquery on the current query
this._resource.children.push(resource);
, _handleBelongsTo: function(column, targetModel, queryParameters, returnTarget) {
var select = this._getSelect(queryParameters)
, filter = this._getFilters(queryParameters)
, targetModelName = targetModel.model.name
, group = []
, resource
, joins;
// create basic join definition, it will be converted to more specific join
// statetments later on
joins = [new JoinStatement({
source : {
table : targetModel.model.name
, column : targetModel.targetColumn
}
, target: {
table : this.tableName
, column : column.name
}
})];
// we may stay at the same level (e.g. fetchModel vs. getModel)
if (returnTarget) {
return new this._queryBuilders[targetModelName]({
resource : resource
, rootResource : this._rootResource
, joins : joins.concat(this._joins)
});
}
else {
return this;
}
}
targetModel.model.primaryKeys.forEach(function(key){
group.push({
table : targetModelName
, column : key
});
});
// create a child tree node for the querybuilder
resource = new Resource({
name : targetModelName
, selected : !!Object.keys(select).length
, parentResource : this._resource
, Model : this._orm[this.databaseName][targetModelName]
, referencedParentColumn : column.name
, referencedParentTable : this.tableName
, joins : joins
, filters : filter
, rootResource : this._rootResource
, primaryKeys : targetModel.model.primaryKeys
, type : 'belongsTo'
, loaderId : targetModel.model.name
, query : new Query({
join : joins.concat(this._joins)
, database : this.databaseName
, from : targetModelName
, filter : clone(this._resource.query.filter)
, group : group
})
});
, _handleBelongsTo: function(column, targetModel, queryParameters, returnTarget) {
var select = this._getSelect(queryParameters)
, filter = this._getFilters(queryParameters)
, targetModelName = targetModel.model.name
, group = []
, resource
, joins;
// add primary keys to default select
targetModel.model.primaryKeys.forEach(function(key){resource.defaultSelect.push(key);});
// create basic join definition, it will be converted to more specific join
// statetments later on
joins = [new JoinStatement({
source : {
table : targetModel.model.name
, column : targetModel.targetColumn
}
, target: {
table : this.tableName
, column : column.name
}
})];
// process options / select on subquery
this._parseSelect(resource.query, targetModelName, select);
// store the subquery on the current query
this._resource.children.push(resource);
targetModel.model.primaryKeys.forEach(function(key){
group.push({
table : targetModelName
, column : key
});
});
// create a child tree node for the querybuilder
resource = new Resource({
name : targetModelName
, selected : !!Object.keys(select).length
, parentResource : this._resource
, Model : this._orm[this.databaseName][targetModelName]
, referencedParentColumn : column.name
, referencedParentTable : this.tableName
, joins : joins
, filters : filter
, rootResource : this._rootResource
, primaryKeys : targetModel.model.primaryKeys
, type : 'belongsTo'
, loaderId : targetModel.model.name
, query : new Query({
join : joins.concat(this._joins)
, database : this.databaseName
, from : targetModelName
, filter : clone(this._resource.query.filter)
, group : group
})
});
// we may stay at the same level (e.g. fetchModel vs. getModel)
if (returnTarget) {
return new this._queryBuilders[targetModelName]({
resource : resource
, rootResource : this._rootResource
, joins : joins.concat(this._joins)
});
}
else {
return this;
}
}
// add primary keys to default seleczt
targetModel.model.primaryKeys.forEach(function(key){resource.defaultSelect.push(key);});
/*
* the _handleMapping method builds the queries for a mapping
*
* @param <String> the name of the column on our side of the mapping
* @param <Object> the definition of the targeted table of the mapping
* @param <Array> the parameters passed as filter / select / options for the mapping
* @param <Boolean> wheter to return a querybuilder instance on the targeted table
* of the paping or to stay on the same querybuilder
*/
, _handleMapping: function(column, targetModel, queryParameters, returnTarget){
var select = this._getSelect(queryParameters)
, filter = this._getFilters(queryParameters)
, targetModelName = targetModel.name
, group = []
, resource
, joins;
// process options / select on subquery
this._parseSelect(resource.query, targetModelName, select);
// create basic join definition, it will be converted to more specific join
// statetments later on
joins = [new JoinStatement({
source : {
table : targetModel.model.name
, column : targetModel.column.name
}
, target: {
table : targetModel.via.model.name
, column : targetModel.via.otherFk
}
}), new JoinStatement({
source : {
table : targetModel.via.model.name
, column : targetModel.via.fk
}
, target: {
table : this.tableName
, column : column.name
}
})];
// store the subquery on the current query
this._resource.children.push(resource);
// group by pk of taarget and by pk of the parent model (this model)
targetModel.model.primaryKeys.forEach(function(key){
group.push({
table : targetModel.model.name
, column : key
});
}.bind(this));
//log(this._definition);
this._definition.primaryKeys.forEach(function(key){
group.push({
table : this.tableName
, column : key
});
}.bind(this));
// we may stay at the same level (e.g. fetchModel vs. getModel)
if (returnTarget) {
return new this._queryBuilders[targetModelName]({
resource : resource
, rootResource : this._rootResource
, joins : joins.concat(this._joins)
});
}
else {
return this;
}
}
// create a child tree node for the querybuilder
resource = new Resource({
name : targetModelName
, selected : !!Object.keys(select).length
, parentResource : this._resource
, Model : this._orm[this.databaseName][targetModel.model.name]
, referencedParentColumn : column.name
, referencedParentTable : this.tableName
, joins : joins
, filters : filter
, rootResource : this._rootResource
, primaryKeys : targetModel.model.primaryKeys
, type : 'mapping'
, loaderId : targetModel.via.model.name
, query : new Query({
join : joins.concat(this._joins)
, database : this.databaseName
, from : targetModel.model.name
, filter : clone(this._resource.query.filter)
, group : group
})
});
/*
* the _handleMapping method builds the queries for a mapping
*
* @param <String> the name of the column on our side of the mapping
* @param <Object> the definition of the targeted table of the mapping
* @param <Array> the parameters passed as filter / select / options for the mapping
* @param <Boolean> wheter to return a querybuilder instance on the targeted table
* of the paping or to stay on the same querybuilder
*/
, _handleMapping: function(column, targetModel, queryParameters, returnTarget){
var select = this._getSelect(queryParameters)
, filter = this._getFilters(queryParameters)
, targetModelName = targetModel.name
, group = []
, resource
, joins;
// create basic join definition, it will be converted to more specific join
// statetments later on
joins = [new JoinStatement({
source : {
table : targetModel.model.name
, column : targetModel.column.name
}
, target: {
table : targetModel.via.model.name
, column : targetModel.via.otherFk
}
}), new JoinStatement({
source : {
table : targetModel.via.model.name
, column : targetModel.via.fk
}
, target: {
table : this.tableName
, column : column.name
}
})];
// add primary keys to default seleczt
targetModel.model.primaryKeys.forEach(function(key){resource.defaultSelect.push(key);});
// process options / select on subquery
this._parseSelect(resource.query, targetModel.model.name, select);
// group by pk of taarget and by pk of the parent model (this model)
targetModel.model.primaryKeys.forEach(function(key){
group.push({
table : targetModel.model.name
, column : key
});
}.bind(this));
// store the subquery on the current query
this._resource.children.push(resource);
//log(this._definition);
this._definition.primaryKeys.forEach(function(key){
group.push({
table : this.tableName
, column : key
});
}.bind(this));
// we may stay at the same level (e.g. fetchModel vs. getModel)
if (returnTarget) {
return new this._queryBuilders[targetModel.model.name]({
resource : resource
, rootResource : this._rootResource
, joins : joins.concat(this._joins)
});
}
else {
return this;
}
}
// create a child tree node for the querybuilder
resource = new Resource({
name : targetModelName
, selected : !!Object.keys(select).length
, parentResource : this._resource
, Model : this._orm[this.databaseName][targetModel.model.name]
, referencedParentColumn : column.name
, referencedParentTable : this.tableName
, joins : joins
, filters : filter
, rootResource : this._rootResource
, primaryKeys : targetModel.model.primaryKeys
, type : 'mapping'
, loaderId : targetModel.via.model.name
, query : new Query({
join : joins.concat(this._joins)
, database : this.databaseName
, from : targetModel.model.name
, filter : clone(this._resource.query.filter)
, group : group
})
});
// add primary keys to default seleczt
targetModel.model.primaryKeys.forEach(function(key){resource.defaultSelect.push(key);});
, _getFilters: function(parameters) {
return new Arguments(parameters).getObject({});
}
// process options / select on subquery
this._parseSelect(resource.query, targetModel.model.name, select);
, _getSelect: function(parameters) {
var args = new Arguments(parameters)
, stringSelect = args.getString();
// store the subquery on the current query
this._resource.children.push(resource);
return args.getArray((stringSelect? [stringSelect] : []));
}
, _getOptions: function(parameters) {
return new Arguments(parameters).getObjectByIndex(1, {});
}
// we may stay at the same level (e.g. fetchModel vs. getModel)
if (returnTarget) {
return new this._queryBuilders[targetModel.model.name]({
resource : resource
, rootResource : this._rootResource
, joins : joins.concat(this._joins)
});
}
else {
return this;
}
}
, _parseFilter: function(query, tablename, filter) {
Object.keys(filter).forEach(function(property){
if (!query.filter[tablename]) query.filter[tablename] = {};
query.filter[tablename][property] = filter[property];
}.bind(this));
}
, _parseSelect: function(query, tablename, select) {
select.forEach(function(item){
query.select.push(item);
}.bind(this));
}
, _getFilters: function(parameters) {
return args(parameters, 'object', {});
}
, _getSelect: function(parameters) {
var stringSelect = args(parameters, 'string');
return args(parameters, 'array', (stringSelect? [stringSelect] : []));
}
, limit: function(limit) {
if (type.number(limit)) this._resource.query.limit = limit;
return this;
}
, _getOptions: function(parameters) {
return args(parameters, 'object', {}, 1);
}
, offset: function(offset) {
if (type.number(offset)) this._resource.query.offset = offset;
return this;
}
, _parseFilter: function(query, tablename, filter) {
Object.keys(filter).forEach(function(property){
if (!query.filter[tablename]) query.filter[tablename] = {};
query.filter[tablename][property] = filter[property];
}.bind(this));
}
, filter: function(filter) {
this._resource.query.filter = filter;
//this._parseFilter(this._resource.query, this.tableName, options);
return this;
}
, _parseSelect: function(query, tablename, select) {
select.forEach(function(item){
query.select.push(item);
}.bind(this));
}
, _addPrimarySelect: function() {
if (this._rootResource.query.select.length) {
this._definition.primaryKeys.forEach(function(key){
this._rootResource.query.select.push(key);
}.bind(this));
}
}
, limit: function(limit) {
if (type.number(limit)) this._resource.query.limit = limit;
return this;
}
, offset: function(offset) {
if (type.number(offset)) this._resource.query.offset = offset;
return this;
}
, find: function(callback) {
new QueryCompiler({
orm : this._orm
, getDatabase : this._getDatabase
, resource : this._rootResource
}).find(callback);
}
, filter: function(filter) {
this._resource.query.filter = filter;
//this._parseFilter(this._resource.query, this.tableName, options);
return this;
}
, findOne: function(callback) {
new QueryCompiler({
orm : this._orm
, getDatabase : this._getDatabase
, resource : this._rootResource
}).findOne(callback);
}
, _addPrimarySelect: function() {
if (this._rootResource.query.select.length) {
this._definition.primaryKeys.forEach(function(key){
this._rootResource.query.select.push(key);
}.bind(this));
}
}
, find: function(callback) {
new QueryCompiler({
orm : this._orm
, getDatabase : this._getDatabase
, resource : this._rootResource
}).find(callback);
}
, findOne: function(callback) {
new QueryCompiler({
orm : this._orm
, getDatabase : this._getDatabase
, resource : this._rootResource
}).findOne(callback);
}
, delete: function(callback) {
new QueryCompiler({
orm : this._orm
, getDatabase : this._getDatabase
, resource : this._rootResource
}).delete(callback);
}
, _setProperty: function(name, value) {
Object.defineProperty(this, name, {value:value});
}
};
// initialize the querybuilder for the specific model
module.exports = new Class({
init: function(options) {
var QB = {}
, mappingMap = {}
, belongsToMap = {}
, referenceMap = {}
, description = {}
, accessorMap = {
get: {}
, fetch: {}
};
QB._setProperties = function(){
this._setProperty('_queryBuilders', options.queryBuilders);
this._setProperty('_definition', options.definition);
this._setProperty('_orm', options.orm);
this._setProperty('_getDatabase', options.getDatabase);
}
// clone class
Object.keys(QueryBuilder).forEach(function (key) {
QB[key] = QueryBuilder[key];
}.bind(this));
// create accessor methods
Object.keys(options.definition.columns).forEach(function (columnName) {
var column = options.definition.columns[columnName]
, id;
// mappin grelations
if (column.mapsTo.length) {
column.mapsTo.filter(function(target){
// store reference into map for generic methods
mappingMap[target.via.model.name] = {
column: column
, target: target
};
// filter thoose which must be accessed via the generic accessor
return !target.useGenericAccessor;
}).forEach(function(target){
accessorMap.get[target.name] = function() {
return this._handleMapping(column, target, Array.prototype.slice.call(arguments), true);
};
accessorMap.fetch[target.name] = function() {
return this._handleMapping(column, target, Array.prototype.slice.call(arguments));
};
description[this._getAccessorName(target.name)] = 'Mapping accessor for the «'+target.name+'» Model';
description[this._getAccessorName(target.name, true)] = 'Mapping accessor for the «'+target.name+'» Model';
//log.warn(definition.name, column.name, target.model.name, this.mapperGetterName(target.model.name), target.via.model.name);
Object.defineProperty(QB, this._getAccessorName(target.name), {
value : accessorMap.get[target.name]
, enumerable : true
});
Object.defineProperty(QB, this._getAccessorName(target.name, true), {
value : accessorMap.fetch[target.name]
, enumerable : true
});
}.bind(this));
}
// reference
if (column.referencedModel) {
referenceMap[column.name] = {
column: column
, target: column.referencedModel
};
if (!column.useGenericAccessor) {
accessorMap.get[column.referencedModel.name] = function() {
return this._handleReference(column, column.referencedModel, Array.prototype.slice.call(arguments));
};
accessorMap.fetch[column.referencedModel.name] = function() {
return this._handleReference(column, column.referencedModel, Array.prototype.slice.call(arguments), true);
};
description[this._getAccessorName(column.referencedModel.name)] = 'Reference accessor for the «'+column.referencedModel.name+'» Model';
description[this._getAccessorName(column.referencedModel.name, true)] = 'Reference accessor for the «'+column.referencedModel.name+'» Model';
QB[this._getAccessorName(column.referencedModel.name, true)] = accessorMap.get[column.referencedModel.name];
QB[this._getAccessorName(column.referencedModel.name)] = accessorMap.fetch[column.referencedModel.name];
}
}
// belongs to
if (column.belongsTo.length) {
column.belongsTo.filter(function(target){
// store reference into map for generic methods
belongsToMap[target.model.name] = {
column: column
, target: target
};
// filter thoose which must be accessed via the generic accessor
return !target.useGenericAccessor;
}).forEach(function(target){
//log.warn(definition.name, column.name, target.name, this.mapperGetterName(target.name));
accessorMap.get[target.name] = function() {
return this._handleBelongsTo(column, target, Array.prototype.slice.call(arguments));
};
accessorMap.fetch[target.name] = function() {
return this._handleBelongsTo(column, target, Array.prototype.slice.call(arguments), true);
};
description[this._getAccessorName(target.name)] = 'Belongs to accessor for the «'+target.name+'» Model';
description[this._getAccessorName(target.name, true)] = 'Belongs to accessor for the «'+target.name+'» Model';
QB[this._getAccessorName(target.name, true)] = accessorMap.get[target.name];
QB[this._getAccessorName(target.name)] = accessorMap.fetch[target.name];
}.bind(this));
}
}.bind(this));
// generic accessor for everything
Object.defineProperty(QB, 'get', {
value: function(targetName) {
if (Object.hasOwnProperty.call(accessorMap.get, targetName)) {
return accessorMap.get[targetName].apply(this, Array.prototype.slice.call(arguments, 1));
}
else throw new Error('The QueryBuilder has no property «'+targetName+'»!');
}
, enumerable: true
});
Object.defineProperty(QB, 'fetch', {
value: function(targetName) {
if (Object.hasOwnProperty.call(accessorMap.fetch, targetName)) {
return accessorMap.fetch[targetName].apply(this, Array.prototype.slice.call(arguments, 1));
}
else throw new Error('The QueryBuilder has no property «'+targetName+'»!');
}
, enumerable: true
});
// generic accessors for mappings
Object.defineProperty(QB, 'getMapping', {
value: function(mappingTableName) {
var info = mappingMap[mappingTableName];
if (info) return this._handleMapping(info.column, info.target, Array.prototype.slice.call(arguments, 1), true);
else throw new Error('Unknown mapping «'+mappingTableName+'» on entity «'+options.definition.name+'»!');
}
, enumerable: true
});
Object.defineProperty(QB, 'fetchMapping', {
value: function(mappingTableName) {
var info = mappingMap[mappingTableName];
if (info) return this._handleMapping(info.column, info.target, Array.prototype.slice.call(arguments, 1));
else throw new Error('Unknown mapping «'+mappingTableName+'» on entity «'+options.definition.name+'»!');
}
, enumerable: true
});
// generic accessors for references
Object.defineProperty(QB, 'getReference', {
value: function(referencedTable) {
var info = referenceMap[referencedTable];
if (info) return this._handleReference(info.column, info.target, Array.prototype.slice.call(arguments, 1), true);
else throw new Error('Unknown reference «'+referencedTable+'» on entity «'+options.definition.name+'»!');
}
, enumerable: true
});
Object.defineProperty(QB, 'fetchReference', {
value: function(referencedTable) {
var info = referenceMap[referencedTable];
if (info) return this._handleReference(info.column, info.target, Array.prototype.slice.call(arguments, 1));
else throw new Error('Unknown reference «'+referencedTable+'» on entity «'+options.definition.name+'»!');
}
, enumerable: true
});
// generic accessors for belongsto
Object.defineProperty(QB, 'getBelongsTo', {
value: function(belongingTableName) {
var info = belongsToMap[belongingTableName];
if (info) return this._handleBelongsTo(info.column, info.target, Array.prototype.slice.call(arguments, 1), true);
else throw new Error('Unknown belongstTo «'+mappingTableName+'» on entity «'+options.definition.name+'»!');
}
, enumerable: true
});
Object.defineProperty(QB, 'fetchBelongsTo', {
value: function(belongingTableName) {
var info = belongsToMap[belongingTableName];
if (info) return this._handleBelongsTo(info.column, info.target, Array.prototype.slice.call(arguments, 1));
else throw new Error('Unknown belongstTo «'+mappingTableName+'» on entity «'+options.definition.name+'»!');
}
, enumerable: true
});
// describe methods function
Object.defineProperty(QB, 'describeMethods', {
value: function() {
log(description);
return this;
}
, enumerable: true
});
return new Class(QB);
}
, _getAccessorName: function(id, useFetch) {
return (useFetch ? 'fetch' : 'get') + id[0].toUpperCase()+id.slice(1);
}
});
, delete: function(callback) {
new QueryCompiler({
orm : this._orm
, getDatabase : this._getDatabase
, resource : this._rootResource
}).delete(callback);
}
});
}();
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, async = require('ee-async')
, Set = require('./Set');
var Class = require('ee-class')
, log = require('ee-log')
, async = require('ee-async')
, Set = require('./Set');

@@ -12,54 +11,54 @@

module.exports = new Class({
init: function(options) {
this._resource = options.resource;
this._orm = options.orm;
this.getDatabase = options.getDatabase;
}
module.exports = new Class({
init: function(options) {
this._resource = options.resource;
this._orm = options.orm;
this.getDatabase = options.getDatabase;
}
, findOne: function(callback) {
this._resource.query.limit = 1;
this.find(function(err, results) {
if (err) callback(err);
else if (results && results.length) callback(null, results.first());
else callback();
}.bind(this));
}
, findOne: function(callback) {
this._resource.query.limit = 1;
this.find(function(err, results) {
if (err) callback(err);
else if (results && results.length) callback(null, results.first());
else callback();
}.bind(this));
}
, find: function(callback) {
var resource = this._resource;
, find: function(callback) {
var resource = this._resource;
// prepare child queries
this._prepareChildResources(resource);
// prepare child queries
this._prepareChildResources(resource);
// execut ebase query
this._executeQuery('query', resource.query, function(err, rows){
if (err) callback(err);
else {
var queries;
// execut ebase query
this._executeQuery('query', resource.query, function(err, rows){
if (err) callback(err);
else {
var queries;
// create set
resource.set = this._makeSet(rows, resource);
// create set
resource.set = this._makeSet(rows, resource);
if (resource.set.length) {
if (resource.set.length) {
// collect queries
queries = this._collectQueries(resource, []);
// collect queries
queries = this._collectQueries(resource, []);
if (queries && queries.length) {
// execute queries
this._executeSubqueries(resource, queries, callback);
}
else callback(null, resource.set);
}
else callback(null, resource.set);
}
}.bind(this));
}
if (queries && queries.length) {
// execute queries
this._executeSubqueries(resource, queries, callback);
}
else callback(null, resource.set);
}
else callback(null, resource.set);
}
}.bind(this));
}

@@ -69,5 +68,5 @@

, delete: function(callback) {
this._executeQuery('delete', this._resource.query, callback);
}
, delete: function(callback) {
this._executeQuery('delete', this._resource.query, callback);
}

@@ -77,164 +76,166 @@

, _executeSubqueries: function(rootResource, queries, callback) {
, _executeSubqueries: function(rootResource, queries, callback) {
async.each(queries,
async.each(queries,
function(resource, next){
function(resource, next){ //log.wtf(resource.name);
// filter the resource by the ids of the root resource
rootResource.applyFilter(resource);
// filter the resource by the ids of the root resource
rootResource.applyFilter(resource);
rootResource.applyGroup(resource);
if (resource.parentResource) resource.parentResource.applyGroup(resource);
//log(resource);
this._executeQuery('query', resource.query, function(err, rows){
if (err) next(err);
else {
resource.set = this._makeSet(rows, resource);
next();
}
}.bind(this));
}.bind(this),
this._executeQuery('query', resource.query, function(err, rows){
if (err) next(err);
else {
resource.set = this._makeSet(rows, resource);
next();
}
}.bind(this));
}.bind(this),
function(err, results){
if (err) callback(err);
else {
this._buildRelations(this._resource);
callback(null, this._resource.set);
}
}.bind(this));
}
function(err, results){
if (err) callback(err);
else {
this._buildRelations(this._resource);
callback(null, this._resource.set);
}
}.bind(this));
}
, _buildRelations: function(resource) { log.error(resource.hasChildren(), resource.query.from);
if (resource.set && resource.hasChildren()) {
resource.children.forEach(function(childResource){
if (childResource.set) {
, _buildRelations: function(resource) {
if (resource.set && resource.hasChildren()) {
resource.children.forEach(function(childResource) {
if (childResource.set) {
childResource.set.forEach(function(record){
if (childResource.set.length) {
childResource.set.forEach(function(record) {
//log(childResource);
record._mappingIds.forEach(function(mappingId) {
var parentRecord = resource.set.getByColumnValue(childResource.referencedParentColumn, mappingId);
record._mappingIds.forEach(function(mappingId){ log(mappingId);
var parentRecord = resource.set.getByColumnValue(childResource.referencedParentColumn, mappingId);
if (parentRecord) {
//log(childResource.loaderId);
if (childResource.type === 'mapping') {
parentRecord.getMapping(childResource.loaderId).addExisiting(record);
//parentRecord[childResource.name].addExisiting(record);
}
else if (childResource.type === 'belongsTo') {
parentRecord.getBelongsTo(childResource.loaderId).addExisiting(record);
//parentRecord[childResource.name].addExisiting(record);
}
else {
// reference
//parentRecord.setReference(childResource.loaderId, record, true);
parentRecord[childResource.name] = record;
}
}
}.bind(this));
}.bind(this));
}
this._buildRelations(childResource);
}
}.bind(this));
}
}
if (parentRecord) {
//log(childResource.loaderId);
if (childResource.type === 'mapping') {
parentRecord.getMapping(childResource.loaderId).addExisiting(record);
//parentRecord[childResource.name].addExisiting(record);
}
else if (childResource.type === 'belongsTo') {
parentRecord.getBelongsTo(childResource.loaderId).addExisiting(record);
//parentRecord[childResource.name].addExisiting(record);
}
else {
// reference
//parentRecord.setReference(childResource.loaderId, record, true);
parentRecord[childResource.name] = record;
}
}
}.bind(this));
}.bind(this));
this._buildRelations(childResource);
}
}.bind(this));
}
}
// get all selected queries, add the correct filter to them
, _collectQueries: function(resource, queries) {
if (resource.hasChildren()) {
resource.children.forEach(function(childResource){
if (childResource.selected) queries.push(childResource);
this._collectQueries(childResource, queries);
}.bind(this));
}
return queries;
}
// get all selected queries, add the correct filter to them
, _collectQueries: function(resource, queries) {
if (resource.hasChildren()) {
resource.children.forEach(function(childResource){
if (childResource.selected) queries.push(childResource);
this._collectQueries(childResource, queries);
}.bind(this));
}
return queries;
}
// parse the the resource tree, check which queriies to execute
// traverse the tree, check if the children are selected, if yes:
// select all parents
, _prepareChildResources: function(resource) {
if (resource.hasChildren()) {
resource.children.forEach(function(childResource){
if (childResource.selected) this._selectParents(childResource);
if (childResource.hasRootFilter) this._filterParents(childResource);
if (childResource.filtered) this._filterByChildren(childResource, [], childResource.query.filter, childResource.query.from);
this._prepareChildResources(childResource);
}.bind(this));
}
}
// parse the the resource tree, check which queriies to execute
// traverse the tree, check if the children are selected, if yes:
// select all parents
, _prepareChildResources: function(resource) {
if (resource.hasChildren()) {
resource.children.forEach(function(childResource){
if (childResource.selected) this._selectParents(childResource);
if (childResource.hasRootFilter) this._filterParents(childResource);
if (childResource.filtered) this._filterByChildren(childResource, [], childResource.query.filter, childResource.query.from);
this._prepareChildResources(childResource);
}.bind(this));
}
}
, _filterByChildren: function(resource, joins, filter, resourceName) {
if (!resource.selected) {
if (!resource.childrenFiltered) {
joins = joins.concat(resource.joins.map(function(joinStatement){
return joinStatement.reverseFormat();
}));
resource.childrenFiltered = true;
}
, _filterByChildren: function(resource, joins, filter, resourceName) {
if (!resource.selected) {
if (!resource.childrenFiltered) {
joins = joins.concat(resource.joins.map(function(joinStatement){
return joinStatement.reverseFormat();
}));
if (resource.parentResource) this._filterByChildren(resource.parentResource, joins, filter, resourceName);
}
else if(resource.query.filter !== filter) {
// apply filter & joins
resource.query.filter[resourceName] = filter;
resource.query.join = resource.query.join.concat(joins);
}
}
resource.childrenFiltered = true;
}
if (resource.parentResource) this._filterByChildren(resource.parentResource, joins, filter, resourceName);
}
else if(resource.query.filter !== filter) {
// apply filter & joins
resource.query.filter[resourceName] = filter;
resource.query.join = resource.query.join.concat(joins);
}
}
// recursive select
, _selectParents: function(resource) {
resource.select();
if (resource.parentResource) {
resource.parentResource.loadRelatingSet(resource.name);
this._selectParents(resource.parentResource);
}
}
// recursive select
, _selectParents: function(resource) {
resource.select();
if (resource.parentResource) {
resource.parentResource.loadRelatingSet(resource.name);
this._selectParents(resource.parentResource);
}
}
, _filterParents: function(resource, originalResource) {
if (resource.parentResource) this._filterParents(resource.parentResource);
resource.filter();
}
, _filterParents: function(resource, originalResource) {
if (resource.parentResource) this._filterParents(resource.parentResource);
resource.filter();
}
, _executeQuery: function(mode, query, callback){
this.getDatabase().executeQuery(mode, query, callback);
}
, _executeQuery: function(mode, query, callback){
this.getDatabase().executeQuery(mode, query, callback);
}
, _makeSet: function(rows, resource) {
var records = new Set({
primaryKeys: resource.primaryKeys
, name: resource.name
});
(rows || []).forEach(function(row){
Object.defineProperty(row, '_isFromDB', {value:true});
records.push(new resource.Model(row, resource.relatingSets));
}.bind(this));
, _makeSet: function(rows, resource) {
var records = new Set({
primaryKeys: resource.primaryKeys
, name: resource.name
});
return records;
}
(rows || []).forEach(function(row){
Object.defineProperty(row, '_isFromDB', {value:true});
records.push(new resource.Model(row, resource.relatingSets));
}.bind(this));
return records;
}
});
});
}();
!function(){
var Class = require('ee-class')
, type = require('ee-types')
, async = require('ee-async')
, arg = require('ee-arguments')
, log = require('ee-log');
var Class = require('ee-class')
, type = require('ee-types')
, async = require('ee-async')
, Arguments = require('ee-arguments')
, log = require('ee-log');
var proto = Array.prototype;
var proto = Array.prototype;
var RelatingPrototype = Object.create(proto, {
module.exports = new Class({
inherits: Array
// queries beeing executed
_workers: {
get: function(){ return this._workerCount;}
, set: function(value) {
this._workerCount = value;
if (value === 0) this.emit('idle');
}
}
, init: function(options) {
Class.define(this, '_orm' , Class(options.orm));
Class.define(this, '_definition' , Class(options.definition));
Class.define(this, '_relatesTo' , Class(options.related));
Class.define(this, '_column' , Class(options.column));
Class.define(this, '_database' , Class(options.database));
Class.define(this, '_workerCount' , Class(0).Writable());
Class.define(this, '_originalRecords' , Class([]).Writable());
Class.define(this, '_error' , Class(null).Writable());
Class.define(this, 'isMapping' , Class(!!options.isMapping));
}
// save changes on the
, save: {value: function() {
var callback = arg(arguments, 'function', function(){})
, noReload = arg(arguments, 'boolean', false)
, transaction = arg(arguments, 'object');
if (transaction) {
this._save(transaction, noReload, callback);
}
else {
transaction = this.getDatabase().createTransaction();
, _workers: {
get: function(){ return this._workerCount;}
, set: function(value) {
this._workerCount = value;
if (value === 0) this.emit('idle');
}
}
this._save(transaction, noReload, function(err){
if (err) {
transaction.rollback(function(transactionErr){
if (transactionErr) callback(transactionErr);
else callback(err);
}.bind(this));
}
else {
transaction.commit(function(err){
if (err) callback(err);
else callback(null, this);
}.bind(this));
}
}.bind(this));
}
return this;
}}
// save changes on the
, save: {value: function() {
var args = new Arguments(arguments)
, callback = args.getFunction(function(){})
, noReload = args.getBoolean(false)
, transaction = args.getObject();
if (transaction) {
this._save(transaction, noReload, callback);
}
else {
transaction = this.getDatabase().createTransaction();
// save changes on the
, _save: {value: function(transaction, noReload, callback) {
// wait until the relatingset is idle, then store all records and create relations
if (this._error) callback(this._error);
else {
if (this._workers) {
this.once('idle', function(){
this._executeSave(transaction, noReload, callback);
}.bind(this));
}
else {
this._executeSave(transaction, noReload, callback);
}
}
}}
this._save(transaction, noReload, function(err){
if (err) {
transaction.rollback(function(transactionErr){
if (transactionErr) callback(transactionErr);
else callback(err);
}.bind(this));
}
else {
transaction.commit(function(err){
if (err) callback(err);
else callback(null, this);
}.bind(this));
}
}.bind(this));
}
return this;
}}
, _executeSave: {value: function(transaction, noReload, callback) {
// check for query items
var queries = [];
for (var i = 0; i < this.length; i++) {
if (this[i].isQuery) {
queries.push(proto.splice.call(this, i, 1)[0]);
i--;
}
}
// save changes on the
, _save: {value: function(transaction, noReload, callback) {
// wait until the relatingset is idle, then store all records and create relations
if (this._error) callback(this._error);
else {
if (this._workers) {
this.once('idle', function(){
this._executeSave(transaction, noReload, callback);
}.bind(this));
}
else {
this._executeSave(transaction, noReload, callback);
}
}
}}
// execute all queries
async.each(queries, function(query, next){
this._addQueryItem(item, next);
}.bind(this), function(err){
if (err) callback(err);
else {
// save all unsaved items
async.each(this, function(item, next){
if (item.isQuery) {
this._addQueryItem(item, next);
}
else if (!item.isFromDatabase() || !item.isSaved()) {
// we have to save this item before we can proceed
// if we're a belongs to set we need to set our id on the item
if (!this.isMapping) {
item[this._definition.targetColumn] = this._relatesTo[this._column.name];
item[this._relatesTo.getDefinition().name] = this._relatesTo;
}
item.save(transaction, noReload, next);
}
else next();
}.bind(this), function(err){
if (err) callback(err);
else {
this._getChangedRecords(function(err, added, removed){
if (err) callback(err);
else {
// create / remove relational records
async.wait(function(done){
this._deleteRelationRecords(removed, transaction, noReload, done);
}.bind(this), function(done){
this._createRelationRecords(added, transaction, noReload, done);
}.bind(this), callback);
}
}.bind(this));
}
}.bind(this));
}
}.bind(this));
}}
, _executeSave: {value: function(transaction, noReload, callback) {
// check for query items
var queries = [];
for (var i = 0; i < this.length; i++) {
if (this[i].isQuery) { log.warn('query');
queries.push(proto.splice.call(this, i, 1)[0]);
i--;
}
}
// execute all queries
async.each(queries, function(query, next){
this._addQueryItem(item, next);
}.bind(this), function(err){
if (err) callback(err);
else {
// save all unsaved items
async.each(this, function(item, next){
if (item.isQuery) {
this._addQueryItem(item, next);
}
else if (!item.isFromDatabase() || !item.isSaved()) {
// we have to save this item before we can proceed
// if we're a belongs to set we need to set our id on the item
//log.warn('unsaved item');
if (!this.isMapping) {
//log(this._definition.targetColumn, this._relatesTo[this._column.name]);
item[this._definition.targetColumn] = this._relatesTo[this._column.name];
//item[this._relatesTo.getDefinition().name] = this._relatesTo;
}
item.save(transaction, noReload, next);
}
else next();
}.bind(this), function(err){
if (err) callback(err);
else {
this._getChangedRecords(function(err, added, removed){
if (err) callback(err);
else {
// create / remove relational records
async.wait(function(done){
this._deleteRelationRecords(removed, transaction, noReload, done);
}.bind(this), function(done){
this._createRelationRecords(added, transaction, noReload, done);
}.bind(this), callback);
}
}.bind(this));
}
}.bind(this));
}
}.bind(this));
}}
, _getChangedRecords: {value: function(callback){
var removed = []
, added = []
, originalMap = this._createMap(this._originalRecords)
, currentMap = this._createMap(this);
// adde items
Object.keys(currentMap).forEach(function(newItemKey){
if (!originalMap[newItemKey]) {
// new item
added.push(currentMap[newItemKey]);
}
}.bind(this));
// removed items
Object.keys(originalMap).forEach(function(oldItemKey){
if (!currentMap[oldItemKey]) {
// new item
removed.Push(originalMap[oldItemKey]);
}
}.bind(this));
, _getChangedRecords: {value: function(callback){
var removed = []
, added = []
, originalMap = this._createMap(this._originalRecords)
, currentMap = this._createMap(this);
callback(null, added, removed);
}}
// adde items
Object.keys(currentMap).forEach(function(newItemKey){
if (!originalMap[newItemKey]) {
// new item
added.push(currentMap[newItemKey]);
}
}.bind(this));
// removed items
Object.keys(originalMap).forEach(function(oldItemKey){
if (!currentMap[oldItemKey]) {
// new item
removed.Push(originalMap[oldItemKey]);
}
}.bind(this));
, _createRelationRecords: { value: function(addedRecords, transaction, noReload, callback) {
async.each(addedRecords, function(record, next){
if (this.isMapping) {
var values = {};
callback(null, added, removed);
}}
values[this._definition.via.fk] = this._relatesTo[this._column.name];
values[this._definition.via.otherFk] = record[this._definition.column.name];
new this._orm[this._definition.model.getDatabaseName()][this._definition.via.model.name](values).save(transaction, next);
}
else next();
}.bind(this), callback);
}}
, _createRelationRecords: { value: function(addedRecords, transaction, noReload, callback) {
async.each(addedRecords, function(record, next){
if (this.isMapping) {
var values = {};
values[this._definition.via.fk] = this._relatesTo[this._column.name];
values[this._definition.via.otherFk] = record[this._definition.column.name];
, _deleteRelationRecords: {value: function(removedRecords, transaction, noReload, callback) {
async.each(removedRecords, function(record, next){
if (this.isMapping) {
var values = {};
new this._orm[this._definition.model.getDatabaseName()][this._definition.via.model.name](values).save(transaction, next);
}
else next();
}.bind(this), callback);
}}
values[this._definition.via.fk] = this._relatesTo[this._column.name];
values[this._definition.via.otherFk] = record[this._definition.column.name];
transaction[this._definition.via.model.name](values).delete(next)
}
else next();
}.bind(this), callback);
}}
, _deleteRelationRecords: {value: function(removedRecords, transaction, noReload, callback) {
async.each(removedRecords, function(record, next){
if (this.isMapping) {
var values = {};
values[this._definition.via.fk] = this._relatesTo[this._column.name];
values[this._definition.via.otherFk] = record[this._definition.column.name];
transaction[this._definition.via.model.name](values).delete(next)
}
else next();
}.bind(this), callback);
}}
, _createMap: {value: function(items){
var map = {}
, primaryKeys = this._relatesTo.getDefinition().primaryKeys;
items.forEach(function(item){
var compositeKey = '';
primaryKeys.forEach(function(key){
compositeKey += '|'+item[key];
}.bind(this), '');
, _createMap: {value: function(items){
var map = {}
, primaryKeys = this._relatesTo.getDefinition().primaryKeys;
map[compositeKey] = item;
});
items.forEach(function(item){
var compositeKey = '';
return map;
}}
primaryKeys.forEach(function(key){
compositeKey += '|'+item[key];
}.bind(this), '');
map[compositeKey] = item;
});
return map;
}}
// reload all records
, reload: {value: function(callback, transaction) {
// check if there are unsaved values on the relation, then reload all of them
var doRelaod = function(){
this._reload(callback, (transaction || this._relatesTo._getDatabase()));
}.bind(this);
// wait until we become idle
if (this._workers) this.on('idle', doRelaod);
else doRelaod();
}}
// reload all records
, reload: {value: function(callback, transaction) {
// check if there are unsaved values on the relation, then reload all of them
var doRelaod = function(){
this._reload(callback, (transaction || this._relatesTo._getDatabase()));
}.bind(this);
, _reload: {value: function(callback, transaction) {
// check if there are unsaved values on the relation, then reload all of them
this._getChangedRecords(function(err, added, removed){
if (err) callback(err);
else {
log.error('reloading for relating sets needs to be implemented.');
return callback();
if (added.length || removed.length) callback(new Error('Cannot reload relation «'+this._definition.name+'» on model «'+this._relatesTo.getEntityName()+'», there are unsaved changes!'));
else {
if (this.isMapping) {
// create a map of existing ids
var query = {
// wait until we become idle
if (this._workers) this.on('idle', doRelaod);
else doRelaod();
}}
};
}
else {
}
}
}
}.bind(this));
}}
, _reload: {value: function(callback, transaction) {
// check if there are unsaved values on the relation, then reload all of them
this._getChangedRecords(function(err, added, removed){
if (err) callback(err);
else {
//log.error('reloading for relating sets needs to be implemented.');
return callback();
if (added.length || removed.length) callback(new Error('Cannot reload relation «'+this._definition.name+'» on model «'+this._relatesTo.getEntityName()+'», there are unsaved changes!'));
else {
if (this.isMapping) {
// create a map of existing ids
var query = {
};
}
else {
}
}
}
}.bind(this));
}}
/*
* the push() method accepts eiteher a quer or a saved or not saved
* model of the target entity. if it gets a query it execute it immediatelly
* in order to get the records which it shoudl link. it wil lpush all models
* added or found via query to the _addedRecords array which will be stored /
* linked when the save method gets calleds
*
* @param <Object> item: query or model
* @param <Function> callback
*/
, push: { value: function push (item, callback) {
if (type.object(item)) {
// check if the item is a model or a query
if (item.isQuery) {
// execute the query, add it to
this._addQueryItem(item, callback);
}
else {
if (this._isCorrectType(item)){
proto.push.call(this, item);
if (callback) callback(null, this.length);
}
else {
this._error = new Error('Attempt to add models of type «'+item.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
if (callback) callback(this._error);
}
}
}
else {
this._error = new Error('Push was called with an invalid value: «'+type(item)+'»')
if (callback) callback(this._error);
}
return this.length;
}}
/*
* the push() method accepts eiteher a quer or a saved or not saved
* model of the target entity. if it gets a query it execute it immediatelly
* in order to get the records which it shoudl link. it wil lpush all models
* added or found via query to the _addedRecords array which will be stored /
* linked when the save method gets calleds
*
* @param <Object> item: query or model
* @param <Function> callback
*/
, push: { value: function push (item, callback) {
if (type.object(item)) {
// check if the item is a model or a query
if (item.isQuery) {
// execute the query, add it to
this._addQueryItem(item, callback);
}
else {
if (this._isCorrectType(item)){
proto.push.call(this, item);
if (callback) callback(null, this.length);
}
else {
this._error = new Error('Attempt to add models of type «'+item.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
if (callback) callback(this._error);
}
}
}
else {
this._error = new Error('Push was called with an invalid value: «'+type(item)+'»')
if (callback) callback(this._error);
}
return this.length;
}}
, splice: {value:function(index, howMany) {
var newItems = proto.slice.call(arguments, 2)
, removedItems = proto.splice.call(this, index, howMany)
, callback = arg(arguments, 'function');
// remove the callback if present
if (callback) newItems.pop();
if (newItems.length) {
asyn.each(newItems, function(item, next){
if (type.object(item)) {
if (item.isQuery) this._addQueryItem(item, next, index);
else {
if (this._isCorrectType(item)) {
proto.splice.call(this, index, 0, item);
next();
}
else {
this._error = new Error('Attempt to add models of type «'+item.getName()+'» to a relatingset of type «'+this._definition.name+'»!');
next(this._error);
}
}
}
else {
this._error = new Error('Push was called with an invalid value: «'+type(item)+'»')
next(this._error);
}
}.bind(this)
, splice: {value:function(index, howMany) {
var newItems = proto.slice.call(arguments, 2)
, removedItems = proto.splice.call(this, index, howMany)
, callback = arg(arguments, 'function');
, function(err){
if (err) {
this._error = err;
callback(err, removedItems);
}
else callback(null, removedItems);
}.bind(this));
}
else {
if (callback) callback(null, removedItems);
}
// remove the callback if present
if (callback) newItems.pop();
return removedItems;
}}
if (newItems.length) {
asyn.each(newItems, function(item, next){
if (type.object(item)) {
if (item.isQuery) this._addQueryItem(item, next, index);
else {
if (this._isCorrectType(item)) {
proto.splice.call(this, index, 0, item);
next();
}
else {
this._error = new Error('Attempt to add models of type «'+item.getName()+'» to a relatingset of type «'+this._definition.name+'»!');
next(this._error);
}
}
}
else {
this._error = new Error('Push was called with an invalid value: «'+type(item)+'»')
next(this._error);
}
}.bind(this)
, function(err){
if (err) {
this._error = err;
callback(err, removedItems);
}
else callback(null, removedItems);
}.bind(this));
}
else {
if (callback) callback(null, removedItems);
}
return removedItems;
}}
, unshift: {value: function(item, callback){
if (type.object(item)) {
// check if the item is a model or a query
if (item.isQuery) {
// execute the query, add it to
this._addQueryItem(item, callback, null, true);
}
else {
if (this._isCorrectType(item)){
proto.unshift.call(this, item);
if (callback) callback(null, this.length);
}
else {
this._error = new Error('Attempt to add models of type «'+item.getName()+'» to a relatingset of type «'+this._definition.name+'»!');
if (callback) callback(this._error);
}
}
}
else {
this._error = new Error('Unshift was called with an invalid value: «'+type(item)+'»')
if (callback) callback(this._error);
}
return this.length;
}}
, unshift: {value: function(item, callback){
if (type.object(item)) {
// check if the item is a model or a query
if (item.isQuery) {
// execute the query, add it to
this._addQueryItem(item, callback, null, true);
}
else {
if (this._isCorrectType(item)){
proto.unshift.call(this, item);
if (callback) callback(null, this.length);
}
else {
this._error = new Error('Attempt to add models of type «'+item.getName()+'» to a relatingset of type «'+this._definition.name+'»!');
if (callback) callback(this._error);
}
}
}
else {
this._error = new Error('Unshift was called with an invalid value: «'+type(item)+'»')
if (callback) callback(this._error);
}
return this.length;
}}
, _addQueryItem: {value: function(item, callback, index, unshift){
this._workers++;
item.find(function(err, records){
var err;
this._workers--;
, _addQueryItem: {value: function(item, callback, index, unshift){
this._workers++;
if (err) {
if (callback) callback(err);
}
else {
records.forEach(function(model){
if (this._isCorrectType(model)){
if (type.number(index)) proto.splice.call(this, index, 0, model);
else if (unshift) proto.unshift.call(this, model);
else proto.push.call(this, model);
}
else {
err = this._error = new Error('Attempt to add models of type «'+model.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
}
}.bind(this));
item.find(function(err, records){
var err;
if (callback) callback(err, this.length);
}
}.bind(this));
}}
this._workers--;
if (err) {
if (callback) callback(err);
}
else {
records.forEach(function(model){
if (this._isCorrectType(model)){
if (type.number(index)) proto.splice.call(this, index, 0, model);
else if (unshift) proto.unshift.call(this, model);
else proto.push.call(this, model);
}
else {
err = this._error = new Error('Attempt to add models of type «'+model.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
}
}.bind(this));
if (callback) callback(err, this.length);
}
}.bind(this));
}}
// internal only method for adding records which were already mappend to the collection
, addExisiting: { value: function(item){
this._originalRecords.push(item);
proto.push.call(this, item);
}}
// check if a model is typeof this
, _isCorrectType: { value: function(model){
return model.getEntityName() === this._definition.model.name;
}}
// internal only method for adding records which were already mappend to the collection
, addExisiting: { value: function(item){
this._originalRecords.push(item);
proto.push.call(this, item);
}}
// check if a model is typeof this
, _isCorrectType: { value: function(model){
return model.getEntityName() === this._definition.model.name;
}}
, pop: { value: function pop () {
pop.parent();
}}
, pop: { value: function pop () {
pop.parent();
}}
, shift: { value: function shift () {
shift.parent();
}}
, unshift: { value: function unshift () {
unshift.parent();
}}
, shift: { value: function shift () {
shift.parent();
}}
, unshift: { value: function unshift () {
unshift.parent();
}}
// inheriting from the array type, have to implement event by myself
, _events: {value: {}, writable: true}
// on
, on: {value: function(evt, listener){
if (!this._events[evt]) this._events[evt] = [];
this._events[evt].push({fn: listener});
}}
// once
, once: {value: function(evt, listener){
if (!this._events[evt]) this._events[evt] = [];
this._events[evt].push({fn: listener, once: true});
}}
// inheriting from the array type, have to implement event by myself
, _events: {value: {}, writable: true}
// emit
, emit: {value: function(evt){
var rm = [];
// on
, on: {value: function(evt, listener){
if (!this._events[evt]) this._events[evt] = [];
this._events[evt].push({fn: listener});
}}
if (this._events[evt]) {
this._events[evt].forEach(function(listener){
listener.fn.apply(null, Array.prototype.slice.call(arguments, 1));
if (listener.once) rm.push(listener);
});
// once
, once: {value: function(evt, listener){
if (!this._events[evt]) this._events[evt] = [];
this._events[evt].push({fn: listener, once: true});
}}
rm.forEach(function(listener){
this.off(evt, listener);
});
}
}}
// emit
, emit: {value: function(evt){
var rm = [];
// off
, off: {value: function(evt, listener){
var index;
if (this._events[evt]) {
this._events[evt].forEach(function(listener){
listener.fn.apply(null, Array.prototype.slice.call(arguments, 1));
if (listener.once) rm.push(listener);
});
if (evt === undefined) this._events = {};
else if (evt) {
if (listener === undefined) delete this._events[evt];
else if(this._events[evt]) {
index = this._events[evt].indexOf(listener);
if (index >= 0) this._events[evt].splice(index, 1);
}
}
}}
rm.forEach(function(listener){
this.off(evt, listener);
});
}
}}
, toJSON: {value: function() {
return Array.prototype.slice.call(this);
}}
// off
, off: {value: function(evt, listener){
var index;
});
if (evt === undefined) this._events = {};
else if (evt) {
if (listener === undefined) delete this._events[evt];
else if(this._events[evt]) {
index = this._events[evt].indexOf(listener);
if (index >= 0) this._events[evt].splice(index, 1);
}
}
}}
, dir: {value: function(returnResult) {
var result = [];
this.forEach(function(item){
result.push(item.dir(true));
});
if (returnResult) return result;
else log(result);
}}
module.exports = function(options){
return Object.create(RelatingPrototype, {
_orm: {
value: options.orm
}
, _definition: {
value: options.definition
}
, _relatesTo: {
value: options.related
}
, _column: {
value: options.column
}
, _database: {
value: options.database
}
, _workerCount: {
value: 0
}
// the records which were on the collection when it was initialized
, _originalRecords: {
value: []
}
// collect errors when selecting records to add to this set
, _error: {
value: null
}
, isMapping: {
value: !!options.isMapping
}
});
};
, toJSON: {value: function() {
return Array.prototype.slice.call(this);
}}
});
}();
!function(){
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, ORM;
var Class = require('ee-class')
, EventEmitter = require('ee-event-emitter')
, log = require('ee-log')
, ORM;

@@ -11,145 +11,158 @@

module.exports = new Class({
module.exports = new Class({
// id counter for the root resource
_id: 0
// child resources
children: []
// flags if this resource was already joined into the root resource
, joined: false
// id counter for the root resource
, _id: 0
// select was executeed?
, selectExecuted: false
// flags if this resource was already joined into the root resource
, joined: false
// select was executeed?
, selectExecuted: false
, filtered: {get: function() {
return !!Object.keys(this.query.filter);
}}
// which relating sets must be laoded
, relatingSets: {}
, init: function(options) {
this.query = options.query;
this.filters = options.filters;
this.joins = options.joins || [];
this.type = options.type;
this.name = options.name;
this.defaultSelect = options.defaultSelect || [];
this.selected = options.selected;
//this.filtered = options.filtered;
this.parentResource = options.parentResource;
this.Model = options.Model;
this.referencedParentColumn = options.referencedParentColumn;
this.referencedParentTable = options.referencedParentTable;
this.primaryKeys = options.primaryKeys;
this.rootResource = options.rootResource;
this.rootFiltered = options.rootFiltered; // flags if this filter was added to the root query
this.loaderId = options.loaderId;
Class.define(this, 'children', Class([]).Enumerable())
Class.define(this, 'relatingSets', Class({}).Enumerable())
, get filtered() {
return !!Object.keys(this.query.filter);
}
this.hasRootFilter = this.filters && !!Object.keys(this.filters).length;
, init: function(options) {
this.query = options.query;
this.filters = options.filters;
this.joins = options.joins || [];
this.type = options.type;
this.name = options.name;
this.defaultSelect = options.defaultSelect || [];
this.selected = options.selected;
//this.filtered = options.filtered;
this.parentResource = options.parentResource;
this.Model = options.Model;
this.referencedParentColumn = options.referencedParentColumn;
this.referencedParentTable = options.referencedParentTable;
this.primaryKeys = options.primaryKeys;
this.rootResource = options.rootResource;
this.rootFiltered = options.rootFiltered; // flags if this filter was added to the root query
this.loaderId = options.loaderId;
if (this.primaryKeys) this.defaultSelect = this.defaultSelect.concat(this.primaryKeys);
this.hasRootFilter = this.filters && !!Object.keys(this.filters).length;
if (!ORM) ORM = require('./ORM');
if (this.primaryKeys) this.defaultSelect = this.defaultSelect.concat(this.primaryKeys);
// get an unique id
if (this.parentResource) this.id = this.getUniqueId();
}
if (!ORM) ORM = require('./ORM');
// get an unique id
if (this.parentResource) this.id = this.getUniqueId();
}
// get an unique id for this query ( get it fromt the root resource )
, getUniqueId: function() {
return this.rootResource.getId();
}
// get an unique id for this query ( get it fromt the root resource )
, getUniqueId: function() {
return this.rootResource.getId();
}
// only called on the root resource
, getId: function() {
return ++this._id;
}
// only called on the root resource
, getId: function() {
return ++this._id;
}
// check which relating sets to load
, loadRelatingSet: function(name) {
this.relatingSets[name] = true;
}
// check which relating sets to load
, loadRelatingSet: function(name) {
this.relatingSets[name] = true;
}
, select: function() {
if (!this.selectExecuted) {
this.selectExecuted = true;
this.selected = true;
// add parent reference to selects
if (this.referencedParentColumn) {
this.defaultSelect.push(ORM.alias('____id____', this.referencedParentTable, this.referencedParentColumn));
, select: function() {
if (!this.selectExecuted) {
this.selectExecuted = true;
this.selected = true;
this.parentResource.selectReferencedColumn(this.referencedParentColumn);
}
// add parent reference to selects
if (this.referencedParentColumn) {
this.defaultSelect.push(ORM.alias('____id____', this.referencedParentTable, this.referencedParentColumn));
// add additional selected fields to query
this.query.addSeleted(this.defaultSelect);
this.parentResource.selectReferencedColumn(this.referencedParentColumn);
}
// create join statements
this.query.formatJoins();
}
}
// add additional selected fields to query
this.query.addSeleted(this.defaultSelect);
// create join statements
this.query.formatJoins();
}
}
// add my joins to the root (at the end)
, filter: function() {
var filter;
if (!this.rootFiltered) { // filter not yet added to root query?
this.rootFiltered = true;
// add my joins to the root (at the end)
, filter: function() {
var filter;
// create join with alias, add it to the root resource( end )
this.joins.reverse().forEach(function(joinStatement, index){
this.rootResource.query.join.push(joinStatement.reverseFormat(this.id, (index > 0 ? this.id: this.parentResource.id)));
}.bind(this));
if (!this.rootFiltered) { // filter not yet added to root query?
this.rootFiltered = true;
// create join with alias, add it to the root resource( end )
this.joins.reverse().forEach(function(joinStatement, index){
this.rootResource.query.join.push(joinStatement.reverseFormat(this.id, (index > 0 ? this.id: this.parentResource.id)));
}.bind(this));
// filters
if (this.filters && Object.keys(this.filters).length){
filter = this.rootResource.query.filter[this.query.from+this.id] = {};
Object.keys(this.filters).forEach(function(key){
filter[key] = this.filters[key];
}.bind(this));
}
}
}
// filters
if (this.filters && Object.keys(this.filters).length){
filter = this.rootResource.query.filter[this.query.from+this.id] = {};
Object.keys(this.filters).forEach(function(key){
filter[key] = this.filters[key];
}.bind(this));
}
}
}
, selectReferencedColumn: function(columnName) {
this.query.select.push(columnName);
}
, selectReferencedColumn: function(columnName) {
this.query.select.push(columnName);
}
, applyFilter: function(resource) {
var q = resource.query;
/*if (!q.filter) q.filter = {};
if (!q.filter[this.name]) q.filter[this.name] = {};
q.filter[this.name][resource.referencedParentColumn] = ORM.in(this.set.getColumnValues(resource.referencedParentColumn));*/
, applyFilter: function(resource) {
var q = resource.query;
/*this.primaryKeys.forEach(function(pk){
q.group.push({
table : this.name
, column : pk
});
}.bind(this));*/
/*
q.group.push({
table : this.name
, column : resource.referencedParentColumn
});*/
}
if (!q.filter) q.filter = {};
if (!q.filter[this.name]) q.filter[this.name] = {};
q.filter[this.name][resource.referencedParentColumn] = ORM.in(this.set.getColumnValues(resource.referencedParentColumn));
q.group.push({
table : this.name
, column : resource.referencedParentColumn
});
}
, applyGroup: function(resource) { //log.highlight(this.name, resource.name);
this.primaryKeys.forEach(function(pk){ //log.info(this.name, pk);
resource.query.group.push({
table : this.name
, column : pk
});
}.bind(this));
}
, hasChildren: function() {
return !!this.children.length;
}
});
, hasChildren: function() {
return !!this.children.length;
}
});
}();
!function(){
var Class = require('ee-class')
, type = require('ee-types')
, log = require('ee-log');
var Class = require('ee-class')
, type = require('ee-types')
, log = require('ee-log');
module.exports = new Class({
inherits: Array
module.exports = new Class({
inherits: Array
, init: function(options){
Object.defineProperty(this, '_primaryKeys', {value: options.primaryKeys});
Object.defineProperty(this, '_name', {value: options.name});
Object.defineProperty(this, '_maps', {value: {_primary: {}}});
}
, init: function(options){
Object.defineProperty(this, '_primaryKeys', {value: options.primaryKeys});
Object.defineProperty(this, '_name', {value: options.name});
Object.defineProperty(this, '_maps', {value: {_primary: {}}});
}
, push: function(item) {
// we need unique items by primary key
var key = '';
, push: function(item) {
// we need unique items by primary key
var key = '';
this._primaryKeys.forEach(function(keyName){
key += item[keyName];
}.bind(this));
this._primaryKeys.forEach(function(keyName){
key += item[keyName];
}.bind(this));
if (this._maps._primary[key]){
// this value was added bvefore
this._maps._primary[key]._mappingIds = this._maps._primary[key]._mappingIds.concat(item._mappingIds);
return this.length;
}
else {
this._maps._primary[key] = item;
return Array.prototype.push.call(this, item);
}
}
if (this._maps._primary[key]){
// this value was added before
this._maps._primary[key]._mappingIds = this._maps._primary[key]._mappingIds.concat(item._mappingIds);
return this.length;
}
else {
this._maps._primary[key] = item;
return Array.prototype.push.call(this, item);
}
}
, first: function(){
return this[0];
}
, first: function(){
return this[0];
}
, last: function(){
return this.length ? this[this.length-1] : undefined;
}
, last: function(){
return this.length ? this[this.length-1] : undefined;
}
, getColumnValues: function(column){
column = column || 'id';
, getColumnValues: function(column){
column = column || 'id';
return this.map(function(row){
return row[column];
});
}
return this.map(function(row){
return row[column];
});
}
, getIds: function(){
return this.map(function(item){ return item.id;});
}
, getIds: function(){
return this.map(function(item){ return item.id;});
}
, getByColumnValue: function(column, value){
if (!this._maps[column]) this.createMap(column);
return this._maps[column] ? this._maps[column][value] : undefined;
}
, getByColumnValue: function(column, value){
if (!this._maps[column]) this.createMap(column);
return this._maps[column] ? this._maps[column][value] : undefined;
}
, createMap: function(column){
if (!this._maps[column]){
this._maps[column] = {};
, createMap: function(column){
if (!this._maps[column]){
this._maps[column] = {};
this.forEach(function(item){
this._maps[column][item[column]] = item;
}.bind(this));
}
}
this.forEach(function(item){
this._maps[column][item[column]] = item;
}.bind(this));
}
}
, toJSON: function() {
return Array.prototype.slice.call(this);
}
});
, dir: function(returnResult) {
var result = [];
this.forEach(function(item){
result.push(item.dir(true));
});
if (returnResult) return result;
else log(result);
}
, toJSON: function() {
return Array.prototype.slice.call(this);
}
});
}();
!function(){
var Class = require('ee-class')
, log = require('ee-log')
, type = require('ee-types');
var Class = require('ee-class')
, log = require('ee-log')
, type = require('ee-types');

@@ -10,59 +10,59 @@

module.exports = new Class({
module.exports = new Class({
alias: function(){
var len = arguments.length
, tableName = len === 3 ? arguments[1] : null
, columnName = arguments[len === 3 ? 2 : 1]
, alias = arguments[0];
alias: function(){
var len = arguments.length
, tableName = len === 3 ? arguments[1] : null
, columnName = arguments[len === 3 ? 2 : 1]
, alias = arguments[0];
return function(){
return {
table : tableName
, column : columnName
, alias : alias
}
};
}
return function(){
return {
table : tableName
, column : columnName
, alias : alias
}
};
}
, in: function(values) {
return function(){
return {
fn: 'in'
, values: values
};
};
}
, in: function(values) {
return function(){
return {
fn: 'in'
, values: values
};
};
}
, notIn: function(values) {
return function(){
return {
fn: 'notIn'
, values: values
};
};
}
, notIn: function(values) {
return function(){
return {
fn: 'notIn'
, values: values
};
};
}
, notNull: function() {
return function(){
return {
fn: 'notNull'
};
};
}
, notNull: function() {
return function(){
return {
fn: 'notNull'
};
};
}
, gt: function(value) {
return function(){
return {
operator: '>'
, value: value
}
}
}
});
, gt: function(value) {
return function(){
return {
operator: '>'
, value: value
}
}
}
});

@@ -72,3 +72,3 @@

}();
!function(){
var Class = require('ee-class')
, log = require('ee-log')
, type = require('ee-types');
var Class = require('ee-class')
, log = require('ee-log')
, type = require('ee-types');

@@ -11,43 +11,43 @@

module.exports = function(database) {
return Object.create(database, {
module.exports = function(database) {
return Object.create(database, {
commit: {enumerable: true, value: function(callback){
if (this._transaction) this._transaction.commit(callback);
else callback(new Error('Cannot commit! The transaction has already eneded.'));
this._endTransaction();
}}
commit: {enumerable: true, value: function(callback){
if (this._transaction) this._transaction.commit(callback);
else callback(new Error('Cannot commit! The transaction has already eneded.'));
this._endTransaction();
}}
, rollback: {enumerable: true, value: function(callback){
if (this._transaction) this._transaction.rollback(callback);
else callback(new Error('Cannot rollback! The transaction has already eneded.'));
this._endTransaction();
}}
, rollback: {enumerable: true, value: function(callback){
if (this._transaction) this._transaction.rollback(callback);
else callback(new Error('Cannot rollback! The transaction has already eneded.'));
this._endTransaction();
}}
, executeQuery: {enumerable: true, value: function(mode, query, callback){
if (this._transaction) this._transaction.query(mode, query, callback);
else {
this._database.getConnection(false, function(err, connection){
if (err) callback(err);
else {
this._transaction = connection;
this._transaction.startTransaction();
this._transaction.on('end', this._endTransaction.bind(this));
, executeQuery: {enumerable: true, value: function(mode, query, callback){
if (this._transaction) this._transaction.query(mode, query, callback);
else {
this._database.getConnection(false, function(err, connection){
if (err) callback(err);
else {
this._transaction = connection;
this._transaction.startTransaction();
this._transaction.on('end', this._endTransaction.bind(this));
this.executeQuery(mode, query, callback);
}
}.bind(this));
}
}}
this.executeQuery(mode, query, callback);
}
}.bind(this));
}
}}
, _endTransaction: {value: function(){
this._transaction = null;
}}
, _endTransaction: {value: function(){
this._transaction = null;
}}
, _transaction: {value: null, writable:true, configurable: true}
});
}
, _transaction: {value: null, writable:true, configurable: true}
});
}
}();
{
"name" : "ee-orm"
, "description" : "An easy to use ORM for node.js. Supports advanced eager loading, complex queries, joins, transactions, complex database clusters & connection pooling."
, "version" : "0.2.11"
, "version" : "0.3.0"
, "homepage" : "https://github.com/eventEmitter/ee-orm"

@@ -19,3 +19,3 @@ , "author" : "Michael van der Weg <michael@eventemitter.com> (http://eventemitter.com/)"

, "dependencies": {
"ee-class" : "0.4.x"
"ee-class" : "1.0.x"
, "ee-event-emitter" : "0.1.x"

@@ -25,3 +25,4 @@ , "ee-types" : "0.1.x"

, "ee-async" : "0.2.x"
, "ee-arguments" : "0.1.x"
, "ee-argv" : "0.1.x"
, "ee-arguments" : "1.0.x"
, "clone" : "0.1.x"

@@ -32,12 +33,11 @@ , "ee-db-cluster" : "0.1.x"

"mocha" : "1.18.x"
, "ee-travis" : "0.1.x"
, "ee-mysql-connection" : "0.1.x"
, "ee-postgres-connection" : "0.1.x"
, "ee-project" : "0.2.x"
, "ee-postgres-connection" : "0.2.x"
, "ee-project" : "0.2.x"
}
, "optionalDependencies": {}
, "keywords" : ["orm", "mysql", "postgres", "object relationale mapper", "eager loading", "connection pooling", "cluster"]
, "keywords" : ["orm", "mysql", "postgres", "object relational mapper", "eager loading", "connection pooling", "cluster"]
, "scripts": {
"test" : "./node_modules/mocha/bin/mocha --reporter spec"
"test" : "./node_modules/mocha/bin/mocha --reporter spec --bail"
}
}

@@ -63,3 +63,3 @@ # ee-orm

// get first event
log(events.first()); // {id:1, title: "prodigy", startdate: "2012-07-08 22:00:00", venues: [{id: 45, name: "via felsenau"}]};
events.first().dir() // {id:1, title: "prodigy", startdate: "2012-07-08 22:00:00", venues: [{id: 45, name: "via felsenau"}]};

@@ -78,7 +78,7 @@ // add new venue to the second event. the events list is an array with advanced functions

orm.eventdata.events(['id']).fetchVenues({id: 56}).find(function(err, events){
log(events); // [{id:1}, {id:2}]
events.dir(); // [{id:1}, {id:2}]
});
orm.eventdata.venues({id: 56}).events(['id']).find(function(err, venues){
log(venues); // [{id:56, events: [{id:1}, {id:2}]}]
venues.dir(); // [{id:56, events: [{id:1}, {id:2}]}]
});

@@ -85,0 +85,0 @@ }

@@ -15,18 +15,50 @@

log('orm loaded');
var role5
, arr = []
, start
, i = 10000;
var db = orm.ee_orm_test
, start;
while(i--) arr.push(1);
log(orm);
var cb = function(err, data){
if (err) log(err);
if (data) data.dir();
}
/*return new db.eventLocale({
description : 'some text'
, language : db.language({id:1})
, event : db.event({id:1})
}).save(log);*/
db.image.setMappingAccessorName('venue_image', 'venue');
db.image().describeMethods();
db.event({id:1}).getImage(['*']).getVenue(['*']).find(cb);
/*
orm.ee_orm_test.venue.setMappingAccessorName('venue_image', 'image');*/
//log(orm.eventbooster.resource().describeMethods());
/*new orm.eventbooster.resource({
/*var db = orm.ee_orm_test;
db.venue.setReferenceAccessorName('id_image', 'logo');
db.venue.setMappingAccessorName('venue_image', 'images');*/
/*
new db.venue({
name: 'Dachstock Reitschule'
, municipality: db.municipality({
name: 'Bern'
})
, id_image: 1
}).save(function(err, image){
log(err, image);
});
*/
/*
console.time("insert")
new orm.eventbooster.resource({
key: 'email.test.1'+Math.random()
, id_tenant: 0
, resourceLocale: new orm.eventbooster.resourceLocale({
id_language: 1
id_language: 5
, text: 'hi'

@@ -39,6 +71,6 @@ })

}).save(function(err, resource){ log(err);
});*/
orm.eventbooster.resource(['*']).getResourceLocale(['*']).find(log)
console.timeEnd("insert")
});
*/
//orm.eventbooster.resourceLocale(['*'], {id_resource:63}).getResource(['*']).find(log)
return;

@@ -45,0 +77,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc