sequelize
Advanced tools
Comparing version 1.7.0-beta.2 to 1.7.0-beta.5
@@ -6,8 +6,9 @@ var Utils = require("./../utils") | ||
module.exports = (function() { | ||
var BelongsTo = function(srcDAO, targetDAO, options) { | ||
this.associationType = 'BelongsTo' | ||
this.source = srcDAO | ||
this.target = targetDAO | ||
this.options = options | ||
this.isSelfAssociation = (this.source.tableName == this.target.tableName) | ||
var BelongsTo = function(source, target, options) { | ||
this.associationType = 'BelongsTo' | ||
this.source = source | ||
this.target = target | ||
this.options = options | ||
this.isSingleAssociation = true | ||
this.isSelfAssociation = (this.source.tableName == this.target.tableName) | ||
@@ -18,7 +19,16 @@ if (this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) { | ||
if (!this.options.as) { | ||
this.options.as = Utils.singularize(this.target.tableName, this.target.options.language) | ||
} | ||
this.associationAccessor = this.isSelfAssociation | ||
? Utils.combineTableNames(this.target.tableName, this.options.as) | ||
: this.options.as | ||
this.options.useHooks = options.useHooks | ||
this.associationAccessor = this.isSelfAssociation | ||
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName) | ||
: this.options.as || this.target.tableName | ||
this.accessors = { | ||
get: Utils._.camelize('get_' + this.options.as), | ||
set: Utils._.camelize('set_' + this.options.as) | ||
} | ||
} | ||
@@ -45,7 +55,6 @@ | ||
var self = this | ||
, accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language))) | ||
, primaryKeys = Object.keys(self.target.primaryKeys) | ||
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id' | ||
obj[accessor] = function(params) { | ||
obj[this.accessors.get] = function(params) { | ||
var id = this[self.identifier] | ||
@@ -75,5 +84,4 @@ , where = {} | ||
var self = this | ||
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language))) | ||
obj[accessor] = function(associatedObject, options) { | ||
obj[this.accessors.set] = function(associatedObject, options) { | ||
var primaryKeys = !!associatedObject && !!associatedObject.daoFactory ? Object.keys(associatedObject.daoFactory.primaryKeys) : [] | ||
@@ -80,0 +88,0 @@ , primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id' |
@@ -5,4 +5,4 @@ var Utils = require('./../utils') | ||
module.exports = (function() { | ||
var HasManyDoubleLinked = function(definition, instance) { | ||
this.__factory = definition | ||
var HasManyDoubleLinked = function(association, instance) { | ||
this.association = association | ||
this.instance = instance | ||
@@ -21,6 +21,6 @@ | ||
var where = {} | ||
, connectorDAO = self.__factory.connectorDAO | ||
, through = self.association.through | ||
, options = _options || {} | ||
, queryOptions = {} | ||
, association = self.__factory.target.associations[self.__factory.associationAccessor] | ||
, targetAssociation = self.association.targetAssociation | ||
@@ -31,19 +31,19 @@ //fully qualify | ||
where[connectorDAO.tableName+"."+self.__factory.identifier] = self.instance[instancePrimaryKey] | ||
where[through.tableName+"."+self.association.identifier] = self.instance[instancePrimaryKey] | ||
var primaryKeys = Object.keys(connectorDAO.primaryKeys) | ||
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0] | ||
, foreignPrimary = Object.keys(self.__factory.target.primaryKeys) | ||
var primaryKeys = Object.keys(through.primaryKeys) | ||
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.association.identifier })[0] | ||
, foreignPrimary = Object.keys(self.association.target.primaryKeys) | ||
foreignPrimary = foreignPrimary.length === 1 ? foreignPrimary[0] : 'id' | ||
where[connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+"."+foreignPrimary} | ||
where[through.tableName+"."+foreignKey] = {join: self.association.target.tableName+"."+foreignPrimary} | ||
if (association.hasJoinTableModel) { | ||
if (Object(targetAssociation.through) === targetAssociation.through) { | ||
queryOptions.hasJoinTableModel = true | ||
queryOptions.joinTableModel = connectorDAO | ||
queryOptions.joinTableModel = through | ||
if (!options.attributes) { | ||
options.attributes = [ | ||
self.QueryInterface.quoteIdentifier(self.__factory.target.tableName)+".*" | ||
self.QueryInterface.quoteIdentifier(self.association.target.tableName)+".*" | ||
] | ||
@@ -55,11 +55,11 @@ } | ||
options.attributes.push( | ||
self.QueryInterface.quoteIdentifiers(connectorDAO.tableName + '.' + elem) + ' as ' + | ||
self.QueryInterface.quoteIdentifier(connectorDAO.name + '.' + elem, true) | ||
self.QueryInterface.quoteIdentifiers(through.tableName + '.' + elem) + ' as ' + | ||
self.QueryInterface.quoteIdentifier(through.name + '.' + elem, true) | ||
) | ||
}) | ||
} else { | ||
Utils._.forOwn(connectorDAO.rawAttributes, function (elem, key) { | ||
Utils._.forOwn(through.rawAttributes, function (elem, key) { | ||
options.attributes.push( | ||
self.QueryInterface.quoteIdentifiers(connectorDAO.tableName + '.' + key) + ' as ' + | ||
self.QueryInterface.quoteIdentifier(connectorDAO.name + '.' + key, true) | ||
self.QueryInterface.quoteIdentifiers(through.tableName + '.' + key) + ' as ' + | ||
self.QueryInterface.quoteIdentifier(through.name + '.' + key, true) | ||
) | ||
@@ -72,4 +72,4 @@ }) | ||
if (Array.isArray(options.where)) { | ||
smart = Utils.smartWhere([where, options.where], self.__factory.target.daoFactoryManager.sequelize.options.dialect) | ||
smart = Utils.compileSmartWhere.call(self.__factory.target, smart, self.__factory.target.daoFactoryManager.sequelize.options.dialect) | ||
smart = Utils.smartWhere([where, options.where], self.association.target.daoFactoryManager.sequelize.options.dialect) | ||
smart = Utils.compileSmartWhere.call(self.association.target, smart, self.association.target.daoFactoryManager.sequelize.options.dialect) | ||
if (smart.length > 0) { | ||
@@ -79,4 +79,4 @@ options.where = smart | ||
} else { | ||
smart = Utils.smartWhere([where, options.where], self.__factory.target.daoFactoryManager.sequelize.options.dialect) | ||
smart = Utils.compileSmartWhere.call(self.__factory.target, smart, self.__factory.target.daoFactoryManager.sequelize.options.dialect) | ||
smart = Utils.smartWhere([where, options.where], self.association.target.daoFactoryManager.sequelize.options.dialect) | ||
smart = Utils.compileSmartWhere.call(self.association.target, smart, self.association.target.daoFactoryManager.sequelize.options.dialect) | ||
if (smart.length > 0) { | ||
@@ -90,3 +90,3 @@ options.where = smart | ||
self.__factory.target.findAllJoin(connectorDAO.tableName, options, queryOptions) | ||
self.association.target.findAllJoin(through.tableName, options, queryOptions) | ||
.on('success', function(objects) { customEventEmitter.emit('success', objects) }) | ||
@@ -103,6 +103,6 @@ .on('error', function(err){ customEventEmitter.emit('error', err) }) | ||
, chainer = new Utils.QueryChainer() | ||
, association = self.__factory.target.associations[self.__factory.associationAccessor] | ||
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier | ||
, sourceKeys = Object.keys(self.__factory.source.primaryKeys) | ||
, targetKeys = Object.keys(self.__factory.target.primaryKeys) | ||
, targetAssociation = self.association.targetAssociation | ||
, foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier | ||
, sourceKeys = Object.keys(self.association.source.primaryKeys) | ||
, targetKeys = Object.keys(self.association.target.primaryKeys) | ||
, obsoleteAssociations = [] | ||
@@ -131,12 +131,20 @@ , changedAssociations = [] | ||
obsoleteAssociations.push(old) | ||
} else if (association.hasJoinTableModel) { | ||
} else if (Object(targetAssociation.through) === targetAssociation.through) { | ||
var throughAttributes = newObj[self.association.through.name]; | ||
// Quick-fix for subtle bug when using existing objects that might have the through model attached (not as an attribute object) | ||
if (throughAttributes instanceof self.association.through.DAO) { | ||
throughAttributes = {}; | ||
} | ||
var changedAssociation = { | ||
where: {}, | ||
attributes: Utils._.defaults({}, newObj[self.__factory.connectorDAO.name], defaultAttributes) | ||
attributes: Utils._.defaults({}, throughAttributes, defaultAttributes) | ||
} | ||
changedAssociation.where[self.__factory.identifier] = self.instance[self.__factory.identifier] || self.instance.id | ||
changedAssociation.where[self.association.identifier] = self.instance[self.association.identifier] || self.instance.id | ||
changedAssociation.where[foreignIdentifier] = newObj[foreignIdentifier] || newObj.id | ||
changedAssociations.push(changedAssociation) | ||
if (Object.keys(changedAssociation.attributes).length) { | ||
changedAssociations.push(changedAssociation) | ||
} | ||
} | ||
@@ -152,6 +160,6 @@ }) | ||
where[self.__factory.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) | ||
where[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) | ||
where[foreignIdentifier] = foreignIds | ||
chainer.add(self.__factory.connectorDAO.destroy(where, options)) | ||
chainer.add(self.association.through.destroy(where, options)) | ||
} | ||
@@ -163,7 +171,7 @@ | ||
attributes[self.__factory.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) | ||
attributes[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) | ||
attributes[foreignIdentifier] = ((targetKeys.length === 1) ? unassociatedObject[targetKeys[0]] : unassociatedObject.id) | ||
if (association.hasJoinTableModel) { | ||
attributes = Utils._.defaults(attributes, unassociatedObject[association.connectorDAO.name], defaultAttributes) | ||
if (Object(targetAssociation.through) === targetAssociation.through) { | ||
attributes = Utils._.defaults(attributes, unassociatedObject[targetAssociation.through.name], defaultAttributes) | ||
} | ||
@@ -174,3 +182,3 @@ | ||
chainer.add(self.__factory.connectorDAO.bulkCreate(bulk, options)) | ||
chainer.add(self.association.through.bulkCreate(bulk, options)) | ||
} | ||
@@ -180,3 +188,3 @@ | ||
changedAssociations.forEach(function (assoc) { | ||
chainer.add(self.__factory.connectorDAO.update(assoc.attributes, assoc.where, options)) | ||
chainer.add(self.association.through.update(assoc.attributes, assoc.where, options)) | ||
}) | ||
@@ -193,23 +201,25 @@ } | ||
HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) { | ||
var attributes = {} | ||
, association = this.__factory.target.associations[this.__factory.associationAccessor] | ||
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier; | ||
var attributes = {} | ||
, targetAssociation = this.association.targetAssociation | ||
, foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier; | ||
var sourceKeys = Object.keys(this.__factory.source.primaryKeys); | ||
var targetKeys = Object.keys(this.__factory.target.primaryKeys); | ||
var sourceKeys = Object.keys(this.association.source.primaryKeys); | ||
var targetKeys = Object.keys(this.association.target.primaryKeys); | ||
attributes[this.__factory.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id) | ||
attributes[this.association.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id) | ||
attributes[foreignIdentifier] = ((targetKeys.length === 1) ? newAssociation[targetKeys[0]] : newAssociation.id) | ||
if (exists) { // implies hasJoinTableModel === true | ||
if (exists) { | ||
var where = attributes | ||
attributes = Utils._.defaults({}, newAssociation[association.connectorDAO.name], additionalAttributes) | ||
attributes = Utils._.defaults({}, newAssociation[targetAssociation.through.name], additionalAttributes) | ||
association.connectorDAO.update(attributes, where).proxy(emitterProxy) | ||
if (Object.keys(attributes).length) { | ||
targetAssociation.through.update(attributes, where).proxy(emitterProxy) | ||
} else { | ||
emitterProxy.emit('success') | ||
} | ||
} else { | ||
if (association.hasJoinTableModel === true) { | ||
attributes = Utils._.defaults(attributes, newAssociation[association.connectorDAO.name], additionalAttributes) | ||
} | ||
attributes = Utils._.defaults(attributes, newAssociation[targetAssociation.through.name], additionalAttributes) | ||
this.__factory.connectorDAO.create(attributes) | ||
this.association.through.create(attributes) | ||
.success(function() { emitterProxy.emit('success', newAssociation) }) | ||
@@ -216,0 +226,0 @@ .error(function(err) { emitterProxy.emit('error', err) }) |
var Utils = require("./../utils") | ||
, DataTypes = require('./../data-types') | ||
, Helpers = require('./helpers') | ||
, _ = require('lodash') | ||
@@ -9,27 +10,96 @@ var HasManySingleLinked = require("./has-many-single-linked") | ||
module.exports = (function() { | ||
var HasMany = function(srcDAO, targetDAO, options) { | ||
var HasMany = function(source, target, options) { | ||
var self = this | ||
this.associationType = 'HasMany' | ||
this.source = srcDAO | ||
this.target = targetDAO | ||
this.source = source | ||
this.target = target | ||
this.targetAssociation = null | ||
this.options = options | ||
this.useJunctionTable = this.options.useJunctionTable === undefined ? true : this.options.useJunctionTable | ||
this.sequelize = source.daoFactoryManager.sequelize | ||
this.through = options.through | ||
this.isMultiAssociation = true | ||
this.isSelfAssociation = (this.source.tableName === this.target.tableName) | ||
this.hasJoinTableModel = !!this.options.joinTableModel | ||
this.doubleLinked = false | ||
this.combinedTableName = Utils.combineTableNames( | ||
this.source.tableName, | ||
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName | ||
) | ||
var combinedTableName; | ||
if (this.hasJoinTableModel) { | ||
combinedTableName = this.options.joinTableModel.tableName | ||
} else if (this.options.joinTableName) { | ||
combinedTableName = this.options.joinTableName | ||
} else { | ||
combinedTableName = Utils.combineTableNames( | ||
this.source.tableName, | ||
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName | ||
) | ||
/* | ||
* Map joinTableModel/Name to through for BC | ||
*/ | ||
if (this.through === undefined) { | ||
this.through = this.options.joinTableModel || this.options.joinTableName; | ||
/* | ||
* If both are undefined, see if useJunctionTable was false (for self associations) - else assume through to be true | ||
*/ | ||
if (this.through === undefined) { | ||
if (this.options.useJunctionTable === false) { | ||
this.through = null; | ||
} else { | ||
this.through = true; | ||
} | ||
} | ||
} | ||
this.options.tableName = this.combinedName = (this.options.joinTableName || combinedTableName) | ||
this.options.useHooks = options.useHooks | ||
this.associationAccessor = this.options.as || this.combinedName | ||
/* | ||
* Determine associationAccessor, especially for include options to identify the correct model | ||
*/ | ||
this.associationAccessor = this.options.as | ||
if (!this.associationAccessor && (typeof this.through === "string" || Object(this.through) === this.through)) { | ||
this.associationAccessor = this.through.tableName || this.through | ||
} | ||
else if (!this.associationAccessor) { | ||
this.associationAccessor = this.combinedTableName | ||
} | ||
/* | ||
* Find partner DAOFactory if present, to identify double linked association | ||
*/ | ||
if (this.through) { | ||
_.each(this.target.associations, function (association, accessor) { | ||
if (self.source === association.target) { | ||
var paired = false | ||
// If through is default, we determine pairing by the accesor value (i.e. DAOFactory's using as won't pair, but regular ones will) | ||
if (self.through === true && accessor === self.associationAccessor) { | ||
paired = true | ||
} | ||
// If through is not default, determine pairing by through value (model/string) | ||
if (self.through !== true && self.options.through === association.options.through) { | ||
paired = true | ||
} | ||
// If paired, set properties identifying both associations as double linked, and allow them to each eachtoerh | ||
if (paired) { | ||
self.doubleLinked = true | ||
association.doubleLinked = true | ||
self.targetAssociation = association | ||
association.targetAssociation = self | ||
} | ||
} | ||
}) | ||
} | ||
/* | ||
* If we are double linked, and through is either default or a string, we create the through model and set it on both associations | ||
*/ | ||
if (this.doubleLinked) { | ||
if (this.through === true) { | ||
this.through = this.combinedTableName | ||
} | ||
if (typeof this.through === "string") { | ||
this.through = this.sequelize.define(this.through, {}, _.extend(this.options, { | ||
tableName: this.through | ||
})) | ||
this.targetAssociation.through = this.through | ||
} | ||
} | ||
this.options.tableName = this.combinedName = (this.through === Object(this.through) ? this.through.tableName : this.through) | ||
var as = (this.options.as || Utils.pluralize(this.target.tableName, this.target.options.language)) | ||
@@ -50,4 +120,4 @@ | ||
HasMany.prototype.injectAttributes = function() { | ||
var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor) | ||
, self = this | ||
var doubleLinked = this.doubleLinked | ||
, self = this | ||
@@ -58,3 +128,3 @@ this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName, this.source.options.language) + "Id", this.options.underscored) | ||
// or is the association on the model itself? | ||
if ((this.isSelfAssociation && this.useJunctionTable) || multiAssociation) { | ||
if ((this.isSelfAssociation && this.through) || doubleLinked) { | ||
// remove the obsolete association identifier from the source | ||
@@ -64,4 +134,4 @@ if (this.isSelfAssociation) { | ||
} else { | ||
this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier | ||
this.target.associations[this.associationAccessor].foreignIdentifier = this.identifier | ||
this.foreignIdentifier = this.targetAssociation.identifier | ||
this.targetAssociation.foreignIdentifier = this.identifier | ||
@@ -73,3 +143,3 @@ if (isForeignKeyDeletionAllowedFor.call(this, this.source, this.foreignIdentifier)) { | ||
if (isForeignKeyDeletionAllowedFor.call(this, this.target, this.identifier)) { | ||
delete this.target.associations[this.associationAccessor].source.rawAttributes[this.identifier] | ||
delete this.targetAssociation.source.rawAttributes[this.identifier] | ||
} | ||
@@ -87,24 +157,14 @@ } | ||
if (this.hasJoinTableModel === true) { | ||
this.connectorDAO = this.options.joinTableModel | ||
// remove any previously defined PKs | ||
Utils._.each(this.through.attributes, function(dataTypeString, attributeName) { | ||
if (dataTypeString.toString().indexOf('PRIMARY KEY') !== -1) { | ||
delete self.through.rawAttributes[attributeName] | ||
} | ||
}) | ||
// remove any previously defined PKs | ||
Utils._.each(this.connectorDAO.attributes, function(dataTypeString, attributeName) { | ||
if (dataTypeString.toString().indexOf('PRIMARY KEY') !== -1) { | ||
delete self.connectorDAO.rawAttributes[attributeName] | ||
} | ||
}) | ||
this.through.rawAttributes = Utils._.merge(this.through.rawAttributes, combinedTableAttributes) | ||
this.through.init(this.through.daoFactoryManager) | ||
this.connectorDAO.rawAttributes = Utils._.merge(this.connectorDAO.rawAttributes, combinedTableAttributes) | ||
this.connectorDAO.init(this.connectorDAO.daoFactoryManager) | ||
} else { | ||
this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options) | ||
} | ||
if (!this.isSelfAssociation) { | ||
this.target.associations[this.associationAccessor].connectorDAO = this.connectorDAO | ||
} | ||
if (this.options.syncOnAssociation) { | ||
this.connectorDAO.sync() | ||
this.through.sync() | ||
} | ||
@@ -129,3 +189,3 @@ } else { | ||
obj[this.accessors.get] = function(options) { | ||
var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked | ||
var Class = Object(self.through) === self.through ? HasManyMultiLinked : HasManySingleLinked | ||
return new Class(self, this).injectGetter(options) | ||
@@ -188,11 +248,6 @@ } | ||
.success(function(oldAssociatedObjects) { | ||
var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked | ||
var Class = self.doubleLinked ? HasManyMultiLinked : HasManySingleLinked | ||
new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes) | ||
}) | ||
.error(function(err) { | ||
emitter.emit('error', err) | ||
}) | ||
.on('sql', function(sql) { | ||
emitter.emit('sql', sql) | ||
}) | ||
.proxy(emitter, {events: ['error', 'sql']}) | ||
}).run() | ||
@@ -211,6 +266,6 @@ } | ||
instance[self.accessors.get]({ where: where }) | ||
.error(function(err){ emitter.emit('error', err)}) | ||
.proxy(emitter, {events: ['error', 'sql']}) | ||
.success(function(currentAssociatedObjects) { | ||
if (currentAssociatedObjects.length === 0 || self.hasJoinTableModel === true) { | ||
var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked | ||
if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) { | ||
var Class = self.doubleLinked ? HasManyMultiLinked : HasManySingleLinked | ||
new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length) | ||
@@ -226,3 +281,3 @@ } else { | ||
var instance = this | ||
var customEventEmitter = new Utils.CustomEventEmitter(function() { | ||
return new Utils.CustomEventEmitter(function(emitter) { | ||
instance[self.accessors.get]().success(function(currentAssociatedObjects) { | ||
@@ -255,8 +310,6 @@ var newAssociations = [] | ||
if (!!err) { | ||
return customEventEmitter.emit('error', err) | ||
return emitter.emit('error', err) | ||
} | ||
instance[self.accessors.set](newAssociations) | ||
.success(function() { customEventEmitter.emit('success', null) }) | ||
.error(function(err) { customEventEmitter.emit('error', err) }) | ||
instance[self.accessors.set](newAssociations).proxy(emitter) | ||
} | ||
@@ -270,4 +323,3 @@ | ||
}) | ||
}) | ||
return customEventEmitter.run() | ||
}).run() | ||
} | ||
@@ -274,0 +326,0 @@ |
@@ -7,7 +7,8 @@ var Utils = require("./../utils") | ||
var HasOne = function(srcDAO, targetDAO, options) { | ||
this.associationType = 'HasOne' | ||
this.source = srcDAO | ||
this.target = targetDAO | ||
this.options = options | ||
this.isSelfAssociation = (this.source.tableName == this.target.tableName) | ||
this.associationType = 'HasOne' | ||
this.source = srcDAO | ||
this.target = targetDAO | ||
this.options = options | ||
this.isSingleAssociation = true | ||
this.isSelfAssociation = (this.source.tableName == this.target.tableName) | ||
@@ -18,5 +19,9 @@ if (this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) { | ||
if (!this.options.as) { | ||
this.options.as = Utils.singularize(this.target.tableName, this.target.options.language) | ||
} | ||
this.associationAccessor = this.isSelfAssociation | ||
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName) | ||
: this.options.as || this.target.tableName | ||
? Utils.combineTableNames(this.target.tableName, this.options.as) | ||
: this.options.as | ||
@@ -26,4 +31,4 @@ this.options.useHooks = options.useHooks | ||
this.accessors = { | ||
get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language))), | ||
set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language))) | ||
get: Utils._.camelize('get_' + this.options.as), | ||
set: Utils._.camelize('set_' + this.options.as) | ||
} | ||
@@ -30,0 +35,0 @@ } |
@@ -9,3 +9,3 @@ var Utils = require("./../utils") | ||
Mixin.hasOne = function(associatedDAO, options) { | ||
Mixin.hasOne = function(associatedDAOFactory, options) { | ||
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option) | ||
@@ -17,3 +17,3 @@ options = options || {} | ||
// the id is in the foreign table | ||
var association = new HasOne(this, associatedDAO, Utils._.extend((options||{}), this.options)) | ||
var association = new HasOne(this, associatedDAOFactory, Utils._.extend((options||{}), this.options)) | ||
this.associations[association.associationAccessor] = association.injectAttributes() | ||
@@ -27,3 +27,3 @@ | ||
Mixin.belongsTo = function(associatedDAO, options) { | ||
Mixin.belongsTo = function(associatedDAOFactory, options) { | ||
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option) | ||
@@ -35,3 +35,3 @@ options = options || {} | ||
// the id is in this table | ||
var association = new BelongsTo(this, associatedDAO, Utils._.extend(options, this.options)) | ||
var association = new BelongsTo(this, associatedDAOFactory, Utils._.extend(options, this.options)) | ||
this.associations[association.associationAccessor] = association.injectAttributes() | ||
@@ -45,3 +45,3 @@ | ||
Mixin.hasMany = function(associatedDAO, options) { | ||
Mixin.hasMany = function(associatedDAOFactory, options) { | ||
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option) | ||
@@ -53,3 +53,3 @@ options = options || {} | ||
// the id is in the foreign table or in a connecting table | ||
var association = new HasMany(this, associatedDAO, Utils._.extend((options||{}), this.options)) | ||
var association = new HasMany(this, associatedDAOFactory, Utils._.extend((options||{}), this.options)) | ||
this.associations[association.associationAccessor] = association.injectAttributes() | ||
@@ -56,0 +56,0 @@ |
@@ -171,3 +171,6 @@ var Utils = require("./utils") | ||
this.DAO.prototype.__factory = this | ||
this.DAO.prototype.daoFactory = this | ||
this.DAO.prototype.Model = this | ||
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues) | ||
this.DAO.prototype.daoFactoryName = this.name | ||
@@ -377,5 +380,3 @@ return this | ||
options.include = options.include.map(function(include) { | ||
return validateIncludedElement.call(this, include) | ||
}.bind(this)) | ||
validateIncludedElements.call(this, options) | ||
} | ||
@@ -463,5 +464,3 @@ | ||
options.include = options.include.map(function(include) { | ||
return validateIncludedElement.call(this, include) | ||
}.bind(this)) | ||
validateIncludedElements.call(this, options) | ||
} | ||
@@ -561,10 +560,20 @@ | ||
if (options.hasOwnProperty('include') && options.include && (!options.includeValidated || !options.includeNames)) { | ||
options.includeNames = [] | ||
options.include = options.include.map(function(include) { | ||
include = validateIncludedElement.call(this, include) | ||
options.includeNames.push(include.as) | ||
return include | ||
}.bind(this)) | ||
} | ||
if (options.includeNames) { | ||
options.includeNames = options.includeNames.concat(options.includeNames.map(function (key) { | ||
return key.slice(0,1).toLowerCase() + key.slice(1) | ||
})) | ||
} | ||
var self = this | ||
, instance = new this.DAO(values, this.options, options.isNewRecord) | ||
, instance = new this.DAO(values, options) | ||
instance.isNewRecord = options.isNewRecord | ||
instance.daoFactoryName = this.name | ||
instance.daoFactory = this | ||
instance.isDirty = options.isDirty | ||
return instance | ||
@@ -784,3 +793,3 @@ } | ||
emitter.emit('error', err) | ||
}).success(function() { | ||
}).success(function(rows) { | ||
done() | ||
@@ -846,2 +855,5 @@ }) | ||
DAOFactory.prototype.destroy = function(where, options) { | ||
options = options || {} | ||
options.force = options.force === undefined ? false : Boolean(options.force) | ||
var self = this | ||
@@ -859,3 +871,3 @@ , query = null | ||
if (self.options.timestamps && self.options.paranoid) { | ||
if (self.options.timestamps && self.options.paranoid && options.force === false) { | ||
var attr = Utils._.underscoredIf(self.options.deletedAt, self.options.underscored) | ||
@@ -1119,4 +1131,6 @@ var attrValueHash = {} | ||
var deletedAtCol = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored) | ||
// Don't overwrite our explicit deletedAt search value if we provide one | ||
if (!!options.where[this.options.deletedAt]) { | ||
if (!!options.where[deletedAtCol]) { | ||
return options | ||
@@ -1126,8 +1140,8 @@ } | ||
if (typeof options.where === "string") { | ||
options.where += ' AND ' + this.QueryInterface.quoteIdentifier(this.options.deletedAt) + ' IS NULL ' | ||
options.where += ' AND ' + this.QueryInterface.quoteIdentifier(deletedAtCol) + ' IS NULL ' | ||
} | ||
else if (Array.isArray(options.where)) { | ||
options.where[0] += ' AND ' + this.QueryInterface.quoteIdentifier(this.options.deletedAt) + ' IS NULL ' | ||
options.where[0] += ' AND ' + this.QueryInterface.quoteIdentifier(deletedAtCol) + ' IS NULL ' | ||
} else { | ||
options.where[this.options.deletedAt] = null | ||
options.where[deletedAtCol] = null | ||
} | ||
@@ -1188,3 +1202,15 @@ } | ||
var validateIncludedElement = function(include) { | ||
var validateIncludedElements = function(options) { | ||
options.includeNames = [] | ||
options.includeMap = {} | ||
options.include = options.include.map(function(include) { | ||
include = validateIncludedElement.call(this, include, options.daoFactory) | ||
options.includeMap[include.as] = include | ||
options.includeNames.push(include.as) | ||
return include | ||
}.bind(this)) | ||
}; | ||
var validateIncludedElement = function(include, parent) { | ||
if (include instanceof DAOFactory) { | ||
@@ -1194,2 +1220,6 @@ include = { daoFactory: include, as: include.tableName } | ||
if (typeof parent === "undefined") { | ||
parent = this | ||
} | ||
if (typeof include === 'object') { | ||
@@ -1201,2 +1231,6 @@ if (include.hasOwnProperty('model')) { | ||
if (!include.hasOwnProperty('as')) { | ||
include.as = include.daoFactory.tableName | ||
} | ||
if (include.hasOwnProperty('attributes')) { | ||
@@ -1219,4 +1253,9 @@ var primaryKeys; | ||
var usesAlias = (include.as !== include.daoFactory.tableName) | ||
, association = (usesAlias ? this.getAssociationByAlias(include.as) : this.getAssociation(include.daoFactory)) | ||
, association = (usesAlias ? parent.getAssociationByAlias(include.as) : parent.getAssociation(include.daoFactory)) | ||
// If single (1:1) association, we singularize the alias, so it will match the automatically generated alias of belongsTo/HasOne | ||
if (association && !usesAlias && association.isSingleAssociation) { | ||
include.as = Utils.singularize(include.daoFactory.tableName, include.daoFactory.options.language) | ||
} | ||
// check if the current daoFactory is actually associated with the passed daoFactory | ||
@@ -1226,2 +1265,6 @@ if (!!association && (!association.options.as || (association.options.as === include.as))) { | ||
if (include.hasOwnProperty('include')) { | ||
validateIncludedElements(include) | ||
} | ||
return include | ||
@@ -1228,0 +1271,0 @@ } else { |
104
lib/dao.js
@@ -6,12 +6,18 @@ var Utils = require("./utils") | ||
, hstore = require('./dialects/postgres/hstore') | ||
, _ = require('lodash') | ||
module.exports = (function() { | ||
var DAO = function(values, options, isNewRecord) { | ||
var DAO = function(values, options) { | ||
this.dataValues = {} | ||
this.__options = options | ||
this.hasPrimaryKeys = options.hasPrimaryKeys | ||
this.selectedValues = values | ||
this.__options = this.__factory.options | ||
this.options = options | ||
this.hasPrimaryKeys = this.__factory.options.hasPrimaryKeys | ||
// What is selected values even used for? | ||
this.selectedValues = options.include ? _.omit(values, options.includeNames) : values | ||
this.__eagerlyLoadedAssociations = [] | ||
this.isNewRecord = options.isNewRecord | ||
initAttributes.call(this, values, isNewRecord) | ||
initAttributes.call(this, values, options) | ||
this.isDirty = options.isDirty | ||
} | ||
@@ -40,13 +46,3 @@ | ||
get: function() { | ||
var result = {} | ||
, self = this | ||
this.attributes.concat(this.__eagerlyLoadedAssociations).forEach(function(attr) { | ||
result[attr] = self.dataValues.hasOwnProperty(attr) | ||
? self.dataValues[attr] | ||
: self[attr] | ||
; | ||
}) | ||
return result | ||
return this.dataValues | ||
} | ||
@@ -109,3 +105,3 @@ }) | ||
var self = this | ||
, values = options.fields ? {} : this.dataValues | ||
, values = options.fields ? {} : (this.options.includeNames ? _.omit(this.dataValues, this.options.includeNames) : this.dataValues) | ||
, updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored) | ||
@@ -220,3 +216,3 @@ , createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored) | ||
result.dataValues = newValues | ||
result.dataValues = _.extend(result.dataValues, newValues) | ||
emitter.emit('success', result) | ||
@@ -247,3 +243,3 @@ }) | ||
limit: 1, | ||
include: this.__eagerlyLoadedOptions || [] | ||
include: this.options.include || [] | ||
}, options) | ||
@@ -337,2 +333,5 @@ .on('sql', function(sql) { emitter.emit('sql', sql) }) | ||
DAO.prototype.destroy = function(options) { | ||
options = options || {} | ||
options.force = options.force === undefined ? false : Boolean(options.force) | ||
var self = this | ||
@@ -347,3 +346,3 @@ , query = null | ||
if (self.__options.timestamps && self.__options.paranoid) { | ||
if (self.__options.timestamps && self.__options.paranoid && options.force === false) { | ||
var attr = Utils._.underscoredIf(self.__options.deletedAt, self.__options.underscored) | ||
@@ -481,3 +480,3 @@ self.dataValues[attr] = new Date() | ||
DAO.prototype.toJSON = function() { | ||
return this.values; | ||
return this.dataValues; | ||
} | ||
@@ -487,3 +486,3 @@ | ||
var initAttributes = function(values, isNewRecord) { | ||
var initAttributes = function(values, options) { | ||
// set id to null if not passed as value, a newly created dao has no id | ||
@@ -495,9 +494,4 @@ var defaults = this.hasPrimaryKeys ? {} : { id: null }, | ||
// add all passed values to the dao and store the attribute names in this.attributes | ||
for (key in values) { | ||
if (values.hasOwnProperty(key)) { | ||
this.addAttribute(key, values[key]) | ||
} | ||
} | ||
if (isNewRecord) { | ||
if (options.isNewRecord) { | ||
if (this.hasDefaultValues) { | ||
@@ -547,18 +541,46 @@ Utils._.each(this.defaultValues, function(valueFn, key) { | ||
for (key in attrs) { | ||
this.addAttribute(key, attrs[key]) | ||
} | ||
if (options.include && options.includeNames.indexOf(key) !== -1) { | ||
if (!Array.isArray(attrs[key])) attrs[key] = [attrs[key]]; | ||
// this.addAttributes COMPLETELY destroys the structure of our DAO due to __defineGetter__ resetting the object | ||
// so now we have to rebuild for bulkInserts, bulkUpdates, etc. | ||
var rebuild = {} | ||
var include = _.find(options.include, function (include) { | ||
return include.as === key || (include.as.slice(0,1).toLowerCase() + include.as.slice(1)) === key | ||
}) | ||
var association = include.association | ||
, self = this | ||
// Get the correct map.... | ||
Utils._.each(this.attributes, function(key) { | ||
if (this.dataValues.hasOwnProperty(key)) { | ||
rebuild[key] = this.dataValues[key] | ||
var accessor = Utils._.camelize(key) | ||
// downcase the first char | ||
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1) | ||
attrs[key].forEach(function(data) { | ||
var daoInstance = include.daoFactory.build(data, { | ||
isNewRecord: false, | ||
isDirty: false, | ||
include: include.include, | ||
includeNames: include.includeNames, | ||
includeMap: include.includeMap, | ||
includeValidated: true | ||
}) | ||
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers) | ||
if (association.isSingleAssociation) { | ||
accessor = Utils.singularize(accessor, self.sequelize.language) | ||
self.dataValues[accessor] = isEmpty ? null : daoInstance | ||
self[accessor] = self.dataValues[accessor] | ||
} else { | ||
if (!self.dataValues[accessor]) { | ||
self.dataValues[accessor] = [] | ||
self[accessor] = self.dataValues[accessor] | ||
} | ||
if (!isEmpty) { | ||
self.dataValues[accessor].push(daoInstance) | ||
} | ||
} | ||
}.bind(this)) | ||
} else { | ||
this.addAttribute(key, attrs[key]) | ||
} | ||
}.bind(this)) | ||
// This allows for aliases, etc. | ||
this.dataValues = Utils._.extend(rebuild, this.dataValues) | ||
} | ||
} | ||
@@ -565,0 +587,0 @@ |
@@ -191,3 +191,3 @@ var STRING = function(length, binary) { | ||
BLOB: BLOB, | ||
UUID: 'CHAR(36)', | ||
UUID: 'UUID', | ||
UUIDV1: 'UUIDV1', | ||
@@ -194,0 +194,0 @@ UUIDV4: 'UUIDV4', |
@@ -364,4 +364,5 @@ var Utils = require("../../utils") | ||
selectQuery: function(tableName, options, factory) { | ||
var table = null, | ||
joinQuery = "" | ||
var table = null | ||
, self = this | ||
, joinQuery = "" | ||
@@ -382,23 +383,18 @@ options = options || {} | ||
options.include.forEach(function(include) { | ||
var attributes = include.attributes.map(function(attr) { | ||
return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr) | ||
}.bind(this)) | ||
var generateJoinQuery = function(include, parentTable) { | ||
var table = include.daoFactory.tableName | ||
, as = include.as | ||
, joinQueryItem = "" | ||
, attributes | ||
if (tableName !== parentTable) as = parentTable+'.'+include.as | ||
attributes = include.attributes.map(function(attr) { | ||
return self.quoteIdentifier(as) + "." + self.quoteIdentifier(attr) + " AS " + self.quoteIdentifier(as + "." + attr) | ||
}) | ||
optAttributes = optAttributes.concat(attributes) | ||
var table = include.daoFactory.tableName | ||
, as = include.as | ||
if (!include.association.connectorDAO) { | ||
var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys)) | ||
, tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) | ||
, attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0]) | ||
, tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) | ||
, attrRight = include.association.identifier | ||
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) | ||
} else { | ||
if (include.association.doubleLinked) { | ||
var primaryKeysSource = Object.keys(include.association.source.primaryKeys) | ||
, tableSource = tableName | ||
, tableSource = parentTable | ||
, identSource = include.association.identifier | ||
@@ -408,10 +404,35 @@ , attrSource = ((!include.association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0]) | ||
var primaryKeysTarget = Object.keys(include.association.target.primaryKeys) | ||
, tableTarget = include.as | ||
, tableTarget = as | ||
, identTarget = include.association.foreignIdentifier | ||
, attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0]) | ||
var tableJunction = include.association.connectorDAO.tableName | ||
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableSource) + "." + this.quoteIdentifier(attrSource) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identSource) | ||
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableTarget) + "." + this.quoteIdentifier(attrTarget) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identTarget) | ||
var tableJunction = include.association.through.tableName | ||
joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(tableJunction) + " ON " | ||
joinQueryItem += self.quoteIdentifier(tableSource) + "." + self.quoteIdentifier(attrSource) + " = " | ||
joinQueryItem += self.quoteIdentifier(tableJunction) + "." + self.quoteIdentifier(identSource) | ||
joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON " | ||
joinQueryItem += self.quoteIdentifier(tableTarget) + "." + self.quoteIdentifier(attrTarget) + " = " | ||
joinQueryItem += self.quoteIdentifier(tableJunction) + "." + self.quoteIdentifier(identTarget) | ||
} else { | ||
var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys)) | ||
, tableLeft = ((include.association.associationType === 'BelongsTo') ? as : parentTable) | ||
, attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0]) | ||
, tableRight = ((include.association.associationType === 'BelongsTo') ? parentTable : as) | ||
, attrRight = include.association.identifier | ||
joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON " + self.quoteIdentifier(tableLeft) + "." + self.quoteIdentifier(attrLeft) + " = " + self.quoteIdentifier(tableRight) + "." + self.quoteIdentifier(attrRight) | ||
} | ||
if (include.include) { | ||
include.include.forEach(function(childInclude) { | ||
joinQueryItem += generateJoinQuery(childInclude, as) | ||
}.bind(this)) | ||
} | ||
return joinQueryItem | ||
} | ||
options.include.forEach(function(include) { | ||
joinQuery += generateJoinQuery(include, tableName) | ||
}.bind(this)) | ||
@@ -641,10 +662,10 @@ | ||
joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField) | ||
} else if (association.connectorDAO){ | ||
joinedTables[association.connectorDAO.tableName] = true; | ||
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.connectorDAO.tableName) | ||
} else if (Object(association.through) === association.through) { | ||
joinedTables[association.through.tableName] = true; | ||
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.through.tableName) | ||
joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField) | ||
joins += ' = ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.identifier) | ||
joins += ' = ' + self.quoteIdentifiers(association.through.tableName + '.' + association.identifier) | ||
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName) | ||
joins += ' ON ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.foreignIdentifier) | ||
joins += ' ON ' + self.quoteIdentifiers(association.through.tableName + '.' + association.foreignIdentifier) | ||
joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField) | ||
@@ -694,3 +715,3 @@ } else { | ||
result.push(this.arrayValue(value, key, _key, dao)) | ||
} else if ((value) && (typeof value == 'object') && !(value instanceof Date)) { | ||
} else if ((value) && (typeof value == 'object') && !(value instanceof Date) && !Buffer.isBuffer(value)) { | ||
if (!!value.join) { | ||
@@ -697,0 +718,0 @@ //using as sentinel for join column => value |
var Utils = require('../../utils') | ||
, CustomEventEmitter = require("../../emitters/custom-event-emitter") | ||
, Dot = require('dottie') | ||
, _ = require('lodash') | ||
@@ -103,35 +104,2 @@ module.exports = (function() { | ||
/** | ||
Shortcut methods (success, ok) for listening for success events. | ||
Params: | ||
- fct: A function that gets executed once the *success* event was triggered. | ||
Result: | ||
The function returns the instance of the query. | ||
*/ | ||
AbstractQuery.prototype.success = | ||
AbstractQuery.prototype.ok = | ||
function(fct) { | ||
this.on('success', fct) | ||
return this | ||
} | ||
/** | ||
Shortcut methods (failure, fail, error) for listening for error events. | ||
Params: | ||
- fct: A function that gets executed once the *error* event was triggered. | ||
Result: | ||
The function returns the instance of the query. | ||
*/ | ||
AbstractQuery.prototype.failure = | ||
AbstractQuery.prototype.fail = | ||
AbstractQuery.prototype.error = | ||
function(fct) { | ||
this.on('error', fct) | ||
return this | ||
} | ||
/** | ||
* This function is a wrapper for private methods. | ||
@@ -171,6 +139,9 @@ * | ||
} | ||
if (!this.options.includeNames) { | ||
this.options.includeNames = this.options.include.map(function(include) { | ||
return include.as | ||
}) | ||
} | ||
var tableNames = this.options.include.map(function(include) { | ||
return include.as | ||
}).filter(function(include) { | ||
var tableNames = this.options.includeNames.filter(function(include) { | ||
return attribute.indexOf(include + '.') === 0 | ||
@@ -186,16 +157,2 @@ }) | ||
var queryResultHasJoin = function(results) { | ||
if (!!results[0]) { | ||
var keys = Object.keys(results[0]) | ||
for (var i = 0; i < keys.length; i++) { | ||
if (!!findTableNameInAttribute.call(this, keys[i])) { | ||
return true | ||
} | ||
} | ||
} | ||
return false | ||
} | ||
var isInsertQuery = function(results, metaData) { | ||
@@ -240,3 +197,3 @@ var result = true | ||
var isSelectQuery = function() { | ||
return this.options.type === 'SELECT'; | ||
return this.options.type === 'SELECT' | ||
} | ||
@@ -251,2 +208,3 @@ | ||
// Raw queries | ||
if (this.options.raw) { | ||
@@ -266,4 +224,16 @@ result = results.map(function(result) { | ||
result = result.map(Dot.transform) | ||
// Queries with include | ||
} else if (this.options.hasJoin === true) { | ||
result = transformRowsWithEagerLoadingIntoDaos.call(this, results) | ||
results = groupJoinData(results, this.options) | ||
result = results.map(function(result) { | ||
return this.callee.build(result, { | ||
isNewRecord: false, | ||
isDirty: false, | ||
include:this.options.include, | ||
includeNames: this.options.includeNames, | ||
includeMap: this.options.includeMap, | ||
includeValidated: true | ||
}) | ||
}.bind(this)) | ||
} else if (this.options.hasJoinTableModel === true) { | ||
@@ -284,2 +254,4 @@ result = results.map(function(result) { | ||
}.bind(this)) | ||
// Regular queries | ||
} else { | ||
@@ -299,71 +271,2 @@ result = results.map(function(result) { | ||
var transformRowsWithEagerLoadingIntoDaos = function(results) { | ||
var result = [] | ||
result = prepareJoinData.call(this, results) | ||
result = groupDataByCalleeFactory.call(this, result).map(function(result) { | ||
return transformRowWithEagerLoadingIntoDao.call(this, result) | ||
}.bind(this)) | ||
return result | ||
} | ||
var transformRowWithEagerLoadingIntoDao = function(result, dao) { | ||
// let's build the actual dao instance first... | ||
dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false, isDirty: false }) | ||
// ... and afterwards the prefetched associations | ||
for (var tableName in result) { | ||
if (result.hasOwnProperty(tableName) && (tableName !== this.callee.tableName)) { | ||
buildAssociatedDaoInstances.call(this, tableName, result[tableName], dao) | ||
} | ||
} | ||
return dao | ||
} | ||
var buildAssociatedDaoInstances = function(tableName, associationData, dao) { | ||
var associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(tableName, { attribute: 'tableName' }) | ||
, association = null | ||
, self = this | ||
if (!!associatedDaoFactory) { | ||
association = this.callee.getAssociation(associatedDaoFactory) | ||
} else { | ||
associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(Utils.pluralize(tableName, this.sequelize.language), { attribute: 'tableName' }) | ||
if (!!associatedDaoFactory) { | ||
association = this.callee.getAssociation(associatedDaoFactory) | ||
} else { | ||
association = this.callee.getAssociationByAlias(tableName) | ||
associatedDaoFactory = association.target | ||
} | ||
} | ||
var accessor = Utils._.camelize(tableName) | ||
// downcase the first char | ||
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1) | ||
associationData.forEach(function(data) { | ||
var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false, isDirty: false }) | ||
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers) | ||
if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) { | ||
accessor = Utils.singularize(accessor, self.sequelize.language) | ||
dao[accessor] = isEmpty ? null : daoInstance | ||
} else { | ||
dao[accessor] = dao[accessor] || [] | ||
if (!isEmpty) { | ||
dao[accessor].push(daoInstance) | ||
} | ||
} | ||
// add the accessor to the eagerly loaded associations array | ||
dao.__eagerlyLoadedAssociations = Utils._.uniq(dao.__eagerlyLoadedAssociations.concat([accessor])) | ||
dao.__eagerlyLoadedOptions = (this.options && this.options.include) ? this.options.include : [] | ||
}.bind(this)) | ||
} | ||
var isShowOrDescribeQuery = function() { | ||
@@ -392,11 +295,14 @@ var result = false | ||
Example: | ||
groupDataByCalleeFactory([ | ||
groupJoinData([ | ||
{ | ||
callee: { some: 'data', id: 1 }, | ||
some: 'data', | ||
id: 1, | ||
association: { foo: 'bar', id: 1 } | ||
}, { | ||
callee: { some: 'data', id: 1 }, | ||
some: 'data', | ||
id: 1, | ||
association: { foo: 'bar', id: 2 } | ||
}, { | ||
callee: { some: 'data', id: 1 }, | ||
some: 'data', | ||
id: 1, | ||
association: { foo: 'bar', id: 3 } | ||
@@ -411,3 +317,4 @@ } | ||
{ | ||
callee: { some: 'data', id: 1 }, | ||
some: 'data', | ||
id: 1, | ||
association: [ | ||
@@ -421,63 +328,44 @@ { foo: 'bar', id: 1 }, | ||
*/ | ||
var groupDataByCalleeFactory = function(data) { | ||
var result = [] | ||
, calleeTableName = this.callee.tableName | ||
data.forEach(function(row) { | ||
var calleeData = row[calleeTableName] | ||
, existingEntry = result.filter(function(groupedRow) { | ||
return Utils._.isEqual(groupedRow[calleeTableName], calleeData) | ||
})[0] | ||
var groupJoinData = function(data, options) { | ||
var results = [] | ||
, existingResult | ||
, calleeData | ||
, child | ||
if (!existingEntry) { | ||
existingEntry = {} | ||
result.push(existingEntry) | ||
existingEntry[calleeTableName] = calleeData | ||
} | ||
data.forEach(function (row) { | ||
row = Dot.transform(row) | ||
calleeData = _.omit(row, options.includeNames) | ||
for (var attrName in row) { | ||
if (row.hasOwnProperty(attrName) && (attrName !== calleeTableName)) { | ||
existingEntry[attrName] = existingEntry[attrName] || [] | ||
var attrRowExists = existingEntry[attrName].some(function(attrRow) { | ||
return Utils._.isEqual(attrRow, row[attrName]) | ||
}) | ||
if (!attrRowExists) { | ||
existingEntry[attrName].push(row[attrName]) | ||
} | ||
existingResult = _.find(results, function (result) { | ||
if (options.includeNames) { | ||
return Utils._.isEqual(_.omit(result, options.includeNames.concat(['__children'])), calleeData) | ||
} | ||
return Utils._.isEqual(result, calleeData) | ||
}) | ||
if (!existingResult) { | ||
results.push(existingResult = calleeData) | ||
} | ||
}) | ||
return result | ||
} | ||
for (var attrName in row) { | ||
if (row.hasOwnProperty(attrName)) { | ||
child = Object(row[attrName]) === row[attrName] && options.includeMap && options.includeMap[attrName] | ||
if (child) { | ||
if (!existingResult.__children) existingResult.__children = {} | ||
if (!existingResult.__children[attrName]) existingResult.__children[attrName] = [] | ||
/** | ||
* This function will prepare the result of select queries with joins. | ||
* | ||
* @param {Array} data This array contains objects. | ||
* @return {Array} The array will have the needed format for groupDataByCalleeFactory. | ||
*/ | ||
var prepareJoinData = function(data) { | ||
var result = data.map(function(row) { | ||
var nestedRow = {} | ||
for (var key in row) { | ||
if (row.hasOwnProperty(key)) { | ||
var tableName = findTableNameInAttribute.call(this, key) | ||
if (!!tableName) { | ||
nestedRow[tableName] = nestedRow[tableName] || {} | ||
nestedRow[tableName][key.replace(tableName + '.', '')] = row[key] | ||
} else { | ||
nestedRow[this.callee.tableName] = nestedRow[this.callee.tableName] || {} | ||
nestedRow[this.callee.tableName][key] = row[key] | ||
existingResult.__children[attrName].push(row[attrName]) | ||
} | ||
} | ||
} | ||
}) | ||
return nestedRow | ||
}.bind(this)) | ||
return result | ||
results.forEach(function (result) { | ||
_.each(result.__children, function (children, key) { | ||
result[key] = groupJoinData(children, options.includeMap[key]) | ||
}) | ||
delete result.__children | ||
}) | ||
return results | ||
} | ||
@@ -484,0 +372,0 @@ |
@@ -91,2 +91,3 @@ var Utils = require("../../utils") | ||
self.emit('sql', self.sql) | ||
err.sql = sql | ||
self.emit('error', err, self.callee) | ||
@@ -93,0 +94,0 @@ }) |
@@ -53,3 +53,3 @@ var Utils = require("../../utils") | ||
if (attributes.hasOwnProperty(attr)) { | ||
var dataType = attributes[attr] | ||
var dataType = this.mysqlDataTypeMapping(tableName, attr, attributes[attr]) | ||
@@ -121,3 +121,3 @@ if (Utils._.includes(dataType, 'PRIMARY KEY')) { | ||
attrName: attrName, | ||
definition: definition | ||
definition: this.mysqlDataTypeMapping(tableName, attrName, definition) | ||
})) | ||
@@ -501,2 +501,10 @@ } | ||
return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';' | ||
}, | ||
mysqlDataTypeMapping: function(tableName, attr, dataType) { | ||
if (Utils._.includes(dataType, 'UUID')) { | ||
dataType = dataType.replace(/UUID/, 'CHAR(36) BINARY') | ||
} | ||
return dataType | ||
} | ||
@@ -503,0 +511,0 @@ } |
@@ -30,2 +30,3 @@ var Utils = require("../../utils") | ||
if (err) { | ||
err.sql = sql | ||
this.emit('error', err, this.callee) | ||
@@ -32,0 +33,0 @@ } else { |
@@ -30,2 +30,3 @@ | ||
this.pendingQueries = 0 | ||
this.clientDrained = true | ||
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50) | ||
@@ -55,2 +56,3 @@ | ||
self.pendingQueries++ | ||
self.clientDrained = false | ||
@@ -69,4 +71,2 @@ return new Utils.CustomEventEmitter(function(emitter) { | ||
done && done(err) }) | ||
.success(function(results) { self.endQuery.call(self) }) | ||
.error(function(err) { self.endQuery.call(self) }) | ||
.proxy(emitter) | ||
@@ -158,2 +158,8 @@ }) | ||
}) | ||
// Closes a client correctly even if we have backed up queries | ||
// https://github.com/brianc/node-postgres/pull/346 | ||
this.client.on('drain', function() { | ||
self.clientDrained = true | ||
}) | ||
} | ||
@@ -175,5 +181,5 @@ } | ||
if (this.client) { | ||
// Closes a client correctly even if we have backed up queries | ||
// https://github.com/brianc/node-postgres/pull/346 | ||
this.client.on('drain', this.client.end.bind(this.client)) | ||
if (this.clientDrained) { | ||
this.client.end() | ||
} | ||
this.client = null | ||
@@ -180,0 +186,0 @@ } |
@@ -259,5 +259,8 @@ var Utils = require("../../utils") | ||
insertQuery: function(tableName, attrValueHash, attributes) { | ||
var query | ||
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;" | ||
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES RETURNING *;" | ||
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) | ||
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;" | ||
// Remove serials that are null or undefined, which causes an error in PG | ||
@@ -288,2 +291,4 @@ Utils._.forEach(attrValueHash, function(value, key, hash) { | ||
query = replacements.attributes.length ? valueQuery : emptyQuery | ||
return Utils._.template(query)(replacements) | ||
@@ -295,7 +300,20 @@ }, | ||
, tuples = [] | ||
, serials = [] | ||
Utils._.forEach(attrValueHashes, function(attrValueHash) { | ||
removeSerialsFromHash(tableName, attrValueHash) | ||
Utils._.forEach(attrValueHashes, function(attrValueHash, i) { | ||
if (i === 0) { | ||
Utils._.forEach(attrValueHash, function(value, key, hash) { | ||
if (tables[tableName] && tables[tableName][key]) { | ||
if (['bigserial', 'serial'].indexOf(tables[tableName][key]) !== -1) { | ||
serials.push(key) | ||
} | ||
} | ||
}) | ||
} | ||
tuples.push("(" + | ||
Utils._.values(attrValueHash).map(function(value){ | ||
Utils._.map(attrValueHash, function(value, key){ | ||
if (serials.indexOf(key) !== -1) { | ||
return 'DEFAULT'; | ||
} | ||
return this.escape(value) | ||
@@ -441,4 +459,4 @@ }.bind(this)).join(",") + | ||
"CREATE", options.indicesType, "INDEX", this.quoteIdentifiers(options.indexName), | ||
(options.indexType ? ('USING ' + options.indexType) : undefined), | ||
"ON", this.quoteIdentifiers(tableName), '(' + transformedAttributes.join(', ') + ')' | ||
"ON", this.quoteIdentifiers(tableName), (options.indexType ? ('USING ' + options.indexType) : undefined), | ||
'(' + transformedAttributes.join(', ') + ')' | ||
]).join(' ') | ||
@@ -445,0 +463,0 @@ }, |
@@ -39,2 +39,4 @@ var Utils = require("../../utils") | ||
receivedError = true | ||
err.sql = sql | ||
self.emit('sql', sql) | ||
self.emit('error', err, self.callee) | ||
@@ -128,2 +130,3 @@ }) | ||
} | ||
this.emit('success', this.send('handleSelectQuery', rows)) | ||
@@ -144,5 +147,4 @@ } | ||
} | ||
} | ||
this.emit('success', this.callee) | ||
} | ||
this.emit('success', this.callee) | ||
} else if (this.send('isUpdateQuery')) { | ||
@@ -149,0 +151,0 @@ if(this.callee !== null) { // may happen for bulk updates |
@@ -159,3 +159,5 @@ var Utils = require("../../utils") | ||
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);"; | ||
var query | ||
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);" | ||
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES;" | ||
@@ -170,2 +172,3 @@ var replacements = { | ||
query = replacements.attributes.length ? valueQuery : emptyQuery | ||
return Utils._.template(query)(replacements) | ||
@@ -172,0 +175,0 @@ }, |
@@ -47,2 +47,3 @@ var Utils = require("../../utils") | ||
if (err) { | ||
err.sql = self.sql | ||
onFailure.call(self, err) | ||
@@ -49,0 +50,0 @@ } else { |
@@ -31,2 +31,11 @@ var util = require("util") | ||
/** | ||
Shortcut methods (success, ok) for listening for success events. | ||
Params: | ||
- fct: A function that gets executed once the *success* event was triggered. | ||
Result: | ||
The function returns the instance of the query. | ||
*/ | ||
CustomEventEmitter.prototype.success = | ||
@@ -39,2 +48,11 @@ CustomEventEmitter.prototype.ok = | ||
/** | ||
Shortcut methods (failure, fail, error) for listening for error events. | ||
Params: | ||
- fct: A function that gets executed once the *error* event was triggered. | ||
Result: | ||
The function returns the instance of the query. | ||
*/ | ||
CustomEventEmitter.prototype.failure = | ||
@@ -41,0 +59,0 @@ CustomEventEmitter.prototype.fail = |
@@ -662,3 +662,4 @@ var Utils = require(__dirname + '/utils') | ||
var sql = this.QueryGenerator.selectQuery(tableName, options, factory) | ||
queryOptions = Utils._.extend({}, queryOptions, { include: options.include }) | ||
queryOptions = Utils._.extend({}, queryOptions, { include: options.include, includeNames: options.includeNames, includeMap: options.includeMap }) | ||
return queryAndEmit.call(this, [sql, factory, queryOptions], 'select') | ||
@@ -665,0 +666,0 @@ } |
@@ -82,3 +82,3 @@ var url = require("url") | ||
sync: {}, | ||
logging: console.log, | ||
logging: false, | ||
omitNull: false, | ||
@@ -85,0 +85,0 @@ queue: true, |
@@ -1,2 +0,2 @@ | ||
Utils = require('./utils') | ||
var Utils = require('./utils') | ||
@@ -3,0 +3,0 @@ var TransactionManager = module.exports = function(sequelize) { |
var cJSON = require('circular-json') | ||
, _ = require('lodash') // Don't require Utils here, as it creates a circular dependency | ||
var ParameterValidator = module.exports = { | ||
check: function(value, expectation, options) { | ||
options = Utils._.extend({ | ||
options = _.extend({ | ||
throwError: true, | ||
@@ -7,0 +8,0 @@ deprecated: false, |
{ | ||
"name": "sequelize", | ||
"description": "Multi dialect ORM for Node.JS", | ||
"version": "1.7.0-beta.2", | ||
"version": "1.7.0-beta.5", | ||
"author": "Sascha Depold <sascha@depold.com>", | ||
@@ -67,3 +67,4 @@ "contributors": [ | ||
"istanbul": "~0.1.45", | ||
"coveralls": "~2.5.0" | ||
"coveralls": "~2.5.0", | ||
"async": "~0.2.9" | ||
}, | ||
@@ -70,0 +71,0 @@ "keywords": [ |
@@ -13,2 +13,4 @@ # Sequelize [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/sequelize/sequelize/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Build Status](https://secure.travis-ci.org/sequelize/sequelize.png)](http://travis-ci.org/sequelize/sequelize) [![Dependency Status](https://david-dm.org/sequelize/sequelize.png)](https://david-dm.org/sequelize/sequelize) [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1259407/Sequelize) # | ||
##### 2.0.0 API should be considered unstable | ||
### 1.6.0 ### | ||
@@ -41,7 +43,7 @@ | ||
You can find the documentation and announcements of updates on the [project's website](http://www.sequelizejs.com). | ||
You can find the documentation and announcements of updates on the [project's website](http://sequelizejs.com). | ||
If you want to know about latest development and releases, follow me on [Twitter](http://twitter.com/sdepold). | ||
Also make sure to take a look at the examples in the repository. The website will contain them soon, as well. | ||
- [Documentation](http://www.sequelizejs.com) | ||
- [Documentation](http://sequelizejs.com) | ||
- [Twitter](http://twitter.com/sdepold) | ||
@@ -110,4 +112,13 @@ - [IRC](http://webchat.freenode.net?channels=sequelizejs) | ||
### 2. Database... Come to me! ### | ||
### 2. Install the dependencies ### | ||
Just "cd" into sequelize directory and run `npm install`, see an example below: | ||
```console | ||
$ cd path/to/sequelize | ||
$ npm install | ||
``` | ||
### 3. Database... Come to me! ### | ||
First class citizen of Sequelize was MySQL. Over time, Sequelize began to | ||
@@ -136,12 +147,28 @@ become compatible to SQLite and PostgreSQL. In order to provide a fully | ||
#### 3a. Docker | ||
If you don't feel like setting up databases and users, you can use our [docker](http://docker.io) [image](https://index.docker.io/u/mhansen/sequelize-contribution/) for sequelize contribution. | ||
### 3. Install the dependencies ### | ||
Getting the image: | ||
```console | ||
$ sudo docker pull mhansen/sequelize-contribution | ||
``` | ||
Just "cd" into sequelize directory and run `npm install`, see an example below: | ||
Start the container and save references to container id and ip: | ||
```console | ||
$ CONTAINER=$(sudo docker run -d -i -t mhansen/sequelize-contribution) | ||
$ CONTAINER_IP=$(sudo docker inspect -format='{{.NetworkSettings.IPAddress}}' $CONTAINER) | ||
``` | ||
Run tests: | ||
```console | ||
$ cd path/to/sequelize | ||
$ npm install | ||
$ SEQ_HOST=$CONTAINER_IP SEQ_USER=sequelize_test make all | ||
``` | ||
Stop the container: | ||
```console | ||
$ sudo docker stop $CONTAINER | ||
``` | ||
When running tests repeatedly, you only need to redo step 3 if you haven't stopped the container. | ||
### 4. Run the tests ### | ||
@@ -148,0 +175,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
398497
9523
283
15