dao-factory.js 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594
  1. var Utils = require("./utils")
  2. , DAO = require("./dao")
  3. , DataTypes = require("./data-types")
  4. , Util = require('util')
  5. , sql = require('sql')
  6. , SqlString = require('./sql-string')
  7. , Transaction = require('./transaction')
  8. , QueryTypes = require('./query-types')
  9. module.exports = (function() {
  10. var DAOFactory = function(name, attributes, options) {
  11. this.options = Utils._.extend({
  12. timestamps: true,
  13. createdAt: 'createdAt',
  14. updatedAt: 'updatedAt',
  15. deletedAt: 'deletedAt',
  16. instanceMethods: {},
  17. classMethods: {},
  18. validate: {},
  19. freezeTableName: false,
  20. underscored: false,
  21. syncOnAssociation: true,
  22. paranoid: false,
  23. whereCollection: null,
  24. schema: null,
  25. schemaDelimiter: '',
  26. language: 'en',
  27. defaultScope: null,
  28. scopes: null,
  29. hooks: {
  30. beforeCreate: [],
  31. afterCreate: []
  32. }
  33. }, options || {})
  34. // error check options
  35. Utils._.each(options.validate, function(validator, validatorType) {
  36. if (Utils._.contains(Utils._.keys(attributes), validatorType)) {
  37. throw new Error("A model validator function must not have the same name as a field. Model: " + name + ", field/validation name: " + validatorType)
  38. }
  39. if (!Utils._.isFunction(validator)) {
  40. throw new Error("Members of the validate option must be functions. Model: " + name + ", error with validate member " + validatorType)
  41. }
  42. })
  43. this.name = name
  44. if (!this.options.tableName) {
  45. this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name, this.options.language)
  46. } else {
  47. this.tableName = this.options.tableName
  48. }
  49. attributes = replaceReferencesWithTableNames(attributes)
  50. this.options.hooks = this.replaceHookAliases(this.options.hooks)
  51. this.rawAttributes = attributes
  52. this.daoFactoryManager = null // defined in init function
  53. this.associations = {}
  54. this.scopeObj = {}
  55. }
  56. Object.defineProperty(DAOFactory.prototype, 'attributes', {
  57. get: function() {
  58. return this.QueryGenerator.attributesToSQL(this.rawAttributes)
  59. }
  60. })
  61. Object.defineProperty(DAOFactory.prototype, 'sequelize', {
  62. get: function() { return this.daoFactoryManager.sequelize }
  63. })
  64. Object.defineProperty(DAOFactory.prototype, 'QueryInterface', {
  65. get: function() { return this.daoFactoryManager.sequelize.getQueryInterface() }
  66. })
  67. Object.defineProperty(DAOFactory.prototype, 'QueryGenerator', {
  68. get: function() { return this.QueryInterface.QueryGenerator }
  69. })
  70. // inject the node-sql methods to the dao factory in order to
  71. // receive the syntax sugar ...
  72. ;(function() {
  73. var instance = sql.define({ name: "dummy", columns: [] })
  74. for (var methodName in instance) {
  75. ;(function(methodName) {
  76. DAOFactory.prototype[methodName] = function() {
  77. var dataset = this.dataset()
  78. , result = dataset[methodName].apply(dataset, arguments)
  79. , dialect = this.daoFactoryManager.sequelize.options.dialect
  80. , self = this
  81. result.toSql = function() {
  82. var query = result.toQuery()
  83. return SqlString.format(query.text.replace(/(\$\d)/g, '?'), query.values, null, dialect) + ';'
  84. }
  85. result.exec = function(options) {
  86. options = Utils._.extend({
  87. transaction: null,
  88. type: QueryTypes.SELECT
  89. }, options || {})
  90. return self.QueryInterface.queryAndEmit([result.toSql(), self, options], 'snafu')
  91. }
  92. return result
  93. }
  94. })(methodName)
  95. }
  96. })()
  97. DAOFactory.prototype.init = function(daoFactoryManager) {
  98. var self = this
  99. this.daoFactoryManager = daoFactoryManager
  100. this.primaryKeys = {}
  101. self.options.uniqueKeys = {}
  102. Utils._.each(this.rawAttributes, function(columnValues, columnName) {
  103. if (columnValues.hasOwnProperty('unique') && columnValues.unique !== true && columnValues.unique !== false) {
  104. var idxName = columnValues.unique
  105. if (typeof columnValues.unique === "object") {
  106. idxName = columnValues.unique.name
  107. }
  108. self.options.uniqueKeys[idxName] = self.options.uniqueKeys[idxName] || {fields: [], msg: null}
  109. self.options.uniqueKeys[idxName].fields.push(columnName)
  110. self.options.uniqueKeys[idxName].msg = self.options.uniqueKeys[idxName].msg || columnValues.unique.msg || null
  111. }
  112. })
  113. Utils._.each(this.attributes, function(dataTypeString, attributeName) {
  114. if (dataTypeString.indexOf('PRIMARY KEY') !== -1) {
  115. self.primaryKeys[attributeName] = dataTypeString
  116. }
  117. })
  118. this.primaryKeyAttributes = Object.keys(this.primaryKeys)
  119. this.primaryKeyCount = this.primaryKeyAttributes.length
  120. this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0
  121. if (typeof this.options.defaultScope === "object") {
  122. Utils.injectScope.call(this, this.options.defaultScope)
  123. }
  124. // DAO prototype
  125. // WTF ... ?
  126. this.DAO = function() {
  127. DAO.apply(this, arguments);
  128. }
  129. Util.inherits(this.DAO, DAO);
  130. this._timestampAttributes = {}
  131. if (this.options.timestamps) {
  132. if (this.options.createdAt) {
  133. this._timestampAttributes.createdAt = Utils._.underscoredIf(this.options.createdAt, this.options.underscored)
  134. }
  135. if (this.options.updatedAt) {
  136. this._timestampAttributes.updatedAt = Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)
  137. }
  138. if (this.options.paranoid && this.options.deletedAt) {
  139. this._timestampAttributes.deletedAt = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)
  140. }
  141. this.DAO.prototype._readOnlyAttributes = Object.keys(this._timestampAttributes)
  142. }
  143. this.DAO.prototype._hasReadOnlyAttributes = this.DAO.prototype._readOnlyAttributes && this.DAO.prototype._readOnlyAttributes.length
  144. this.DAO.prototype._isReadOnlyAttribute = Utils._.memoize(function (key) {
  145. return self.DAO.prototype._hasReadOnlyAttributes && self.DAO.prototype._readOnlyAttributes.indexOf(key) !== -1
  146. })
  147. addDefaultAttributes.call(this)
  148. addOptionalClassMethods.call(this)
  149. findAutoIncrementField.call(this)
  150. this.DAO.prototype.rawAttributes = this.rawAttributes;
  151. this.DAO.prototype._hasPrimaryKeys = this.options.hasPrimaryKeys
  152. this.DAO.prototype._isPrimaryKey = Utils._.memoize(function (key) {
  153. return self.primaryKeyAttributes.indexOf(key) !== -1 && key !== 'id'
  154. })
  155. if (this.options.instanceMethods) {
  156. Utils._.each(this.options.instanceMethods, function(fct, name) {
  157. self.DAO.prototype[name] = fct
  158. })
  159. }
  160. this.refreshAttributes();
  161. this.DAO.prototype.booleanValues = []
  162. this.DAO.prototype.dateAttributes = []
  163. this.DAO.prototype.defaultValues = {}
  164. this.DAO.prototype.validators = {}
  165. Utils._.each(this.rawAttributes, function (definition, name) {
  166. if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) {
  167. self.DAO.prototype.booleanValues.push(name);
  168. }
  169. if (((definition === DataTypes.DATE) || (definition.type === DataTypes.DATE) || (definition.originalType === DataTypes.DATE))) {
  170. self.DAO.prototype.dateAttributes.push(name);
  171. }
  172. if (definition.hasOwnProperty('defaultValue')) {
  173. self.DAO.prototype.defaultValues[name] = Utils._.partial(
  174. Utils.toDefaultValue, definition.defaultValue)
  175. }
  176. if (definition.hasOwnProperty('validate')) {
  177. self.DAO.prototype.validators[name] = definition.validate;
  178. }
  179. })
  180. this.DAO.prototype._hasBooleanAttributes = !!this.DAO.prototype.booleanValues.length
  181. this.DAO.prototype._isBooleanAttribute = Utils._.memoize(function (key) {
  182. return self.DAO.prototype.booleanValues.indexOf(key) !== -1
  183. })
  184. this.DAO.prototype._hasDateAttributes = !!this.DAO.prototype.dateAttributes.length
  185. this.DAO.prototype._isDateAttribute = Utils._.memoize(function (key) {
  186. return self.DAO.prototype.dateAttributes.indexOf(key) !== -1
  187. })
  188. this.DAO.prototype.__factory = this
  189. this.DAO.prototype.daoFactory = this
  190. this.DAO.prototype.Model = this
  191. this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues)
  192. this.DAO.prototype.daoFactoryName = this.name
  193. return this
  194. }
  195. DAOFactory.prototype.refreshAttributes = function() {
  196. var self = this
  197. , attributeManipulation = {};
  198. this.DAO.prototype._customGetters = {}
  199. this.DAO.prototype._customSetters = {}
  200. Utils._.each(['get', 'set'], function(type) {
  201. var opt = type + 'terMethods'
  202. , funcs = Utils._.clone(Utils._.isObject(self.options[opt]) ? self.options[opt] : {})
  203. , _custom = type === 'get' ? self.DAO.prototype._customGetters : self.DAO.prototype._customSetters
  204. Utils._.each(funcs, function (method, attribute) {
  205. _custom[attribute] = method
  206. if (type === 'get') {
  207. funcs[attribute] = function() {
  208. return this.get(attribute)
  209. }
  210. }
  211. if (type === 'set') {
  212. funcs[attribute] = function(value) {
  213. return this.set(attribute, value)
  214. }
  215. }
  216. })
  217. Utils._.each(self.rawAttributes, function(options, attribute) {
  218. if (options.hasOwnProperty(type)) {
  219. _custom[attribute] = options[type]
  220. }
  221. if (type === 'get') {
  222. funcs[attribute] = function() {
  223. return this.get(attribute)
  224. }
  225. }
  226. if (type === 'set') {
  227. funcs[attribute] = function(value) {
  228. return this.set(attribute, value)
  229. }
  230. }
  231. })
  232. Utils._.each(funcs, function(fct, name) {
  233. if (!attributeManipulation[name]) {
  234. attributeManipulation[name] = {
  235. configurable: true
  236. }
  237. }
  238. attributeManipulation[name][type] = fct
  239. })
  240. })
  241. this.DAO.prototype._hasCustomGetters = Object.keys(this.DAO.prototype._customGetters).length
  242. this.DAO.prototype._hasCustomSetters = Object.keys(this.DAO.prototype._customSetters).length
  243. Object.defineProperties(this.DAO.prototype, attributeManipulation)
  244. this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes)
  245. this.DAO.prototype._isAttribute = Utils._.memoize(function (key) {
  246. return self.DAO.prototype.attributes.indexOf(key) !== -1
  247. })
  248. }
  249. DAOFactory.prototype.sync = function(options) {
  250. options = Utils._.extend({}, this.options, options || {})
  251. var self = this
  252. return new Utils.CustomEventEmitter(function(emitter) {
  253. var doQuery = function() {
  254. self
  255. .QueryInterface
  256. .createTable(self.getTableName(), self.attributes, options)
  257. .proxy(emitter, {events: ['error', 'sql']})
  258. .success(function() { emitter.emit('success', self) })
  259. }
  260. if (options.force) {
  261. self
  262. .drop(options)
  263. .proxy(emitter, {events: ['error', 'sql']})
  264. .success(doQuery)
  265. } else {
  266. doQuery()
  267. }
  268. }).run()
  269. }
  270. DAOFactory.prototype.drop = function(options) {
  271. // Only Postgres' QueryGenerator.dropTableQuery() will add schema manually
  272. var isPostgres = this.options.dialect === "postgres" || (!!this.daoFactoryManager && this.daoFactoryManager.sequelize.options.dialect === "postgres")
  273. , tableName = isPostgres ? this.tableName : this.getTableName()
  274. return this.QueryInterface.dropTable(tableName, options)
  275. }
  276. DAOFactory.prototype.dropSchema = function(schema) {
  277. return this.QueryInterface.dropSchema(schema)
  278. }
  279. DAOFactory.prototype.schema = function(schema, options) {
  280. this.options.schema = schema
  281. if (!!options) {
  282. if (typeof options === "string") {
  283. this.options.schemaDelimiter = options
  284. } else {
  285. if (!!options.schemaDelimiter) {
  286. this.options.schemaDelimiter = options.schemaDelimiter
  287. }
  288. }
  289. }
  290. return this
  291. }
  292. DAOFactory.prototype.getTableName = function() {
  293. return this.QueryGenerator.addSchema(this)
  294. }
  295. DAOFactory.prototype.scope = function(option) {
  296. var self = Object.create(this)
  297. , type
  298. , options
  299. , merge
  300. , i
  301. , scope
  302. , scopeName
  303. , scopeOptions
  304. , argLength = arguments.length
  305. , lastArg = arguments[argLength-1]
  306. // Set defaults
  307. scopeOptions = (typeof lastArg === "object" && !Array.isArray(lastArg) ? lastArg : {}) || {} // <-- for no arguments
  308. scopeOptions.silent = (scopeOptions !== null && scopeOptions.hasOwnProperty('silent') ? scopeOptions.silent : true)
  309. // Clear out any predefined scopes...
  310. self.scopeObj = {}
  311. // Possible formats for option:
  312. // String of arguments: 'hello', 'world', 'etc'
  313. // Array: ['hello', 'world', 'etc']
  314. // Object: {merge: 'hello'}, {method: ['scopeName' [, args1, args2..]]}, {merge: true, method: ...}
  315. if (argLength < 1 || !option) {
  316. return self
  317. }
  318. for (i = 0; i < argLength; i++) {
  319. options = Array.isArray(arguments[i]) ? arguments[i] : [arguments[i]]
  320. options.forEach(function(o){
  321. type = typeof o
  322. scope = null
  323. merge = false
  324. scopeName = null
  325. if (type === "object") {
  326. // Right now we only support a merge functionality for objects
  327. if (!!o.merge) {
  328. merge = true
  329. scopeName = o.merge[0]
  330. if (Array.isArray(o.merge) && !!self.options.scopes[scopeName]) {
  331. scope = self.options.scopes[scopeName].apply(self, o.merge.splice(1))
  332. }
  333. else if (typeof o.merge === "string") {
  334. scopeName = o.merge
  335. scope = self.options.scopes[scopeName]
  336. }
  337. }
  338. if (!!o.method) {
  339. if (Array.isArray(o.method) && !!self.options.scopes[o.method[0]]) {
  340. scopeName = o.method[0]
  341. scope = self.options.scopes[scopeName].apply(self, o.method.splice(1))
  342. merge = !!o.merge
  343. }
  344. else if (!!self.options.scopes[o.method]) {
  345. scopeName = o.method
  346. scope = self.options.scopes[scopeName].apply(self)
  347. }
  348. } else {
  349. scopeName = o
  350. scope = self.options.scopes[scopeName]
  351. }
  352. } else {
  353. scopeName = o
  354. scope = self.options.scopes[scopeName]
  355. }
  356. if (!!scope) {
  357. Utils.injectScope.call(self, scope, merge)
  358. }
  359. else if (scopeOptions.silent !== true && !!scopeName) {
  360. throw new Error("Invalid scope " + scopeName + " called.")
  361. }
  362. })
  363. }
  364. return self
  365. }
  366. // alias for findAll
  367. DAOFactory.prototype.all = function(options, queryOptions) {
  368. return this.findAll(options, queryOptions)
  369. }
  370. DAOFactory.prototype.findAll = function(options, queryOptions) {
  371. var hasJoin = false
  372. , tableNames = { }
  373. tableNames[this.tableName] = true
  374. options = optClone(options)
  375. if (typeof options === 'object') {
  376. if (options.hasOwnProperty('include') && options.include) {
  377. hasJoin = true
  378. validateIncludedElements.call(this, options, tableNames)
  379. }
  380. // whereCollection is used for non-primary key updates
  381. this.options.whereCollection = options.where || null
  382. }
  383. options = paranoidClause.call(this, options)
  384. return this.QueryInterface.select(this, this.getTableName(), options, Utils._.defaults({
  385. type: QueryTypes.SELECT,
  386. hasJoin: hasJoin,
  387. tableNames: Object.keys(tableNames)
  388. }, queryOptions, { transaction: (options || {}).transaction }))
  389. }
  390. //right now, the caller (has-many-double-linked) is in charge of the where clause
  391. DAOFactory.prototype.findAllJoin = function(joinTableName, options, queryOptions) {
  392. var optcpy = Utils._.clone(options)
  393. optcpy.attributes = optcpy.attributes || [this.QueryInterface.quoteIdentifier(this.tableName)+".*"]
  394. // whereCollection is used for non-primary key updates
  395. this.options.whereCollection = optcpy.where || null;
  396. return this.QueryInterface.select(this, [this.getTableName(), joinTableName], optcpy, Utils._.defaults({
  397. type: QueryTypes.SELECT
  398. }, queryOptions, { transaction: (options || {}).transaction }))
  399. }
  400. /**
  401. * Search for an instance.
  402. *
  403. * @param {Object} options Options to describe the scope of the search.
  404. * @param {Array} include A list of associations which shall get eagerly loaded. Supported is either { include: [ DaoFactory1, DaoFactory2, ...] } or { include: [ { daoFactory: DaoFactory1, as: 'Alias' } ] }.
  405. * @param {Object} set the query options, e.g. raw, specifying that you want raw data instead of built DAOs
  406. * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`.
  407. */
  408. DAOFactory.prototype.find = function(options, queryOptions) {
  409. var hasJoin = false
  410. // no options defined?
  411. // return an emitter which emits null
  412. if ([null, undefined].indexOf(options) !== -1) {
  413. return new Utils.CustomEventEmitter(function(emitter) {
  414. setTimeout(function() { emitter.emit('success', null) }, 10)
  415. }).run()
  416. }
  417. var primaryKeys = this.primaryKeys
  418. , keys = Object.keys(primaryKeys)
  419. , keysLength = keys.length
  420. , tableNames = { }
  421. tableNames[this.tableName] = true
  422. // options is not a hash but an id
  423. if (typeof options === 'number') {
  424. var oldOption = options
  425. options = { where: {} }
  426. if (keysLength === 1) {
  427. options.where[keys[0]] = oldOption
  428. } else {
  429. options.where.id = oldOption
  430. }
  431. } else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) {
  432. var where = {}
  433. Utils._.each(arguments, function(arg, i) {
  434. var key = keys[i]
  435. where[key] = arg
  436. })
  437. options = { where: where }
  438. } else if (typeof options === 'string' && parseInt(options, 10).toString() === options) {
  439. var parsedId = parseInt(options, 10)
  440. if (!Utils._.isFinite(parsedId)) {
  441. throw new Error('Invalid argument to find(). Must be an id or an options object.')
  442. }
  443. options = { where: parsedId }
  444. } else if (typeof options === 'object') {
  445. options = Utils._.clone(options, function(thing) {
  446. if (Buffer.isBuffer(thing)) { return thing }
  447. return undefined;
  448. })
  449. if (options.hasOwnProperty('include') && options.include) {
  450. hasJoin = true
  451. validateIncludedElements.call(this, options, tableNames)
  452. }
  453. // whereCollection is used for non-primary key updates
  454. this.options.whereCollection = options.where || null
  455. } else if (typeof options === "string") {
  456. var where = {}
  457. if (this.primaryKeyCount === 1) {
  458. where[primaryKeys[keys[0]]] = options;
  459. options = where;
  460. } else if (this.primaryKeyCount < 1) {
  461. // Revert to default behavior which is {where: [int]}
  462. options = {where: parseInt(Number(options) || 0, 0)}
  463. }
  464. }
  465. options = paranoidClause.call(this, options)
  466. if (options.limit === undefined) {
  467. options.limit = 1
  468. }
  469. return this.QueryInterface.select(this, this.getTableName(), options, Utils._.defaults({
  470. plain: true,
  471. type: QueryTypes.SELECT,
  472. hasJoin: hasJoin,
  473. tableNames: Object.keys(tableNames)
  474. }, queryOptions, { transaction: (options || {}).transaction }))
  475. }
  476. DAOFactory.prototype.aggregate = function(field, aggregateFunction, options) {
  477. var tableField;
  478. if (field == '*') {
  479. tableField = field
  480. } else {
  481. tableField = this.QueryInterface.QueryGenerator.quoteIdentifier(field)
  482. }
  483. options = Utils._.extend({ attributes: [] }, options || {})
  484. options.attributes.push([aggregateFunction + '(' + tableField + ')', aggregateFunction])
  485. if (!options.dataType) {
  486. if (this.rawAttributes[field]) {
  487. options.dataType = this.rawAttributes[field]
  488. } else {
  489. // Use FLOAT as fallback
  490. options.dataType = DataTypes.FLOAT
  491. }
  492. }
  493. options = paranoidClause.call(this, options)
  494. return this.QueryInterface.rawSelect(this.getTableName(), options, aggregateFunction)
  495. }
  496. DAOFactory.prototype.count = function(options) {
  497. options = Utils._.clone(options || {})
  498. return new Utils.CustomEventEmitter(function (emitter) {
  499. var col = this.sequelize.col('*')
  500. if (options.include) {
  501. col = this.sequelize.col(this.tableName+'.'+(this.primaryKeyAttributes[0] || 'id'))
  502. }
  503. options.attributes = [
  504. [this.sequelize.fn('COUNT', col), 'count']
  505. ]
  506. options.includeIgnoreAttributes = false
  507. options.limit = null
  508. this.find(options, {raw: true, transaction: options.transaction}).proxy(emitter, {events: ['sql', 'error']}).success(function (result) {
  509. var count = (result && result.count) ? parseInt(result.count, 10) : 0
  510. emitter.emit('success', count)
  511. })
  512. }.bind(this)).run()
  513. }
  514. DAOFactory.prototype.findAndCountAll = function(findOptions, queryOptions) {
  515. var self = this
  516. // no limit, offset, order, attributes for the options given to count()
  517. , countOptions = Utils._.omit(findOptions ? Utils._.merge({}, findOptions) : {}, ['offset', 'limit', 'order', 'attributes'])
  518. return new Utils.CustomEventEmitter(function (emitter) {
  519. var emit = {
  520. okay : function(count, results) { // emit success
  521. emitter.emit('success', {
  522. count: count || 0,
  523. rows : (results && Array.isArray(results) ? results : [])
  524. })
  525. }
  526. }
  527. self.count(countOptions)
  528. .proxy(emitter, {events: ['sql', 'error']})
  529. .success(function(count) {
  530. if (count === 0) {
  531. return emit.okay(count) // no records, no need for another query
  532. }
  533. self.findAll(findOptions, queryOptions)
  534. .proxy(emitter, {events: ['sql', 'error']})
  535. .success(function(results) {
  536. emit.okay(count, results)
  537. })
  538. })
  539. }).run()
  540. }
  541. DAOFactory.prototype.max = function(field, options) {
  542. return this.aggregate(field, 'max', options)
  543. }
  544. DAOFactory.prototype.min = function(field, options) {
  545. return this.aggregate(field, 'min', options)
  546. }
  547. DAOFactory.prototype.sum = function(field, options) {
  548. return this.aggregate(field, 'sum', options)
  549. }
  550. DAOFactory.prototype.build = function(values, options) {
  551. options = options || { isNewRecord: true, isDirty: true }
  552. if (options.hasOwnProperty('include') && options.include && !options.includeValidated) {
  553. validateIncludedElements.call(this, options)
  554. }
  555. return new this.DAO(values, options)
  556. }
  557. DAOFactory.prototype.create = function(values, fieldsOrOptions) {
  558. Utils.validateParameter(values, Object, { optional: true })
  559. Utils.validateParameter(fieldsOrOptions, Object, { deprecated: Array, optional: true, index: 2, method: 'DAOFactory#create' })
  560. if (fieldsOrOptions instanceof Array) {
  561. fieldsOrOptions = { fields: fieldsOrOptions }
  562. }
  563. fieldsOrOptions = Utils._.extend({
  564. transaction: null
  565. }, fieldsOrOptions || {})
  566. return this.build(values).save(fieldsOrOptions)
  567. }
  568. DAOFactory.prototype.findOrInitialize = DAOFactory.prototype.findOrBuild = function (params, defaults, options) {
  569. defaults = defaults || {}
  570. options = options || {}
  571. var self = this
  572. , defaultKeys = Object.keys(defaults)
  573. , defaultLength = defaultKeys.length
  574. if (!options.transaction && defaults.transaction && (defaults.transaction instanceof Transaction)) {
  575. options.transaction = defaults.transaction
  576. delete defaults.transaction
  577. }
  578. return new Utils.CustomEventEmitter(function (emitter) {
  579. self.find({
  580. where: params
  581. }, options).success(function (instance) {
  582. if (instance === null) {
  583. var i = 0
  584. for (i = 0; i < defaultLength; i++) {
  585. params[defaultKeys[i]] = defaults[defaultKeys[i]]
  586. }
  587. var build = self.build(params)
  588. build.hookValidate({skip: Object.keys(params)}).success(function (instance) {
  589. emitter.emit('success', build, true)
  590. })
  591. .error(function (error) {
  592. emitter.emit('error', error)
  593. })
  594. } else {
  595. emitter.emit('success', instance, false)
  596. }
  597. }).error(function (error) {
  598. emitter.emit('error', error)
  599. })
  600. }).run()
  601. }
  602. DAOFactory.prototype.findOrCreate = function (where, defaults, options) {
  603. var self = this
  604. , values = {}
  605. options = Utils._.extend({
  606. transaction: null
  607. }, options || {})
  608. if (!(where instanceof Utils.or) && !(where instanceof Utils.and) && !Array.isArray(where)) {
  609. for (var attrname in where) {
  610. values[attrname] = where[attrname]
  611. }
  612. }
  613. return new Utils.CustomEventEmitter(function (emitter) {
  614. self.find({
  615. where: where
  616. }, {
  617. transaction: options.transaction
  618. }).success(function (instance) {
  619. if (instance === null) {
  620. for (var attrname in defaults) {
  621. values[attrname] = defaults[attrname]
  622. }
  623. self
  624. .create(values, options)
  625. .success(function (instance) {
  626. emitter.emit('success', instance, true)
  627. })
  628. .error( function (error) {
  629. emitter.emit('error', error)
  630. })
  631. } else {
  632. emitter.emit('success', instance, false)
  633. }
  634. }).error(function (error) {
  635. emitter.emit('error', error)
  636. });
  637. }).run()
  638. }
  639. /**
  640. * Create and insert multiple instances
  641. *
  642. * @param {Array} records List of objects (key/value pairs) to create instances from
  643. * @param {Array} fields Fields to insert (defaults to all fields)
  644. * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`.
  645. *
  646. * Note: the `success` handler is not passed any arguments. To obtain DAOs for
  647. * the newly created values, you will need to query for them again. This is
  648. * because MySQL and SQLite do not make it easy to obtain back automatically
  649. * generated IDs and other default values in a way that can be mapped to
  650. * multiple records
  651. */
  652. DAOFactory.prototype.bulkCreate = function(records, fieldsOrOptions, options) {
  653. Utils.validateParameter(fieldsOrOptions, Object, { deprecated: Array, optional: true, index: 2, method: 'DAOFactory#bulkCreate' })
  654. Utils.validateParameter(options, 'undefined', { deprecated: Object, optional: true, index: 3, method: 'DAOFactory#bulkCreate' })
  655. if (!records.length) {
  656. return new Utils.CustomEventEmitter(function(emitter) {
  657. emitter.emit('success', [])
  658. }).run();
  659. }
  660. options = Utils._.extend({
  661. validate: false,
  662. hooks: false,
  663. ignoreDuplicates: false
  664. }, options || {})
  665. if (fieldsOrOptions instanceof Array) {
  666. options.fields = fieldsOrOptions
  667. } else {
  668. options.fields = options.fields || []
  669. options = Utils._.extend(options, fieldsOrOptions)
  670. }
  671. if(this.daoFactoryManager.sequelize.options.dialect === 'postgres' && options.ignoreDuplicates ) {
  672. return new Utils.CustomEventEmitter(function(emitter) {
  673. emitter.emit('error', new Error('Postgres does not support the \'ignoreDuplicates\' option.'))
  674. }).run();
  675. }
  676. var self = this
  677. , updatedAtAttr = this._timestampAttributes.updatedAt
  678. , createdAtAttr = this._timestampAttributes.createdAt
  679. , errors = []
  680. , daos = records.map(function(v) { return self.build(v) })
  681. return new Utils.CustomEventEmitter(function(emitter) {
  682. var done = function() {
  683. self.runHooks('afterBulkCreate', daos, options.fields, function(err, newRecords, newFields) {
  684. if (!!err) {
  685. return emitter.emit('error', err)
  686. }
  687. daos = newRecords || daos
  688. options.fields = newFields || options.fields
  689. emitter.emit('success', daos, options.fields)
  690. })
  691. }
  692. var next = function() {
  693. if (options.hooks === false) {
  694. return runQuery()
  695. }
  696. var i = 0
  697. var iterate = function(i) {
  698. self.runHooks('beforeCreate', daos[i], function(err, newValues) {
  699. if (!!err) {
  700. return emitter.emit('error', err)
  701. }
  702. daos[i] = newValues || daos[i]
  703. daos[i].save({ transaction: options.transaction }).error(function(err) {
  704. emitter.emit('error', err)
  705. }).success(function() {
  706. self.runHooks('afterCreate', daos[i], function(err, newValues) {
  707. if (!!err) {
  708. return emitter.emit('error', err)
  709. }
  710. daos[i] = newValues || daos[i]
  711. i++
  712. if (i >= daos.length) {
  713. return done()
  714. }
  715. iterate(i)
  716. })
  717. })
  718. })
  719. }
  720. iterate(i)
  721. }
  722. var runQuery = function() {
  723. // we will re-create from DAOs, which may have set up default attributes
  724. records = []
  725. daos.forEach(function(dao) {
  726. var values = options.fields.length > 0 ? {} : dao.dataValues
  727. options.fields.forEach(function(field) {
  728. values[field] = dao.dataValues[field]
  729. })
  730. if (createdAtAttr && !values[createdAtAttr]) {
  731. values[createdAtAttr] = Utils.now(self.daoFactoryManager.sequelize.options.dialect)
  732. }
  733. if (updatedAtAttr && !values[updatedAtAttr]) {
  734. values[updatedAtAttr] = Utils.now(self.daoFactoryManager.sequelize.options.dialect)
  735. }
  736. records.push(values)
  737. })
  738. self.QueryInterface.bulkInsert(self.tableName, records, options)
  739. .on('sql', function(sql) {
  740. emitter.emit('sql', sql)
  741. })
  742. .error(function(err) {
  743. emitter.emit('error', err)
  744. }).success(function(rows) {
  745. done()
  746. })
  747. }
  748. self.runHooks('beforeBulkCreate', daos, options.fields, function(err, newRecords, newFields) {
  749. if (!!err) {
  750. return emitter.emit('error', err)
  751. }
  752. daos = newRecords || daos
  753. options.fields = newFields || options.fields
  754. if (options.validate === true) {
  755. if (options.fields.length) {
  756. var skippedFields = Utils._.difference(Object.keys(self.attributes), options.fields);
  757. }
  758. if (options.hooks === true) {
  759. var iterate = function(i) {
  760. daos[i].hookValidate({skip: skippedFields}).error(function(err) {
  761. errors[errors.length] = {record: v, errors: err}
  762. i++
  763. if (i > daos.length) {
  764. if (errors.length > 0) {
  765. return emitter.emit('error', errors)
  766. }
  767. return next()
  768. }
  769. iterate(i)
  770. })
  771. }
  772. } else {
  773. daos.forEach(function(v) {
  774. var valid = v.validate({skip: skippedFields})
  775. if (valid !== null) {
  776. errors[errors.length] = {record: v, errors: valid}
  777. }
  778. })
  779. if (errors.length > 0) {
  780. return emitter.emit('error', errors)
  781. }
  782. next()
  783. }
  784. } else {
  785. next()
  786. }
  787. })
  788. }).run()
  789. }
  790. /**
  791. * Delete multiple instances
  792. *
  793. * @param {Object} where Options to describe the scope of the search.
  794. * @param {Object} options Possible options are:
  795. - hooks: If set to true, destroy will find all records within the where parameter and will execute before/afterDestroy hooks on each row
  796. - limit: How many rows to delete
  797. - truncate: If set to true, dialects that support it will use TRUNCATE instead of DELETE FROM. If a table is truncated the where and limit options are ignored
  798. * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`.
  799. */
  800. DAOFactory.prototype.destroy = function(where, options) {
  801. options = options || {}
  802. options.force = options.force === undefined ? false : Boolean(options.force)
  803. options.type = QueryTypes.BULKDELETE
  804. var self = this
  805. , query = null
  806. , args = []
  807. return new Utils.CustomEventEmitter(function(emitter) {
  808. self.runHooks(self.options.hooks.beforeBulkDestroy, where, function(err, newWhere) {
  809. if (!!err) {
  810. return emitter.emit('error', err)
  811. }
  812. where = newWhere || where
  813. if (self._timestampAttributes.deletedAt && options.force === false) {
  814. var attrValueHash = {}
  815. attrValueHash[self._timestampAttributes.deletedAt] = Utils.now()
  816. query = 'bulkUpdate'
  817. args = [self.tableName, attrValueHash, where]
  818. } else {
  819. query = 'bulkDelete'
  820. args = [self.tableName, where, options]
  821. }
  822. var runQuery = function(err, records) {
  823. if (!!err) {
  824. return emitter.emit('error', err)
  825. }
  826. query = self.QueryInterface[query].apply(self.QueryInterface, args)
  827. query.on('sql', function(sql) {
  828. emitter.emit('sql', sql)
  829. })
  830. .error(function(err) {
  831. emitter.emit('error', err)
  832. })
  833. .success(function(results) {
  834. var finished = function(err) {
  835. if (!!err) {
  836. return emitter.emit('error', err)
  837. }
  838. self.runHooks(self.options.hooks.afterBulkDestroy, where, function(err) {
  839. if (!!err) {
  840. return emitter.emit('error', err)
  841. }
  842. emitter.emit('success', results)
  843. })
  844. }
  845. if (options && options.hooks === true) {
  846. var tick = 0
  847. var next = function(i) {
  848. self.runHooks(self.options.hooks.afterDestroy, records[i], function(err, newValues) {
  849. if (!!err) {
  850. return finished(err)
  851. }
  852. records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
  853. tick++
  854. if (tick >= records.length) {
  855. return finished()
  856. }
  857. next(tick)
  858. })
  859. }
  860. next(tick)
  861. } else {
  862. finished()
  863. }
  864. })
  865. }
  866. if (options && options.hooks === true) {
  867. var tick = 0
  868. self.all({where: where}).error(function(err) { emitter.emit('error', err) })
  869. .success(function(records) {
  870. var next = function(i) {
  871. self.runHooks(self.options.hooks.beforeDestroy, records[i], function(err, newValues) {
  872. if (!!err) {
  873. return runQuery(err)
  874. }
  875. records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
  876. tick++
  877. if (tick >= records.length) {
  878. return runQuery(null, records)
  879. }
  880. next(tick)
  881. })
  882. }
  883. next(tick)
  884. })
  885. //
  886. } else {
  887. runQuery()
  888. }
  889. })
  890. }).run()
  891. }
  892. /**
  893. * Update multiple instances
  894. *
  895. * @param {Object} attrValueHash A hash of fields to change and their new values
  896. * @param {Object} where Options to describe the scope of the search.
  897. * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`.
  898. */
  899. DAOFactory.prototype.update = function(attrValueHash, where, options) {
  900. var self = this
  901. , query = null
  902. , tick = 0
  903. options = options || {}
  904. options.validate = options.validate === undefined ? true : Boolean(options.validate)
  905. options.hooks = options.hooks === undefined ? false : Boolean(options.hooks)
  906. options.type = QueryTypes.BULKUPDATE
  907. if (self._timestampAttributes.updatedAt) {
  908. attrValueHash[self._timestampAttributes.updatedAt] = Utils.now()
  909. }
  910. return new Utils.CustomEventEmitter(function(emitter) {
  911. var runSave = function() {
  912. self.runHooks(self.options.hooks.beforeBulkUpdate, attrValueHash, where, function(err, attributes, _where) {
  913. if (!!err) {
  914. return emitter.emit('error', err)
  915. }
  916. where = _where || where
  917. attrValueHash = attributes || attrValueHash
  918. var runQuery = function(err, records) {
  919. if (!!err) {
  920. return emitter.emit('error', err)
  921. }
  922. query = self.QueryInterface.bulkUpdate(self.tableName, attrValueHash, where, options)
  923. query.on('sql', function(sql) {
  924. emitter.emit('sql', sql)
  925. })
  926. .error(function(err) {
  927. emitter.emit('error', err)
  928. })
  929. .success(function(results) {
  930. var finished = function(err, records) {
  931. if (!!err) {
  932. return emitter.emit('error', err)
  933. }
  934. self.runHooks(self.options.hooks.afterBulkUpdate, attrValueHash, where, function(err) {
  935. if (!!err) {
  936. return emitter.emit('error', err)
  937. }
  938. emitter.emit('success', records)
  939. })
  940. }
  941. if (options && options.hooks === true && !!records && records.length > 0) {
  942. var tick = 0
  943. var next = function(i) {
  944. self.runHooks(self.options.hooks.afterUpdate, records[i], function(err, newValues) {
  945. if (!!err) {
  946. return finished(err)
  947. }
  948. records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
  949. tick++
  950. if (tick >= records.length) {
  951. return finished(null, records)
  952. }
  953. next(tick)
  954. })
  955. }
  956. next(tick)
  957. } else {
  958. finished(null, results)
  959. }
  960. })
  961. }
  962. if (options.hooks === true) {
  963. self.all({where: where}).error(function(err) { emitter.emit('error', err) })
  964. .success(function(records) {
  965. if (records === null || records.length < 1) {
  966. return runQuery(null)
  967. }
  968. var next = function(i) {
  969. self.runHooks(self.options.hooks.beforeUpdate, records[i], function(err, newValues) {
  970. if (!!err) {
  971. return runQuery(err)
  972. }
  973. records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
  974. tick++
  975. if (tick >= records.length) {
  976. return runQuery(null, records)
  977. }
  978. next(tick)
  979. })
  980. }
  981. next(tick)
  982. })
  983. } else {
  984. runQuery()
  985. }
  986. })
  987. }
  988. if (options.validate === true) {
  989. var build = self.build(attrValueHash)
  990. // We want to skip validations for all other fields
  991. var updatedFields = Object.keys(attrValueHash)
  992. var skippedFields = Utils._.difference(Object.keys(self.attributes), updatedFields)
  993. build.hookValidate({skip: skippedFields}).error(function(err) {
  994. emitter.emit('error', err)
  995. }).success(function(attributes) {
  996. if (!!attributes && !!attributes.dataValues) {
  997. attrValueHash = Utils._.pick.apply(Utils._, [].concat(attributes.dataValues).concat(Object.keys(attrValueHash)))
  998. }
  999. runSave()
  1000. })
  1001. } else {
  1002. runSave()
  1003. }
  1004. }).run()
  1005. }
  1006. DAOFactory.prototype.describe = function(schema) {
  1007. return this.QueryInterface.describeTable(this.tableName, schema || this.options.schema || undefined)
  1008. }
  1009. DAOFactory.prototype.dataset = function() {
  1010. if (!this.__sql) {
  1011. this.__setSqlDialect()
  1012. }
  1013. var instance = this.__sql.define({ name: this.tableName, columns: [] })
  1014. , attributes = this.attributes
  1015. Object.keys(attributes).forEach(function(key) {
  1016. instance.addColumn(key, attributes[key])
  1017. })
  1018. return instance
  1019. }
  1020. DAOFactory.prototype.__setSqlDialect = function() {
  1021. var dialect = this.daoFactoryManager.sequelize.options.dialect
  1022. this.__sql = sql.setDialect(dialect === 'mariadb' ? 'mysql' : dialect)
  1023. }
  1024. // private
  1025. var paranoidClause = function(options) {
  1026. if (this.options.paranoid === true) {
  1027. options = options || {}
  1028. options.where = options.where || {}
  1029. var deletedAtCol = this._timestampAttributes.deletedAt
  1030. , quoteIdentifiedDeletedAtCol = this.QueryInterface.quoteIdentifier(deletedAtCol)
  1031. // Don't overwrite our explicit deletedAt search value if we provide one
  1032. if (!!options.where[deletedAtCol]) {
  1033. return options
  1034. }
  1035. if(this.tableName) {
  1036. quoteIdentifiedDeletedAtCol = this.QueryInterface.quoteIdentifier(this.tableName) + '.' + quoteIdentifiedDeletedAtCol
  1037. }
  1038. if (typeof options.where === "string") {
  1039. options.where += ' AND ' + quoteIdentifiedDeletedAtCol + ' IS NULL '
  1040. }
  1041. else if (Array.isArray(options.where)) {
  1042. // Don't overwrite our explicit deletedAt search value if we provide one
  1043. if(options.where[0].indexOf(deletedAtCol) !== -1) {
  1044. return options
  1045. }
  1046. options.where[0] += ' AND ' + quoteIdentifiedDeletedAtCol + ' IS NULL '
  1047. } else {
  1048. options.where[deletedAtCol] = null
  1049. }
  1050. }
  1051. return options
  1052. }
  1053. var addOptionalClassMethods = function() {
  1054. var self = this
  1055. Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
  1056. }
  1057. var addDefaultAttributes = function() {
  1058. var self = this
  1059. , tail = {}
  1060. , head = {
  1061. id: {
  1062. type: DataTypes.INTEGER,
  1063. allowNull: false,
  1064. primaryKey: true,
  1065. autoIncrement: true,
  1066. _autoGenerated: true
  1067. }
  1068. }
  1069. if (this.hasPrimaryKeys) {
  1070. head = {}
  1071. }
  1072. if (this._timestampAttributes.createdAt) {
  1073. tail[this._timestampAttributes.createdAt] = {type: DataTypes.DATE, allowNull: false}
  1074. }
  1075. if (this._timestampAttributes.updatedAt) {
  1076. tail[this._timestampAttributes.updatedAt] = {type: DataTypes.DATE, allowNull: false}
  1077. }
  1078. if (this._timestampAttributes.deletedAt) {
  1079. tail[this._timestampAttributes.deletedAt] = {type: DataTypes.DATE}
  1080. }
  1081. var existingAttributes = Utils._.clone(self.rawAttributes)
  1082. self.rawAttributes = {}
  1083. Utils._.each(head, function(value, attr) {
  1084. self.rawAttributes[attr] = value
  1085. })
  1086. Utils._.each(existingAttributes, function(value, attr) {
  1087. self.rawAttributes[attr] = value
  1088. })
  1089. Utils._.each(tail, function(value, attr) {
  1090. if (Utils._.isUndefined(self.rawAttributes[attr])) {
  1091. self.rawAttributes[attr] = value
  1092. }
  1093. })
  1094. }
  1095. var findAutoIncrementField = function() {
  1096. var fields = this.QueryGenerator.findAutoIncrementField(this)
  1097. this.autoIncrementField = null
  1098. fields.forEach(function(field) {
  1099. if (this.autoIncrementField) {
  1100. throw new Error('Invalid DAO definition. Only one autoincrement field allowed.')
  1101. } else {
  1102. this.autoIncrementField = field
  1103. }
  1104. }.bind(this))
  1105. }
  1106. var validateIncludedElements = function(options, tableNames) {
  1107. tableNames = tableNames || {}
  1108. options.includeNames = []
  1109. options.includeMap = {}
  1110. options.hasSingleAssociation = false
  1111. options.hasMultiAssociation = false
  1112. // if include is not an array, wrap in an array
  1113. if (!Array.isArray(options.include)) {
  1114. options.include = [options.include]
  1115. }
  1116. // convert all included elements to { daoFactory: Model } form
  1117. var includes = options.include = options.include.map(function(include) {
  1118. if (include instanceof DAOFactory) {
  1119. return { daoFactory: include }
  1120. } else if (typeof include !== 'object') {
  1121. throw new Error('Include unexpected. Element has to be either an instance of DAOFactory or an object.')
  1122. } else if (include.hasOwnProperty('model')) {
  1123. include.daoFactory = include.model
  1124. delete include.model
  1125. }
  1126. return include
  1127. })
  1128. // validate all included elements
  1129. for (var index = 0; index < includes.length; index++) {
  1130. var include = includes[index]
  1131. if (include.all) {
  1132. includes.splice(index, 1)
  1133. index--
  1134. validateIncludedAllElement.call(this, includes, include)
  1135. continue
  1136. }
  1137. include = includes[index] = validateIncludedElement.call(this, include, tableNames)
  1138. include.parent = options
  1139. // associations that are required or have a required child as is not a ?:M association are candidates for the subquery
  1140. include.subQuery = !include.association.isMultiAssociation && (include.hasIncludeRequired || include.required)
  1141. include.hasParentWhere = options.hasParentWhere || !!options.where
  1142. include.hasParentRequired = options.hasParentRequired || !!options.required
  1143. options.includeMap[include.as] = include
  1144. options.includeNames.push(include.as)
  1145. options.includeNames.push(include.as.substr(0,1).toLowerCase() + include.as.substr(1))
  1146. if (include.association.isMultiAssociation || include.hasMultiAssociation) options.hasMultiAssociation = true
  1147. if (include.association.isSingleAssociation || include.hasSingleAssociation) options.hasSingleAssociation = true
  1148. options.hasIncludeWhere = options.hasIncludeWhere || include.hasIncludeWhere || !!include.where
  1149. options.hasIncludeRequired = options.hasIncludeRequired || include.hasIncludeRequired || !!include.required
  1150. }
  1151. }
  1152. var validateIncludedElement = function(include, tableNames) {
  1153. if (!include.hasOwnProperty('daoFactory')) {
  1154. throw new Error('Include malformed. Expected attributes: daoFactory, as!')
  1155. }
  1156. tableNames[include.daoFactory.tableName] = true
  1157. if (include.hasOwnProperty('attributes')) {
  1158. var primaryKeys;
  1159. if (include.daoFactory.hasPrimaryKeys) {
  1160. primaryKeys = []
  1161. for (var field_name in include.daoFactory.primaryKeys) {
  1162. primaryKeys.push(field_name)
  1163. }
  1164. } else {
  1165. primaryKeys = ['id']
  1166. }
  1167. include.attributes = include.attributes.concat(primaryKeys)
  1168. } else {
  1169. include.attributes = Object.keys(include.daoFactory.attributes)
  1170. }
  1171. // pseudo include just needed the attribute logic, return
  1172. if (include._pseudo) return include
  1173. // check if the current daoFactory is actually associated with the passed daoFactory - or it's a pseudo include
  1174. var association = this.getAssociation(include.daoFactory, include.as)
  1175. if (association) {
  1176. include.association = association
  1177. include.as = association.as
  1178. // If through, we create a pseudo child include, to ease our parsing later on
  1179. if (Object(include.association.through) === include.association.through) {
  1180. if (!include.include) include.include = []
  1181. var through = include.association.through
  1182. include.through = {
  1183. daoFactory: through,
  1184. as: Utils.singularize(through.tableName, through.options.language),
  1185. association: {
  1186. isSingleAssociation: true
  1187. },
  1188. _pseudo: true
  1189. }
  1190. include.include.push(include.through)
  1191. tableNames[through.tableName] = true
  1192. }
  1193. if (include.required === undefined) {
  1194. include.required = !!include.where
  1195. }
  1196. // Validate child includes
  1197. if (include.hasOwnProperty('include')) {
  1198. validateIncludedElements.call(include.daoFactory, include, tableNames)
  1199. }
  1200. return include
  1201. } else {
  1202. var msg = include.daoFactory.name
  1203. if (include.as) {
  1204. msg += " (" + include.as + ")"
  1205. }
  1206. msg += " is not associated to " + this.name + "!"
  1207. throw new Error(msg)
  1208. }
  1209. }
  1210. var validateIncludedAllElement = function(includes, include) {
  1211. // check 'all' attribute provided is valid
  1212. var all = include.all
  1213. delete include.all
  1214. if (all !== true) {
  1215. if (!Array.isArray(all)) {
  1216. all = [all]
  1217. }
  1218. var validTypes = {
  1219. BelongsTo: true,
  1220. HasOne: true,
  1221. HasMany: true,
  1222. One: ['BelongsTo', 'HasOne'],
  1223. Has: ['HasOne', 'HasMany'],
  1224. Many: ['HasMany']
  1225. }
  1226. for (var i = 0; i < all.length; i++) {
  1227. var type = all[i]
  1228. if (type == 'All') {
  1229. all = true
  1230. break
  1231. }
  1232. var types = validTypes[type]
  1233. if (!types) {
  1234. throw new Error('include all \'' + type + '\' is not valid - must be BelongsTo, HasOne, HasMany, One, Has, Many or All')
  1235. }
  1236. if (types !== true) {
  1237. // replace type placeholder e.g. 'One' with it's constituent types e.g. 'HasOne', 'BelongsTo'
  1238. all.splice(i, 1)
  1239. i--
  1240. for (var j = 0; j < types.length; j++) {
  1241. if (all.indexOf(types[j]) == -1) {
  1242. all.unshift(types[j])
  1243. i++
  1244. }
  1245. }
  1246. }
  1247. }
  1248. }
  1249. // add all associations of types specified to includes
  1250. var nested = include.nested
  1251. if (nested) {
  1252. delete include.nested
  1253. if (!include.include) {
  1254. include.include = []
  1255. } else if (!Array.isArray(include.include)) {
  1256. include.include = [include.include]
  1257. }
  1258. }
  1259. var used = []
  1260. ;(function addAllIncludes(parent, includes) {
  1261. used.push(parent)
  1262. Utils._.forEach(parent.associations, function(association) {
  1263. if (all !== true && all.indexOf(association.associationType) == -1) {
  1264. return
  1265. }
  1266. // check if model already included, and skip if so
  1267. var model = association.target
  1268. var as = association.options.as
  1269. if (Utils._.find(includes, {daoFactory: model, as: as})) {
  1270. return
  1271. }
  1272. // skip if recursing over a model already nested
  1273. if (nested && used.indexOf(model) != -1) {
  1274. return
  1275. }
  1276. // include this model
  1277. var thisInclude = optClone(include)
  1278. thisInclude.daoFactory = model
  1279. if (as) {
  1280. thisInclude.as = as
  1281. }
  1282. includes.push(thisInclude)
  1283. // run recursively if nested
  1284. if (nested) {
  1285. addAllIncludes(model, thisInclude.include)
  1286. }
  1287. })
  1288. used.pop()
  1289. })(this, includes)
  1290. }
  1291. var replaceReferencesWithTableNames = function(attributes) {
  1292. Object.keys(attributes).forEach(function(attrName) {
  1293. if (attributes[attrName].references instanceof DAOFactory) {
  1294. attributes[attrName].references = attributes[attrName].references.tableName
  1295. }
  1296. })
  1297. return attributes
  1298. }
  1299. var optClone = function (options) {
  1300. return Utils._.cloneDeep(options, function (elem) {
  1301. // The DAOFactories used for include are pass by ref, so don't clone them.
  1302. if (elem instanceof DAOFactory || elem instanceof Utils.col || elem instanceof Utils.literal || elem instanceof Utils.cast || elem instanceof Utils.fn || elem instanceof Utils.and || elem instanceof Utils.or) {
  1303. return elem
  1304. }
  1305. // Unfortunately, lodash.cloneDeep doesn't preserve Buffer.isBuffer, which we have to rely on for binary data
  1306. if (Buffer.isBuffer(elem)) { return elem; }
  1307. // Otherwise return undefined, meaning, 'handle this lodash'
  1308. return undefined
  1309. })
  1310. }
  1311. Utils._.extend(DAOFactory.prototype, require("./associations/mixin"))
  1312. Utils._.extend(DAOFactory.prototype, require(__dirname + '/hooks'))
  1313. return DAOFactory
  1314. })()