UNPKG

195 kBJavaScriptView Raw
1'use strict';
2
3const assert = require('assert');
4const _ = require('lodash');
5const Dottie = require('dottie');
6
7const Utils = require('./utils');
8const { logger } = require('./utils/logger');
9const BelongsTo = require('./associations/belongs-to');
10const BelongsToMany = require('./associations/belongs-to-many');
11const InstanceValidator = require('./instance-validator');
12const QueryTypes = require('./query-types');
13const sequelizeErrors = require('./errors');
14const Promise = require('./promise');
15const Association = require('./associations/base');
16const HasMany = require('./associations/has-many');
17const DataTypes = require('./data-types');
18const Hooks = require('./hooks');
19const associationsMixin = require('./associations/mixin');
20const Op = require('./operators');
21const { noDoubleNestedGroup } = require('./utils/deprecations');
22
23
24// This list will quickly become dated, but failing to maintain this list just means
25// we won't throw a warning when we should. At least most common cases will forever be covered
26// so we stop throwing erroneous warnings when we shouldn't.
27const validQueryKeywords = new Set(['where', 'attributes', 'paranoid', 'include', 'order', 'limit', 'offset',
28 'transaction', 'lock', 'raw', 'logging', 'benchmark', 'having', 'searchPath', 'rejectOnEmpty', 'plain',
29 'scope', 'group', 'through', 'defaults', 'distinct', 'primary', 'exception', 'type', 'hooks', 'force',
30 'name']);
31
32// List of attributes that should not be implicitly passed into subqueries/includes.
33const nonCascadingOptions = ['include', 'attributes', 'originalAttributes', 'order', 'where', 'limit', 'offset', 'plain', 'group', 'having'];
34
35/**
36 * A Model represents a table in the database. Instances of this class represent a database row.
37 *
38 * Model instances operate with the concept of a `dataValues` property, which stores the actual values represented by the instance.
39 * By default, the values from dataValues can also be accessed directly from the Instance, that is:
40 * ```js
41 * instance.field
42 * // is the same as
43 * instance.get('field')
44 * // is the same as
45 * instance.getDataValue('field')
46 * ```
47 * However, if getters and/or setters are defined for `field` they will be invoked, instead of returning the value from `dataValues`.
48 * Accessing properties directly or using `get` is preferred for regular use, `getDataValue` should only be used for custom getters.
49 *
50 * @see
51 * {@link Sequelize#define} for more information about getters and setters
52 * @mixes Hooks
53 */
54class Model {
55 static get QueryInterface() {
56 return this.sequelize.getQueryInterface();
57 }
58
59 static get QueryGenerator() {
60 return this.QueryInterface.QueryGenerator;
61 }
62
63 /**
64 * A reference to the sequelize instance
65 *
66 * @see
67 * {@link Sequelize}
68 *
69 * @property sequelize
70 *
71 * @returns {Sequelize}
72 */
73 get sequelize() {
74 return this.constructor.sequelize;
75 }
76
77 /**
78 * Builds a new model instance.
79 *
80 * @param {Object} [values={}] an object of key value pairs
81 * @param {Object} [options] instance construction options
82 * @param {boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
83 * @param {boolean} [options.isNewRecord=true] Is this a new record
84 * @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances. See `set`
85 */
86 constructor(values = {}, options = {}) {
87 options = Object.assign({
88 isNewRecord: true,
89 _schema: this.constructor._schema,
90 _schemaDelimiter: this.constructor._schemaDelimiter
91 }, options || {});
92
93 if (options.attributes) {
94 options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
95 }
96
97 if (!options.includeValidated) {
98 this.constructor._conformIncludes(options, this.constructor);
99 if (options.include) {
100 this.constructor._expandIncludeAll(options);
101 this.constructor._validateIncludedElements(options);
102 }
103 }
104
105 this.dataValues = {};
106 this._previousDataValues = {};
107 this._changed = {};
108 this._modelOptions = this.constructor.options;
109 this._options = options || {};
110
111 /**
112 * Returns true if this instance has not yet been persisted to the database
113 * @property isNewRecord
114 * @returns {boolean}
115 */
116 this.isNewRecord = options.isNewRecord;
117
118 this._initValues(values, options);
119 }
120
121 _initValues(values, options) {
122 let defaults;
123 let key;
124
125 values = values && _.clone(values) || {};
126
127 if (options.isNewRecord) {
128 defaults = {};
129
130 if (this.constructor._hasDefaultValues) {
131 defaults = _.mapValues(this.constructor._defaultValues, valueFn => {
132 const value = valueFn();
133 return value && value instanceof Utils.SequelizeMethod ? value : _.cloneDeep(value);
134 });
135 }
136
137 // set id to null if not passed as value, a newly created dao has no id
138 // removing this breaks bulkCreate
139 // do after default values since it might have UUID as a default value
140 if (this.constructor.primaryKeyAttributes.length) {
141 this.constructor.primaryKeyAttributes.forEach(primaryKeyAttribute => {
142 if (!Object.prototype.hasOwnProperty.call(defaults, primaryKeyAttribute)) {
143 defaults[primaryKeyAttribute] = null;
144 }
145 });
146 }
147
148 if (this.constructor._timestampAttributes.createdAt && defaults[this.constructor._timestampAttributes.createdAt]) {
149 this.dataValues[this.constructor._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.createdAt], this.sequelize.options.dialect);
150 delete defaults[this.constructor._timestampAttributes.createdAt];
151 }
152
153 if (this.constructor._timestampAttributes.updatedAt && defaults[this.constructor._timestampAttributes.updatedAt]) {
154 this.dataValues[this.constructor._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.updatedAt], this.sequelize.options.dialect);
155 delete defaults[this.constructor._timestampAttributes.updatedAt];
156 }
157
158 if (this.constructor._timestampAttributes.deletedAt && defaults[this.constructor._timestampAttributes.deletedAt]) {
159 this.dataValues[this.constructor._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.deletedAt], this.sequelize.options.dialect);
160 delete defaults[this.constructor._timestampAttributes.deletedAt];
161 }
162
163 if (Object.keys(defaults).length) {
164 for (key in defaults) {
165 if (values[key] === undefined) {
166 this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), { raw: true });
167 delete values[key];
168 }
169 }
170 }
171 }
172
173 this.set(values, options);
174 }
175
176 // validateIncludedElements should have been called before this method
177 static _paranoidClause(model, options = {}) {
178 // Apply on each include
179 // This should be handled before handling where conditions because of logic with returns
180 // otherwise this code will never run on includes of a already conditionable where
181 if (options.include) {
182 for (const include of options.include) {
183 this._paranoidClause(include.model, include);
184 }
185 }
186
187 // apply paranoid when groupedLimit is used
188 if (_.get(options, 'groupedLimit.on.options.paranoid')) {
189 const throughModel = _.get(options, 'groupedLimit.on.through.model');
190 if (throughModel) {
191 options.groupedLimit.through = this._paranoidClause(throughModel, options.groupedLimit.through);
192 }
193 }
194
195 if (!model.options.timestamps || !model.options.paranoid || options.paranoid === false) {
196 // This model is not paranoid, nothing to do here;
197 return options;
198 }
199
200 const deletedAtCol = model._timestampAttributes.deletedAt;
201 const deletedAtAttribute = model.rawAttributes[deletedAtCol];
202 const deletedAtObject = {};
203
204 let deletedAtDefaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
205
206 deletedAtDefaultValue = deletedAtDefaultValue || {
207 [Op.eq]: null
208 };
209
210 deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
211
212 if (Utils.isWhereEmpty(options.where)) {
213 options.where = deletedAtObject;
214 } else {
215 options.where = { [Op.and]: [deletedAtObject, options.where] };
216 }
217
218 return options;
219 }
220
221 static _addDefaultAttributes() {
222 const tail = {};
223 let head = {};
224
225 // Add id if no primary key was manually added to definition
226 // Can't use this.primaryKeys here, since this function is called before PKs are identified
227 if (!_.some(this.rawAttributes, 'primaryKey')) {
228 if ('id' in this.rawAttributes) {
229 // Something is fishy here!
230 throw new Error(`A column called 'id' was added to the attributes of '${this.tableName}' but not marked with 'primaryKey: true'`);
231 }
232
233 head = {
234 id: {
235 type: new DataTypes.INTEGER(),
236 allowNull: false,
237 primaryKey: true,
238 autoIncrement: true,
239 _autoGenerated: true
240 }
241 };
242 }
243
244 if (this._timestampAttributes.createdAt) {
245 tail[this._timestampAttributes.createdAt] = {
246 type: DataTypes.DATE,
247 allowNull: false,
248 _autoGenerated: true
249 };
250 }
251
252 if (this._timestampAttributes.updatedAt) {
253 tail[this._timestampAttributes.updatedAt] = {
254 type: DataTypes.DATE,
255 allowNull: false,
256 _autoGenerated: true
257 };
258 }
259
260 if (this._timestampAttributes.deletedAt) {
261 tail[this._timestampAttributes.deletedAt] = {
262 type: DataTypes.DATE,
263 _autoGenerated: true
264 };
265 }
266
267 if (this._versionAttribute) {
268 tail[this._versionAttribute] = {
269 type: DataTypes.INTEGER,
270 allowNull: false,
271 defaultValue: 0,
272 _autoGenerated: true
273 };
274 }
275
276 const existingAttributes = _.clone(this.rawAttributes);
277 this.rawAttributes = {};
278
279 _.each(head, (value, attr) => {
280 this.rawAttributes[attr] = value;
281 });
282
283 _.each(existingAttributes, (value, attr) => {
284 this.rawAttributes[attr] = value;
285 });
286
287 _.each(tail, (value, attr) => {
288 if (this.rawAttributes[attr] === undefined) {
289 this.rawAttributes[attr] = value;
290 }
291 });
292
293 if (!Object.keys(this.primaryKeys).length) {
294 this.primaryKeys.id = this.rawAttributes.id;
295 }
296 }
297
298 static _findAutoIncrementAttribute() {
299 this.autoIncrementAttribute = null;
300
301 for (const name in this.rawAttributes) {
302 if (Object.prototype.hasOwnProperty.call(this.rawAttributes, name)) {
303 const definition = this.rawAttributes[name];
304 if (definition && definition.autoIncrement) {
305 if (this.autoIncrementAttribute) {
306 throw new Error('Invalid Instance definition. Only one autoincrement field allowed.');
307 }
308 this.autoIncrementAttribute = name;
309 }
310 }
311 }
312 }
313
314 static _conformIncludes(options, self) {
315 if (!options.include) return;
316
317 // if include is not an array, wrap in an array
318 if (!Array.isArray(options.include)) {
319 options.include = [options.include];
320 } else if (!options.include.length) {
321 delete options.include;
322 return;
323 }
324
325 // convert all included elements to { model: Model } form
326 options.include = options.include.map(include => this._conformInclude(include, self));
327 }
328
329 static _transformStringAssociation(include, self) {
330 if (self && typeof include === 'string') {
331 if (!Object.prototype.hasOwnProperty.call(self.associations, include)) {
332 throw new Error(`Association with alias "${include}" does not exist on ${self.name}`);
333 }
334 return self.associations[include];
335 }
336 return include;
337 }
338
339 static _conformInclude(include, self) {
340 if (include) {
341 let model;
342
343 if (include._pseudo) return include;
344
345 include = this._transformStringAssociation(include, self);
346
347 if (include instanceof Association) {
348 if (self && include.target.name === self.name) {
349 model = include.source;
350 } else {
351 model = include.target;
352 }
353
354 return { model, association: include, as: include.as };
355 }
356
357 if (include.prototype && include.prototype instanceof Model) {
358 return { model: include };
359 }
360
361 if (_.isPlainObject(include)) {
362 if (include.association) {
363 include.association = this._transformStringAssociation(include.association, self);
364
365 if (self && include.association.target.name === self.name) {
366 model = include.association.source;
367 } else {
368 model = include.association.target;
369 }
370
371 if (!include.model) include.model = model;
372 if (!include.as) include.as = include.association.as;
373
374 this._conformIncludes(include, model);
375 return include;
376 }
377
378 if (include.model) {
379 this._conformIncludes(include, include.model);
380 return include;
381 }
382
383 if (include.all) {
384 this._conformIncludes(include);
385 return include;
386 }
387 }
388 }
389
390 throw new Error('Include unexpected. Element has to be either a Model, an Association or an object.');
391 }
392
393 static _expandIncludeAllElement(includes, include) {
394 // check 'all' attribute provided is valid
395 let all = include.all;
396 delete include.all;
397
398 if (all !== true) {
399 if (!Array.isArray(all)) {
400 all = [all];
401 }
402
403 const validTypes = {
404 BelongsTo: true,
405 HasOne: true,
406 HasMany: true,
407 One: ['BelongsTo', 'HasOne'],
408 Has: ['HasOne', 'HasMany'],
409 Many: ['HasMany']
410 };
411
412 for (let i = 0; i < all.length; i++) {
413 const type = all[i];
414 if (type === 'All') {
415 all = true;
416 break;
417 }
418
419 const types = validTypes[type];
420 if (!types) {
421 throw new sequelizeErrors.EagerLoadingError(`include all '${type}' is not valid - must be BelongsTo, HasOne, HasMany, One, Has, Many or All`);
422 }
423
424 if (types !== true) {
425 // replace type placeholder e.g. 'One' with its constituent types e.g. 'HasOne', 'BelongsTo'
426 all.splice(i, 1);
427 i--;
428 for (let j = 0; j < types.length; j++) {
429 if (!all.includes(types[j])) {
430 all.unshift(types[j]);
431 i++;
432 }
433 }
434 }
435 }
436 }
437
438 // add all associations of types specified to includes
439 const nested = include.nested;
440 if (nested) {
441 delete include.nested;
442
443 if (!include.include) {
444 include.include = [];
445 } else if (!Array.isArray(include.include)) {
446 include.include = [include.include];
447 }
448 }
449
450 const used = [];
451 (function addAllIncludes(parent, includes) {
452 _.forEach(parent.associations, association => {
453 if (all !== true && !all.includes(association.associationType)) {
454 return;
455 }
456
457 // check if model already included, and skip if so
458 const model = association.target;
459 const as = association.options.as;
460
461 const predicate = { model };
462 if (as) {
463 // We only add 'as' to the predicate if it actually exists
464 predicate.as = as;
465 }
466
467 if (_.some(includes, predicate)) {
468 return;
469 }
470
471 // skip if recursing over a model already nested
472 if (nested && used.includes(model)) {
473 return;
474 }
475 used.push(parent);
476
477 // include this model
478 const thisInclude = Utils.cloneDeep(include);
479 thisInclude.model = model;
480 if (as) {
481 thisInclude.as = as;
482 }
483 includes.push(thisInclude);
484
485 // run recursively if nested
486 if (nested) {
487 addAllIncludes(model, thisInclude.include);
488 if (thisInclude.include.length === 0) delete thisInclude.include;
489 }
490 });
491 used.pop();
492 })(this, includes);
493 }
494
495 static _validateIncludedElements(options, tableNames) {
496 if (!options.model) options.model = this;
497
498 tableNames = tableNames || {};
499 options.includeNames = [];
500 options.includeMap = {};
501
502 /* Legacy */
503 options.hasSingleAssociation = false;
504 options.hasMultiAssociation = false;
505
506 if (!options.parent) {
507 options.topModel = options.model;
508 options.topLimit = options.limit;
509 }
510
511 options.include = options.include.map(include => {
512 include = this._conformInclude(include);
513 include.parent = options;
514 include.topLimit = options.topLimit;
515
516 this._validateIncludedElement.call(options.model, include, tableNames, options);
517
518 if (include.duplicating === undefined) {
519 include.duplicating = include.association.isMultiAssociation;
520 }
521
522 include.hasDuplicating = include.hasDuplicating || include.duplicating;
523 include.hasRequired = include.hasRequired || include.required;
524
525 options.hasDuplicating = options.hasDuplicating || include.hasDuplicating;
526 options.hasRequired = options.hasRequired || include.required;
527
528 options.hasWhere = options.hasWhere || include.hasWhere || !!include.where;
529 return include;
530 });
531
532 for (const include of options.include) {
533 include.hasParentWhere = options.hasParentWhere || !!options.where;
534 include.hasParentRequired = options.hasParentRequired || !!options.required;
535
536 if (include.subQuery !== false && options.hasDuplicating && options.topLimit) {
537 if (include.duplicating) {
538 include.subQuery = false;
539 include.subQueryFilter = include.hasRequired;
540 } else {
541 include.subQuery = include.hasRequired;
542 include.subQueryFilter = false;
543 }
544 } else {
545 include.subQuery = include.subQuery || false;
546 if (include.duplicating) {
547 include.subQueryFilter = include.subQuery;
548 include.subQuery = false;
549 } else {
550 include.subQueryFilter = false;
551 include.subQuery = include.subQuery || include.hasParentRequired && include.hasRequired;
552 }
553 }
554
555 options.includeMap[include.as] = include;
556 options.includeNames.push(include.as);
557
558 // Set top level options
559 if (options.topModel === options.model && options.subQuery === undefined && options.topLimit) {
560 if (include.subQuery) {
561 options.subQuery = include.subQuery;
562 } else if (include.hasDuplicating) {
563 options.subQuery = true;
564 }
565 }
566
567 /* Legacy */
568 options.hasIncludeWhere = options.hasIncludeWhere || include.hasIncludeWhere || !!include.where;
569 options.hasIncludeRequired = options.hasIncludeRequired || include.hasIncludeRequired || !!include.required;
570
571 if (include.association.isMultiAssociation || include.hasMultiAssociation) {
572 options.hasMultiAssociation = true;
573 }
574 if (include.association.isSingleAssociation || include.hasSingleAssociation) {
575 options.hasSingleAssociation = true;
576 }
577 }
578
579 if (options.topModel === options.model && options.subQuery === undefined) {
580 options.subQuery = false;
581 }
582 return options;
583 }
584
585 static _validateIncludedElement(include, tableNames, options) {
586 tableNames[include.model.getTableName()] = true;
587
588 if (include.attributes && !options.raw) {
589 include.model._expandAttributes(include);
590
591 include.originalAttributes = this._injectDependentVirtualAttributes(include.attributes);
592
593 include = Utils.mapFinderOptions(include, include.model);
594
595 if (include.attributes.length) {
596 _.each(include.model.primaryKeys, (attr, key) => {
597 // Include the primary key if it's not already included - take into account that the pk might be aliased (due to a .field prop)
598 if (!include.attributes.some(includeAttr => {
599 if (attr.field !== key) {
600 return Array.isArray(includeAttr) && includeAttr[0] === attr.field && includeAttr[1] === key;
601 }
602 return includeAttr === key;
603 })) {
604 include.attributes.unshift(key);
605 }
606 });
607 }
608 } else {
609 include = Utils.mapFinderOptions(include, include.model);
610 }
611
612 // pseudo include just needed the attribute logic, return
613 if (include._pseudo) {
614 include.attributes = Object.keys(include.model.tableAttributes);
615 return Utils.mapFinderOptions(include, include.model);
616 }
617
618 // check if the current Model is actually associated with the passed Model - or it's a pseudo include
619 const association = include.association || this._getIncludedAssociation(include.model, include.as);
620
621 include.association = association;
622 include.as = association.as;
623
624 // If through, we create a pseudo child include, to ease our parsing later on
625 if (include.association.through && Object(include.association.through.model) === include.association.through.model) {
626 if (!include.include) include.include = [];
627 const through = include.association.through;
628
629 include.through = _.defaults(include.through || {}, {
630 model: through.model,
631 as: through.model.name,
632 association: {
633 isSingleAssociation: true
634 },
635 _pseudo: true,
636 parent: include
637 });
638
639
640 if (through.scope) {
641 include.through.where = include.through.where ? { [Op.and]: [include.through.where, through.scope] } : through.scope;
642 }
643
644 include.include.push(include.through);
645 tableNames[through.tableName] = true;
646 }
647
648 // include.model may be the main model, while the association target may be scoped - thus we need to look at association.target/source
649 let model;
650 if (include.model.scoped === true) {
651 // If the passed model is already scoped, keep that
652 model = include.model;
653 } else {
654 // Otherwise use the model that was originally passed to the association
655 model = include.association.target.name === include.model.name ? include.association.target : include.association.source;
656 }
657
658 model._injectScope(include);
659
660 // This check should happen after injecting the scope, since the scope may contain a .attributes
661 if (!include.attributes) {
662 include.attributes = Object.keys(include.model.tableAttributes);
663 }
664
665 include = Utils.mapFinderOptions(include, include.model);
666
667 if (include.required === undefined) {
668 include.required = !!include.where;
669 }
670
671 if (include.association.scope) {
672 include.where = include.where ? { [Op.and]: [include.where, include.association.scope] } : include.association.scope;
673 }
674
675 if (include.limit && include.separate === undefined) {
676 include.separate = true;
677 }
678
679 if (include.separate === true) {
680 if (!(include.association instanceof HasMany)) {
681 throw new Error('Only HasMany associations support include.separate');
682 }
683
684 include.duplicating = false;
685
686 if (
687 options.attributes
688 && options.attributes.length
689 && !_.flattenDepth(options.attributes, 2).includes(association.sourceKey)
690 ) {
691 options.attributes.push(association.sourceKey);
692 }
693
694 if (
695 include.attributes
696 && include.attributes.length
697 && !_.flattenDepth(include.attributes, 2).includes(association.foreignKey)
698 ) {
699 include.attributes.push(association.foreignKey);
700 }
701 }
702
703 // Validate child includes
704 if (Object.prototype.hasOwnProperty.call(include, 'include')) {
705 this._validateIncludedElements.call(include.model, include, tableNames);
706 }
707
708 return include;
709 }
710
711 static _getIncludedAssociation(targetModel, targetAlias) {
712 const associations = this.getAssociations(targetModel);
713 let association = null;
714 if (associations.length === 0) {
715 throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is not associated to ${this.name}!`);
716 }
717 if (associations.length === 1) {
718 association = this.getAssociationForAlias(targetModel, targetAlias);
719 if (association) {
720 return association;
721 }
722 if (targetAlias) {
723 const existingAliases = this.getAssociations(targetModel).map(association => association.as);
724 throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is associated to ${this.name} using an alias. ` +
725 `You've included an alias (${targetAlias}), but it does not match the alias(es) defined in your association (${existingAliases.join(', ')}).`);
726 }
727 throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is associated to ${this.name} using an alias. ` +
728 'You must use the \'as\' keyword to specify the alias within your include statement.');
729 }
730 association = this.getAssociationForAlias(targetModel, targetAlias);
731 if (!association) {
732 throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is associated to ${this.name} multiple times. ` +
733 'To identify the correct association, you must use the \'as\' keyword to specify the alias of the association you want to include.');
734 }
735 return association;
736 }
737
738
739 static _expandIncludeAll(options) {
740 const includes = options.include;
741 if (!includes) {
742 return;
743 }
744
745 for (let index = 0; index < includes.length; index++) {
746 const include = includes[index];
747
748 if (include.all) {
749 includes.splice(index, 1);
750 index--;
751
752 this._expandIncludeAllElement(includes, include);
753 }
754 }
755
756 includes.forEach(include => {
757 this._expandIncludeAll.call(include.model, include);
758 });
759 }
760
761 static _conformIndex(index) {
762 if (!index.fields) {
763 throw new Error('Missing "fields" property for index definition');
764 }
765
766 index = _.defaults(index, {
767 type: '',
768 parser: null
769 });
770
771 if (index.type && index.type.toLowerCase() === 'unique') {
772 index.unique = true;
773 delete index.type;
774 }
775
776 return index;
777 }
778
779
780 static _uniqIncludes(options) {
781 if (!options.include) return;
782
783 options.include = _(options.include)
784 .groupBy(include => `${include.model && include.model.name}-${include.as}`)
785 .map(includes => this._assignOptions(...includes))
786 .value();
787 }
788
789 static _baseMerge(...args) {
790 _.assignWith(...args);
791 this._conformIncludes(args[0], this);
792 this._uniqIncludes(args[0]);
793 return args[0];
794 }
795
796 static _mergeFunction(objValue, srcValue, key) {
797 if (Array.isArray(objValue) && Array.isArray(srcValue)) {
798 return _.union(objValue, srcValue);
799 }
800 if (key === 'where' || key === 'having') {
801 if (srcValue instanceof Utils.SequelizeMethod) {
802 srcValue = { [Op.and]: srcValue };
803 }
804 if (_.isPlainObject(objValue) && _.isPlainObject(srcValue)) {
805 return Object.assign(objValue, srcValue);
806 }
807 } else if (key === 'attributes' && _.isPlainObject(objValue) && _.isPlainObject(srcValue)) {
808 return _.assignWith(objValue, srcValue, (objValue, srcValue) => {
809 if (Array.isArray(objValue) && Array.isArray(srcValue)) {
810 return _.union(objValue, srcValue);
811 }
812 });
813 }
814 // If we have a possible object/array to clone, we try it.
815 // Otherwise, we return the original value when it's not undefined,
816 // or the resulting object in that case.
817 if (srcValue) {
818 return Utils.cloneDeep(srcValue, true);
819 }
820 return srcValue === undefined ? objValue : srcValue;
821 }
822
823 static _assignOptions(...args) {
824 return this._baseMerge(...args, this._mergeFunction);
825 }
826
827 static _defaultsOptions(target, opts) {
828 return this._baseMerge(target, opts, (srcValue, objValue, key) => {
829 return this._mergeFunction(objValue, srcValue, key);
830 });
831 }
832
833 /**
834 * Initialize a model, representing a table in the DB, with attributes and options.
835 *
836 * The table columns are defined by the hash that is given as the first argument.
837 * Each attribute of the hash represents a column.
838 *
839 * For more about <a href="/manual/tutorial/models-definition.html#validations"/>Validations</a>
840 *
841 * More examples, <a href="/manual/tutorial/models-definition.html"/>Model Definition</a>
842 *
843 * @example
844 * Project.init({
845 * columnA: {
846 * type: Sequelize.BOOLEAN,
847 * validate: {
848 * is: ['[a-z]','i'], // will only allow letters
849 * max: 23, // only allow values <= 23
850 * isIn: {
851 * args: [['en', 'zh']],
852 * msg: "Must be English or Chinese"
853 * }
854 * },
855 * field: 'column_a'
856 * // Other attributes here
857 * },
858 * columnB: Sequelize.STRING,
859 * columnC: 'MY VERY OWN COLUMN TYPE'
860 * }, {sequelize})
861 *
862 * sequelize.models.modelName // The model will now be available in models under the class name
863 *
864 * @see
865 * {@link DataTypes}
866 * @see
867 * {@link Hooks}
868 *
869 * @param {Object} attributes An object, where each attribute is a column of the table. Each column can be either a DataType, a string or a type-description object, with the properties described below:
870 * @param {string|DataTypes|Object} attributes.column The description of a database column
871 * @param {string|DataTypes} attributes.column.type A string or a data type
872 * @param {boolean} [attributes.column.allowNull=true] If false, the column will have a NOT NULL constraint, and a not null validation will be run before an instance is saved.
873 * @param {any} [attributes.column.defaultValue=null] A literal default value, a JavaScript function, or an SQL function (see `sequelize.fn`)
874 * @param {string|boolean} [attributes.column.unique=false] If true, the column will get a unique constraint. If a string is provided, the column will be part of a composite unique index. If multiple columns have the same string, they will be part of the same unique index
875 * @param {boolean} [attributes.column.primaryKey=false] If true, this attribute will be marked as primary key
876 * @param {string} [attributes.column.field=null] If set, sequelize will map the attribute name to a different name in the database
877 * @param {boolean} [attributes.column.autoIncrement=false] If true, this column will be set to auto increment
878 * @param {boolean} [attributes.column.autoIncrementIdentity=false] If true, combined with autoIncrement=true, will use Postgres `GENERATED BY DEFAULT AS IDENTITY` instead of `SERIAL`. Postgres 10+ only.
879 * @param {string} [attributes.column.comment=null] Comment for this column
880 * @param {string|Model} [attributes.column.references=null] An object with reference configurations
881 * @param {string|Model} [attributes.column.references.model] If this column references another table, provide it here as a Model, or a string
882 * @param {string} [attributes.column.references.key='id'] The column of the foreign table that this column references
883 * @param {string} [attributes.column.onUpdate] What should happen when the referenced key is updated. One of CASCADE, RESTRICT, SET DEFAULT, SET NULL or NO ACTION
884 * @param {string} [attributes.column.onDelete] What should happen when the referenced key is deleted. One of CASCADE, RESTRICT, SET DEFAULT, SET NULL or NO ACTION
885 * @param {Function} [attributes.column.get] Provide a custom getter for this column. Use `this.getDataValue(String)` to manipulate the underlying values.
886 * @param {Function} [attributes.column.set] Provide a custom setter for this column. Use `this.setDataValue(String, Value)` to manipulate the underlying values.
887 * @param {Object} [attributes.column.validate] An object of validations to execute for this column every time the model is saved. Can be either the name of a validation provided by validator.js, a validation function provided by extending validator.js (see the `DAOValidator` property for more details), or a custom validation function. Custom validation functions are called with the value of the field and the instance itself as the `this` binding, and can possibly take a second callback argument, to signal that they are asynchronous. If the validator is sync, it should throw in the case of a failed validation; if it is async, the callback should be called with the error text.
888 * @param {Object} options These options are merged with the default define options provided to the Sequelize constructor
889 * @param {Object} options.sequelize Define the sequelize instance to attach to the new Model. Throw error if none is provided.
890 * @param {string} [options.modelName] Set name of the model. By default its same as Class name.
891 * @param {Object} [options.defaultScope={}] Define the default search scope to use for this model. Scopes have the same form as the options passed to find / findAll
892 * @param {Object} [options.scopes] More scopes, defined in the same way as defaultScope above. See `Model.scope` for more information about how scopes are defined, and what you can do with them
893 * @param {boolean} [options.omitNull] Don't persist null values. This means that all columns with null values will not be saved
894 * @param {boolean} [options.timestamps=true] Adds createdAt and updatedAt timestamps to the model.
895 * @param {boolean} [options.paranoid=false] Calling `destroy` will not delete the model, but instead set a `deletedAt` timestamp if this is true. Needs `timestamps=true` to work
896 * @param {boolean} [options.underscored=false] Add underscored field to all attributes, this covers user defined attributes, timestamps and foreign keys. Will not affect attributes with explicitly set `field` option
897 * @param {boolean} [options.freezeTableName=false] If freezeTableName is true, sequelize will not try to alter the model name to get the table name. Otherwise, the model name will be pluralized
898 * @param {Object} [options.name] An object with two attributes, `singular` and `plural`, which are used when this model is associated to others.
899 * @param {string} [options.name.singular=Utils.singularize(modelName)] Singular name for model
900 * @param {string} [options.name.plural=Utils.pluralize(modelName)] Plural name for model
901 * @param {Array<Object>} [options.indexes] indexes definitions
902 * @param {string} [options.indexes[].name] The name of the index. Defaults to model name + _ + fields concatenated
903 * @param {string} [options.indexes[].type] Index type. Only used by mysql. One of `UNIQUE`, `FULLTEXT` and `SPATIAL`
904 * @param {string} [options.indexes[].using] The method to create the index by (`USING` statement in SQL). BTREE and HASH are supported by mysql and postgres, and postgres additionally supports GIST and GIN.
905 * @param {string} [options.indexes[].operator] Specify index operator.
906 * @param {boolean} [options.indexes[].unique=false] Should the index by unique? Can also be triggered by setting type to `UNIQUE`
907 * @param {boolean} [options.indexes[].concurrently=false] PostgresSQL will build the index without taking any write locks. Postgres only
908 * @param {Array<string|Object>} [options.indexes[].fields] An array of the fields to index. Each field can either be a string containing the name of the field, a sequelize object (e.g `sequelize.fn`), or an object with the following attributes: `attribute` (field name), `length` (create a prefix index of length chars), `order` (the direction the column should be sorted in), `collate` (the collation (sort order) for the column)
909 * @param {string|boolean} [options.createdAt] Override the name of the createdAt attribute if a string is provided, or disable it if false. Timestamps must be true. Underscored field will be set with underscored setting.
910 * @param {string|boolean} [options.updatedAt] Override the name of the updatedAt attribute if a string is provided, or disable it if false. Timestamps must be true. Underscored field will be set with underscored setting.
911 * @param {string|boolean} [options.deletedAt] Override the name of the deletedAt attribute if a string is provided, or disable it if false. Timestamps must be true. Underscored field will be set with underscored setting.
912 * @param {string} [options.tableName] Defaults to pluralized model name, unless freezeTableName is true, in which case it uses model name verbatim
913 * @param {string} [options.schema='public'] schema
914 * @param {string} [options.engine] Specify engine for model's table
915 * @param {string} [options.charset] Specify charset for model's table
916 * @param {string} [options.comment] Specify comment for model's table
917 * @param {string} [options.collate] Specify collation for model's table
918 * @param {string} [options.initialAutoIncrement] Set the initial AUTO_INCREMENT value for the table in MySQL.
919 * @param {Object} [options.hooks] An object of hook function that are called before and after certain lifecycle events. The possible hooks are: beforeValidate, afterValidate, validationFailed, beforeBulkCreate, beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy, beforeUpdate, afterCreate, beforeSave, afterDestroy, afterUpdate, afterBulkCreate, afterSave, afterBulkDestroy and afterBulkUpdate. See Hooks for more information about hook functions and their signatures. Each property can either be a function, or an array of functions.
920 * @param {Object} [options.validate] An object of model wide validations. Validations have access to all model values via `this`. If the validator function takes an argument, it is assumed to be async, and is called with a callback that accepts an optional error.
921 *
922 * @returns {Model}
923 */
924 static init(attributes, options = {}) {
925 if (!options.sequelize) {
926 throw new Error('No Sequelize instance passed');
927 }
928
929 this.sequelize = options.sequelize;
930
931 const globalOptions = this.sequelize.options;
932
933 options = Utils.merge(_.cloneDeep(globalOptions.define), options);
934
935 if (!options.modelName) {
936 options.modelName = this.name;
937 }
938
939 options = Utils.merge({
940 name: {
941 plural: Utils.pluralize(options.modelName),
942 singular: Utils.singularize(options.modelName)
943 },
944 indexes: [],
945 omitNull: globalOptions.omitNull,
946 schema: globalOptions.schema
947 }, options);
948
949 this.sequelize.runHooks('beforeDefine', attributes, options);
950
951 if (options.modelName !== this.name) {
952 Object.defineProperty(this, 'name', { value: options.modelName });
953 }
954 delete options.modelName;
955
956 this.options = Object.assign({
957 timestamps: true,
958 validate: {},
959 freezeTableName: false,
960 underscored: false,
961 paranoid: false,
962 rejectOnEmpty: false,
963 whereCollection: null,
964 schema: null,
965 schemaDelimiter: '',
966 defaultScope: {},
967 scopes: {},
968 indexes: []
969 }, options);
970
971 // if you call "define" multiple times for the same modelName, do not clutter the factory
972 if (this.sequelize.isDefined(this.name)) {
973 this.sequelize.modelManager.removeModel(this.sequelize.modelManager.getModel(this.name));
974 }
975
976 this.associations = {};
977 this._setupHooks(options.hooks);
978
979 this.underscored = this.options.underscored;
980
981 if (!this.options.tableName) {
982 this.tableName = this.options.freezeTableName ? this.name : Utils.underscoredIf(Utils.pluralize(this.name), this.underscored);
983 } else {
984 this.tableName = this.options.tableName;
985 }
986
987 this._schema = this.options.schema;
988 this._schemaDelimiter = this.options.schemaDelimiter;
989
990 // error check options
991 _.each(options.validate, (validator, validatorType) => {
992 if (Object.prototype.hasOwnProperty.call(attributes, validatorType)) {
993 throw new Error(`A model validator function must not have the same name as a field. Model: ${this.name}, field/validation name: ${validatorType}`);
994 }
995
996 if (typeof validator !== 'function') {
997 throw new Error(`Members of the validate option must be functions. Model: ${this.name}, error with validate member ${validatorType}`);
998 }
999 });
1000
1001 this.rawAttributes = _.mapValues(attributes, (attribute, name) => {
1002 attribute = this.sequelize.normalizeAttribute(attribute);
1003
1004 if (attribute.type === undefined) {
1005 throw new Error(`Unrecognized datatype for attribute "${this.name}.${name}"`);
1006 }
1007
1008 if (attribute.allowNull !== false && _.get(attribute, 'validate.notNull')) {
1009 throw new Error(`Invalid definition for "${this.name}.${name}", "notNull" validator is only allowed with "allowNull:false"`);
1010 }
1011
1012 if (_.get(attribute, 'references.model.prototype') instanceof Model) {
1013 attribute.references.model = attribute.references.model.getTableName();
1014 }
1015
1016 return attribute;
1017 });
1018
1019 const tableName = this.getTableName();
1020 this._indexes = this.options.indexes
1021 .map(index => Utils.nameIndex(this._conformIndex(index), tableName));
1022
1023 this.primaryKeys = {};
1024 this._readOnlyAttributes = new Set();
1025 this._timestampAttributes = {};
1026
1027 // setup names of timestamp attributes
1028 if (this.options.timestamps) {
1029 if (this.options.createdAt !== false) {
1030 this._timestampAttributes.createdAt = this.options.createdAt || 'createdAt';
1031 this._readOnlyAttributes.add(this._timestampAttributes.createdAt);
1032 }
1033 if (this.options.updatedAt !== false) {
1034 this._timestampAttributes.updatedAt = this.options.updatedAt || 'updatedAt';
1035 this._readOnlyAttributes.add(this._timestampAttributes.updatedAt);
1036 }
1037 if (this.options.paranoid && this.options.deletedAt !== false) {
1038 this._timestampAttributes.deletedAt = this.options.deletedAt || 'deletedAt';
1039 this._readOnlyAttributes.add(this._timestampAttributes.deletedAt);
1040 }
1041 }
1042
1043 // setup name for version attribute
1044 if (this.options.version) {
1045 this._versionAttribute = typeof this.options.version === 'string' ? this.options.version : 'version';
1046 this._readOnlyAttributes.add(this._versionAttribute);
1047 }
1048
1049 this._hasReadOnlyAttributes = this._readOnlyAttributes.size > 0;
1050
1051 // Add head and tail default attributes (id, timestamps)
1052 this._addDefaultAttributes();
1053 this.refreshAttributes();
1054 this._findAutoIncrementAttribute();
1055
1056 this._scope = this.options.defaultScope;
1057 this._scopeNames = ['defaultScope'];
1058
1059 this.sequelize.modelManager.addModel(this);
1060 this.sequelize.runHooks('afterDefine', this);
1061
1062 return this;
1063 }
1064
1065 static refreshAttributes() {
1066 const attributeManipulation = {};
1067
1068 this.prototype._customGetters = {};
1069 this.prototype._customSetters = {};
1070
1071 ['get', 'set'].forEach(type => {
1072 const opt = `${type}terMethods`;
1073 const funcs = _.clone(_.isObject(this.options[opt]) ? this.options[opt] : {});
1074 const _custom = type === 'get' ? this.prototype._customGetters : this.prototype._customSetters;
1075
1076 _.each(funcs, (method, attribute) => {
1077 _custom[attribute] = method;
1078
1079 if (type === 'get') {
1080 funcs[attribute] = function() {
1081 return this.get(attribute);
1082 };
1083 }
1084 if (type === 'set') {
1085 funcs[attribute] = function(value) {
1086 return this.set(attribute, value);
1087 };
1088 }
1089 });
1090
1091 _.each(this.rawAttributes, (options, attribute) => {
1092 if (Object.prototype.hasOwnProperty.call(options, type)) {
1093 _custom[attribute] = options[type];
1094 }
1095
1096 if (type === 'get') {
1097 funcs[attribute] = function() {
1098 return this.get(attribute);
1099 };
1100 }
1101 if (type === 'set') {
1102 funcs[attribute] = function(value) {
1103 return this.set(attribute, value);
1104 };
1105 }
1106 });
1107
1108 _.each(funcs, (fct, name) => {
1109 if (!attributeManipulation[name]) {
1110 attributeManipulation[name] = {
1111 configurable: true
1112 };
1113 }
1114 attributeManipulation[name][type] = fct;
1115 });
1116 });
1117
1118 this._dataTypeChanges = {};
1119 this._dataTypeSanitizers = {};
1120
1121 this._hasBooleanAttributes = false;
1122 this._hasDateAttributes = false;
1123 this._jsonAttributes = new Set();
1124 this._virtualAttributes = new Set();
1125 this._defaultValues = {};
1126 this.prototype.validators = {};
1127
1128 this.fieldRawAttributesMap = {};
1129
1130 this.primaryKeys = {};
1131 this.uniqueKeys = {};
1132
1133 _.each(this.rawAttributes, (definition, name) => {
1134 definition.type = this.sequelize.normalizeDataType(definition.type);
1135
1136 definition.Model = this;
1137 definition.fieldName = name;
1138 definition._modelAttribute = true;
1139
1140 if (definition.field === undefined) {
1141 definition.field = Utils.underscoredIf(name, this.underscored);
1142 }
1143
1144 if (definition.primaryKey === true) {
1145 this.primaryKeys[name] = definition;
1146 }
1147
1148 this.fieldRawAttributesMap[definition.field] = definition;
1149
1150 if (definition.type._sanitize) {
1151 this._dataTypeSanitizers[name] = definition.type._sanitize;
1152 }
1153
1154 if (definition.type._isChanged) {
1155 this._dataTypeChanges[name] = definition.type._isChanged;
1156 }
1157
1158 if (definition.type instanceof DataTypes.BOOLEAN) {
1159 this._hasBooleanAttributes = true;
1160 } else if (definition.type instanceof DataTypes.DATE || definition.type instanceof DataTypes.DATEONLY) {
1161 this._hasDateAttributes = true;
1162 } else if (definition.type instanceof DataTypes.JSON) {
1163 this._jsonAttributes.add(name);
1164 } else if (definition.type instanceof DataTypes.VIRTUAL) {
1165 this._virtualAttributes.add(name);
1166 }
1167
1168 if (Object.prototype.hasOwnProperty.call(definition, 'defaultValue')) {
1169 this._defaultValues[name] = () => Utils.toDefaultValue(definition.defaultValue, this.sequelize.options.dialect);
1170 }
1171
1172 if (Object.prototype.hasOwnProperty.call(definition, 'unique') && definition.unique) {
1173 let idxName;
1174 if (
1175 typeof definition.unique === 'object' &&
1176 Object.prototype.hasOwnProperty.call(definition.unique, 'name')
1177 ) {
1178 idxName = definition.unique.name;
1179 } else if (typeof definition.unique === 'string') {
1180 idxName = definition.unique;
1181 } else {
1182 idxName = `${this.tableName}_${name}_unique`;
1183 }
1184
1185 const idx = this.uniqueKeys[idxName] || { fields: [] };
1186
1187 idx.fields.push(definition.field);
1188 idx.msg = idx.msg || definition.unique.msg || null;
1189 idx.name = idxName || false;
1190 idx.column = name;
1191 idx.customIndex = definition.unique !== true;
1192
1193 this.uniqueKeys[idxName] = idx;
1194 }
1195
1196 if (Object.prototype.hasOwnProperty.call(definition, 'validate')) {
1197 this.prototype.validators[name] = definition.validate;
1198 }
1199
1200 if (definition.index === true && definition.type instanceof DataTypes.JSONB) {
1201 this._indexes.push(
1202 Utils.nameIndex(
1203 this._conformIndex({
1204 fields: [definition.field || name],
1205 using: 'gin'
1206 }),
1207 this.getTableName()
1208 )
1209 );
1210
1211 delete definition.index;
1212 }
1213 });
1214
1215 // Create a map of field to attribute names
1216 this.fieldAttributeMap = _.reduce(this.fieldRawAttributesMap, (map, value, key) => {
1217 if (key !== value.fieldName) {
1218 map[key] = value.fieldName;
1219 }
1220 return map;
1221 }, {});
1222
1223 this._hasJsonAttributes = !!this._jsonAttributes.size;
1224
1225 this._hasVirtualAttributes = !!this._virtualAttributes.size;
1226
1227 this._hasDefaultValues = !_.isEmpty(this._defaultValues);
1228
1229 this.tableAttributes = _.omitBy(this.rawAttributes, (_a, key) => this._virtualAttributes.has(key));
1230
1231 this.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length;
1232 this.prototype._hasCustomSetters = Object.keys(this.prototype._customSetters).length;
1233
1234 for (const key of Object.keys(attributeManipulation)) {
1235 if (Object.prototype.hasOwnProperty.call(Model.prototype, key)) {
1236 this.sequelize.log(`Not overriding built-in method from model attribute: ${key}`);
1237 continue;
1238 }
1239 Object.defineProperty(this.prototype, key, attributeManipulation[key]);
1240 }
1241
1242 this.prototype.rawAttributes = this.rawAttributes;
1243 this.prototype._isAttribute = key => Object.prototype.hasOwnProperty.call(this.prototype.rawAttributes, key);
1244
1245 // Primary key convenience constiables
1246 this.primaryKeyAttributes = Object.keys(this.primaryKeys);
1247 this.primaryKeyAttribute = this.primaryKeyAttributes[0];
1248 if (this.primaryKeyAttribute) {
1249 this.primaryKeyField = this.rawAttributes[this.primaryKeyAttribute].field || this.primaryKeyAttribute;
1250 }
1251
1252 this._hasPrimaryKeys = this.primaryKeyAttributes.length > 0;
1253 this._isPrimaryKey = key => this.primaryKeyAttributes.includes(key);
1254 }
1255
1256 /**
1257 * Remove attribute from model definition
1258 *
1259 * @param {string} attribute name of attribute to remove
1260 */
1261 static removeAttribute(attribute) {
1262 delete this.rawAttributes[attribute];
1263 this.refreshAttributes();
1264 }
1265
1266 /**
1267 * Sync this Model to the DB, that is create the table.
1268 *
1269 * @param {Object} [options] sync options
1270 *
1271 * @see
1272 * {@link Sequelize#sync} for options
1273 *
1274 * @returns {Promise<Model>}
1275 */
1276 static sync(options) {
1277 options = Object.assign({}, this.options, options);
1278 options.hooks = options.hooks === undefined ? true : !!options.hooks;
1279
1280 const attributes = this.tableAttributes;
1281 const rawAttributes = this.fieldRawAttributesMap;
1282
1283 return Promise.try(() => {
1284 if (options.hooks) {
1285 return this.runHooks('beforeSync', options);
1286 }
1287 }).then(() => {
1288 if (options.force) {
1289 return this.drop(options);
1290 }
1291 })
1292 .then(() => this.QueryInterface.createTable(this.getTableName(options), attributes, options, this))
1293 .then(() => {
1294 if (!options.alter) {
1295 return;
1296 }
1297 return Promise.all([
1298 this.QueryInterface.describeTable(this.getTableName(options)),
1299 this.QueryInterface.getForeignKeyReferencesForTable(this.getTableName(options))
1300 ])
1301 .then(tableInfos => {
1302 const columns = tableInfos[0];
1303 // Use for alter foreign keys
1304 const foreignKeyReferences = tableInfos[1];
1305
1306 const changes = []; // array of promises to run
1307 const removedConstraints = {};
1308
1309 _.each(attributes, (columnDesc, columnName) => {
1310 if (!columns[columnName] && !columns[attributes[columnName].field]) {
1311 changes.push(() => this.QueryInterface.addColumn(this.getTableName(options), attributes[columnName].field || columnName, attributes[columnName]));
1312 }
1313 });
1314 _.each(columns, (columnDesc, columnName) => {
1315 const currentAttribute = rawAttributes[columnName];
1316 if (!currentAttribute) {
1317 changes.push(() => this.QueryInterface.removeColumn(this.getTableName(options), columnName, options));
1318 } else if (!currentAttribute.primaryKey) {
1319 // Check foreign keys. If it's a foreign key, it should remove constraint first.
1320 const references = currentAttribute.references;
1321 if (currentAttribute.references) {
1322 const database = this.sequelize.config.database;
1323 const schema = this.sequelize.config.schema;
1324 // Find existed foreign keys
1325 _.each(foreignKeyReferences, foreignKeyReference => {
1326 const constraintName = foreignKeyReference.constraintName;
1327 if (!!constraintName
1328 && foreignKeyReference.tableCatalog === database
1329 && (schema ? foreignKeyReference.tableSchema === schema : true)
1330 && foreignKeyReference.referencedTableName === references.model
1331 && foreignKeyReference.referencedColumnName === references.key
1332 && (schema ? foreignKeyReference.referencedTableSchema === schema : true)
1333 && !removedConstraints[constraintName]) {
1334 // Remove constraint on foreign keys.
1335 changes.push(() => this.QueryInterface.removeConstraint(this.getTableName(options), constraintName, options));
1336 removedConstraints[constraintName] = true;
1337 }
1338 });
1339 }
1340 changes.push(() => this.QueryInterface.changeColumn(this.getTableName(options), columnName, currentAttribute));
1341 }
1342 });
1343 return Promise.each(changes, f => f());
1344 });
1345 })
1346 .then(() => this.QueryInterface.showIndex(this.getTableName(options), options))
1347 .then(indexes => {
1348 indexes = this._indexes.filter(item1 =>
1349 !indexes.some(item2 => item1.name === item2.name)
1350 ).sort((index1, index2) => {
1351 if (this.sequelize.options.dialect === 'postgres') {
1352 // move concurrent indexes to the bottom to avoid weird deadlocks
1353 if (index1.concurrently === true) return 1;
1354 if (index2.concurrently === true) return -1;
1355 }
1356
1357 return 0;
1358 });
1359
1360 return Promise.each(indexes, index => this.QueryInterface.addIndex(
1361 this.getTableName(options),
1362 Object.assign({
1363 logging: options.logging,
1364 benchmark: options.benchmark,
1365 transaction: options.transaction,
1366 schema: options.schema
1367 }, index),
1368 this.tableName
1369 ));
1370 }).then(() => {
1371 if (options.hooks) {
1372 return this.runHooks('afterSync', options);
1373 }
1374 }).return(this);
1375 }
1376
1377 /**
1378 * Drop the table represented by this Model
1379 *
1380 * @param {Object} [options] drop options
1381 * @param {boolean} [options.cascade=false] Also drop all objects depending on this table, such as views. Only works in postgres
1382 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1383 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1384 *
1385 * @returns {Promise}
1386 */
1387 static drop(options) {
1388 return this.QueryInterface.dropTable(this.getTableName(options), options);
1389 }
1390
1391 static dropSchema(schema) {
1392 return this.QueryInterface.dropSchema(schema);
1393 }
1394
1395 /**
1396 * Apply a schema to this model. For postgres, this will actually place the schema in front of the table name - `"schema"."tableName"`,
1397 * while the schema will be prepended to the table name for mysql and sqlite - `'schema.tablename'`.
1398 *
1399 * This method is intended for use cases where the same model is needed in multiple schemas. In such a use case it is important
1400 * to call `model.schema(schema, [options]).sync()` for each model to ensure the models are created in the correct schema.
1401 *
1402 * If a single default schema per model is needed, set the `options.schema='schema'` parameter during the `define()` call
1403 * for the model.
1404 *
1405 * @param {string} schema The name of the schema
1406 * @param {Object} [options] schema options
1407 * @param {string} [options.schemaDelimiter='.'] The character(s) that separates the schema name from the table name
1408 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1409 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1410 *
1411 * @see
1412 * {@link Sequelize#define} for more information about setting a default schema.
1413 *
1414 * @returns {Model}
1415 */
1416 static schema(schema, options) {
1417
1418 const clone = class extends this {};
1419 Object.defineProperty(clone, 'name', { value: this.name });
1420
1421 clone._schema = schema;
1422
1423 if (options) {
1424 if (typeof options === 'string') {
1425 clone._schemaDelimiter = options;
1426 } else if (options.schemaDelimiter) {
1427 clone._schemaDelimiter = options.schemaDelimiter;
1428 }
1429 }
1430
1431 return clone;
1432 }
1433
1434 /**
1435 * Get the table name of the model, taking schema into account. The method will return The name as a string if the model has no schema,
1436 * or an object with `tableName`, `schema` and `delimiter` properties.
1437 *
1438 * @returns {string|Object}
1439 */
1440 static getTableName() {
1441 return this.QueryGenerator.addSchema(this);
1442 }
1443
1444 /**
1445 * Get un-scoped model
1446 *
1447 * @returns {Model}
1448 */
1449 static unscoped() {
1450 return this.scope();
1451 }
1452
1453 /**
1454 * Add a new scope to the model. This is especially useful for adding scopes with includes, when the model you want to include is not available at the time this model is defined.
1455 *
1456 * By default this will throw an error if a scope with that name already exists. Pass `override: true` in the options object to silence this error.
1457 *
1458 * @param {string} name The name of the scope. Use `defaultScope` to override the default scope
1459 * @param {Object|Function} scope scope or options
1460 * @param {Object} [options] scope options
1461 * @param {boolean} [options.override=false] override old scope if already defined
1462 */
1463 static addScope(name, scope, options) {
1464 options = Object.assign({
1465 override: false
1466 }, options);
1467
1468 if ((name === 'defaultScope' && Object.keys(this.options.defaultScope).length > 0 || name in this.options.scopes) && options.override === false) {
1469 throw new Error(`The scope ${name} already exists. Pass { override: true } as options to silence this error`);
1470 }
1471
1472 if (name === 'defaultScope') {
1473 this.options.defaultScope = this._scope = scope;
1474 } else {
1475 this.options.scopes[name] = scope;
1476 }
1477 }
1478
1479 /**
1480 * Apply a scope created in `define` to the model.
1481 *
1482 * @example <caption>how to create scopes</caption>
1483 * const Model = sequelize.define('model', attributes, {
1484 * defaultScope: {
1485 * where: {
1486 * username: 'dan'
1487 * },
1488 * limit: 12
1489 * },
1490 * scopes: {
1491 * isALie: {
1492 * where: {
1493 * stuff: 'cake'
1494 * }
1495 * },
1496 * complexFunction: function(email, accessLevel) {
1497 * return {
1498 * where: {
1499 * email: {
1500 * [Op.like]: email
1501 * },
1502 * access_level {
1503 * [Op.gte]: accessLevel
1504 * }
1505 * }
1506 * }
1507 * }
1508 * }
1509 * })
1510 *
1511 * # As you have defined a default scope, every time you do Model.find, the default scope is appended to your query. Here's a couple of examples:
1512 *
1513 * Model.findAll() // WHERE username = 'dan'
1514 * Model.findAll({ where: { age: { [Op.gt]: 12 } } }) // WHERE age > 12 AND username = 'dan'
1515 *
1516 * @example <caption>To invoke scope functions you can do</caption>
1517 * Model.scope({ method: ['complexFunction', 'dan@sequelize.com', 42]}).findAll()
1518 * // WHERE email like 'dan@sequelize.com%' AND access_level >= 42
1519 *
1520 * @param {?Array|Object|string} [option] The scope(s) to apply. Scopes can either be passed as consecutive arguments, or as an array of arguments. To apply simple scopes and scope functions with no arguments, pass them as strings. For scope function, pass an object, with a `method` property. The value can either be a string, if the method does not take any arguments, or an array, where the first element is the name of the method, and consecutive elements are arguments to that method. Pass null to remove all scopes, including the default.
1521 *
1522 * @returns {Model} A reference to the model, with the scope(s) applied. Calling scope again on the returned model will clear the previous scope.
1523 */
1524 static scope(option) {
1525 const self = class extends this {};
1526 let scope;
1527 let scopeName;
1528
1529 Object.defineProperty(self, 'name', { value: this.name });
1530
1531 self._scope = {};
1532 self._scopeNames = [];
1533 self.scoped = true;
1534
1535 if (!option) {
1536 return self;
1537 }
1538
1539 const options = _.flatten(arguments);
1540
1541 for (const option of options) {
1542 scope = null;
1543 scopeName = null;
1544
1545 if (_.isPlainObject(option)) {
1546 if (option.method) {
1547 if (Array.isArray(option.method) && !!self.options.scopes[option.method[0]]) {
1548 scopeName = option.method[0];
1549 scope = self.options.scopes[scopeName].apply(self, option.method.slice(1));
1550 }
1551 else if (self.options.scopes[option.method]) {
1552 scopeName = option.method;
1553 scope = self.options.scopes[scopeName].apply(self);
1554 }
1555 } else {
1556 scope = option;
1557 }
1558 } else if (option === 'defaultScope' && _.isPlainObject(self.options.defaultScope)) {
1559 scope = self.options.defaultScope;
1560 } else {
1561 scopeName = option;
1562 scope = self.options.scopes[scopeName];
1563 if (typeof scope === 'function') {
1564 scope = scope();
1565 }
1566 }
1567
1568 if (scope) {
1569 this._conformIncludes(scope, this);
1570 this._assignOptions(self._scope, scope);
1571 self._scopeNames.push(scopeName ? scopeName : 'defaultScope');
1572 } else {
1573 throw new sequelizeErrors.SequelizeScopeError(`Invalid scope ${scopeName} called.`);
1574 }
1575 }
1576
1577 return self;
1578 }
1579
1580 /**
1581 * Search for multiple instances.
1582 *
1583 * @example <caption>Simple search using AND and =</caption>
1584 * Model.findAll({
1585 * where: {
1586 * attr1: 42,
1587 * attr2: 'cake'
1588 * }
1589 * })
1590 *
1591 * # WHERE attr1 = 42 AND attr2 = 'cake'
1592 *
1593 * @example <caption>Using greater than, less than etc.</caption>
1594 * const {gt, lte, ne, in: opIn} = Sequelize.Op;
1595 *
1596 * Model.findAll({
1597 * where: {
1598 * attr1: {
1599 * [gt]: 50
1600 * },
1601 * attr2: {
1602 * [lte]: 45
1603 * },
1604 * attr3: {
1605 * [opIn]: [1,2,3]
1606 * },
1607 * attr4: {
1608 * [ne]: 5
1609 * }
1610 * }
1611 * })
1612 *
1613 * # WHERE attr1 > 50 AND attr2 <= 45 AND attr3 IN (1,2,3) AND attr4 != 5
1614 *
1615 * @example <caption>Queries using OR</caption>
1616 * const {or, and, gt, lt} = Sequelize.Op;
1617 *
1618 * Model.findAll({
1619 * where: {
1620 * name: 'a project',
1621 * [or]: [
1622 * {id: [1, 2, 3]},
1623 * {
1624 * [and]: [
1625 * {id: {[gt]: 10}},
1626 * {id: {[lt]: 100}}
1627 * ]
1628 * }
1629 * ]
1630 * }
1631 * });
1632 *
1633 * # WHERE `Model`.`name` = 'a project' AND (`Model`.`id` IN (1, 2, 3) OR (`Model`.`id` > 10 AND `Model`.`id` < 100));
1634 *
1635 * @see
1636 * {@link Operators} for possible operators
1637 * __Alias__: _all_
1638 *
1639 * The promise is resolved with an array of Model instances if the query succeeds._
1640 *
1641 * @param {Object} [options] A hash of options to describe the scope of the search
1642 * @param {Object} [options.where] A hash of attributes to describe your search. See above for examples.
1643 * @param {Array<string>|Object} [options.attributes] A list of the attributes that you want to select, or an object with `include` and `exclude` keys. To rename an attribute, you can pass an array, with two elements - the first is the name of the attribute in the DB (or some kind of expression such as `Sequelize.literal`, `Sequelize.fn` and so on), and the second is the name you want the attribute to have in the returned instance
1644 * @param {Array<string>} [options.attributes.include] Select all the attributes of the model, plus some additional ones. Useful for aggregations, e.g. `{ attributes: { include: [[sequelize.fn('COUNT', sequelize.col('id')), 'total']] }`
1645 * @param {Array<string>} [options.attributes.exclude] Select all the attributes of the model, except some few. Useful for security purposes e.g. `{ attributes: { exclude: ['password'] } }`
1646 * @param {boolean} [options.paranoid=true] If true, only non-deleted records will be returned. If false, both deleted and non-deleted records will be returned. Only applies if `options.paranoid` is true for the model.
1647 * @param {Array<Object|Model|string>} [options.include] A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }]}` or `{ include: ['Alias']}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y).
1648 * @param {Model} [options.include[].model] The model you want to eagerly load
1649 * @param {string} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliased. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural
1650 * @param {Association} [options.include[].association] The association you want to eagerly load. (This can be used instead of providing a model/as pair)
1651 * @param {Object} [options.include[].where] Where clauses to apply to the child models. Note that this converts the eager load to an inner join, unless you explicitly set `required: false`
1652 * @param {boolean} [options.include[].or=false] Whether to bind the ON and WHERE clause together by OR instead of AND.
1653 * @param {Object} [options.include[].on] Supply your own ON condition for the join.
1654 * @param {Array<string>} [options.include[].attributes] A list of attributes to select from the child model
1655 * @param {boolean} [options.include[].required] If true, converts to an inner join, which means that the parent model will only be loaded if it has any matching children. True if `include.where` is set, false otherwise.
1656 * @param {boolean} [options.include[].separate] If true, runs a separate query to fetch the associated instances, only supported for hasMany associations
1657 * @param {number} [options.include[].limit] Limit the joined rows, only supported with include.separate=true
1658 * @param {Object} [options.include[].through.where] Filter on the join model for belongsToMany relations
1659 * @param {Array} [options.include[].through.attributes] A list of attributes to select from the join model for belongsToMany relations
1660 * @param {Array<Object|Model|string>} [options.include[].include] Load further nested related models
1661 * @param {boolean} [options.include[].duplicating] Mark the include as duplicating, will prevent a subquery from being used.
1662 * @param {Array|Sequelize.fn|Sequelize.col|Sequelize.literal} [options.order] Specifies an ordering. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not.
1663 * @param {number} [options.limit] Limit for result
1664 * @param {number} [options.offset] Offset for result
1665 * @param {Transaction} [options.transaction] Transaction to run query under
1666 * @param {string|Object} [options.lock] Lock the selected rows. Possible options are transaction.LOCK.UPDATE and transaction.LOCK.SHARE. Postgres also supports transaction.LOCK.KEY_SHARE, transaction.LOCK.NO_KEY_UPDATE and specific model locks with joins. See [transaction.LOCK for an example](transaction#lock)
1667 * @param {boolean} [options.skipLocked] Skip locked rows. Only supported in Postgres.
1668 * @param {boolean} [options.raw] Return raw result. See sequelize.query for more information.
1669 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1670 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1671 * @param {Object} [options.having] Having options
1672 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1673 * @param {boolean|Error} [options.rejectOnEmpty=false] Throws an error when no records found
1674 *
1675 * @see
1676 * {@link Sequelize#query}
1677 *
1678 * @returns {Promise<Array<Model>>}
1679 */
1680 static findAll(options) {
1681 if (options !== undefined && !_.isPlainObject(options)) {
1682 throw new sequelizeErrors.QueryError('The argument passed to findAll must be an options object, use findByPk if you wish to pass a single primary key value');
1683 }
1684
1685 if (options !== undefined && options.attributes) {
1686 if (!Array.isArray(options.attributes) && !_.isPlainObject(options.attributes)) {
1687 throw new sequelizeErrors.QueryError('The attributes option must be an array of column names or an object');
1688 }
1689 }
1690
1691 this.warnOnInvalidOptions(options, Object.keys(this.rawAttributes));
1692
1693 const tableNames = {};
1694
1695 tableNames[this.getTableName(options)] = true;
1696 options = Utils.cloneDeep(options);
1697
1698 _.defaults(options, { hooks: true });
1699
1700 // set rejectOnEmpty option, defaults to model options
1701 options.rejectOnEmpty = Object.prototype.hasOwnProperty.call(options, 'rejectOnEmpty')
1702 ? options.rejectOnEmpty
1703 : this.options.rejectOnEmpty;
1704
1705 return Promise.try(() => {
1706 this._injectScope(options);
1707
1708 if (options.hooks) {
1709 return this.runHooks('beforeFind', options);
1710 }
1711 }).then(() => {
1712 this._conformIncludes(options, this);
1713 this._expandAttributes(options);
1714 this._expandIncludeAll(options);
1715
1716 if (options.hooks) {
1717 return this.runHooks('beforeFindAfterExpandIncludeAll', options);
1718 }
1719 }).then(() => {
1720 options.originalAttributes = this._injectDependentVirtualAttributes(options.attributes);
1721
1722 if (options.include) {
1723 options.hasJoin = true;
1724
1725 this._validateIncludedElements(options, tableNames);
1726
1727 // If we're not raw, we have to make sure we include the primary key for de-duplication
1728 if (
1729 options.attributes
1730 && !options.raw
1731 && this.primaryKeyAttribute
1732 && !options.attributes.includes(this.primaryKeyAttribute)
1733 && (!options.group || !options.hasSingleAssociation || options.hasMultiAssociation)
1734 ) {
1735 options.attributes = [this.primaryKeyAttribute].concat(options.attributes);
1736 }
1737 }
1738
1739 if (!options.attributes) {
1740 options.attributes = Object.keys(this.rawAttributes);
1741 options.originalAttributes = this._injectDependentVirtualAttributes(options.attributes);
1742 }
1743
1744 // whereCollection is used for non-primary key updates
1745 this.options.whereCollection = options.where || null;
1746
1747 Utils.mapFinderOptions(options, this);
1748
1749 options = this._paranoidClause(this, options);
1750
1751 if (options.hooks) {
1752 return this.runHooks('beforeFindAfterOptions', options);
1753 }
1754 }).then(() => {
1755 const selectOptions = Object.assign({}, options, { tableNames: Object.keys(tableNames) });
1756 return this.QueryInterface.select(this, this.getTableName(selectOptions), selectOptions);
1757 }).tap(results => {
1758 if (options.hooks) {
1759 return this.runHooks('afterFind', results, options);
1760 }
1761 }).then(results => {
1762
1763 //rejectOnEmpty mode
1764 if (_.isEmpty(results) && options.rejectOnEmpty) {
1765 if (typeof options.rejectOnEmpty === 'function') {
1766 throw new options.rejectOnEmpty();
1767 }
1768 if (typeof options.rejectOnEmpty === 'object') {
1769 throw options.rejectOnEmpty;
1770 }
1771 throw new sequelizeErrors.EmptyResultError();
1772 }
1773
1774 return Model._findSeparate(results, options);
1775 });
1776 }
1777
1778 static warnOnInvalidOptions(options, validColumnNames) {
1779 if (!_.isPlainObject(options)) {
1780 return;
1781 }
1782
1783 const unrecognizedOptions = Object.keys(options).filter(k => !validQueryKeywords.has(k));
1784 const unexpectedModelAttributes = _.intersection(unrecognizedOptions, validColumnNames);
1785 if (!options.where && unexpectedModelAttributes.length > 0) {
1786 logger.warn(`Model attributes (${unexpectedModelAttributes.join(', ')}) passed into finder method options of model ${this.name}, but the options.where object is empty. Did you forget to use options.where?`);
1787 }
1788 }
1789
1790 static _injectDependentVirtualAttributes(attributes) {
1791 if (!this._hasVirtualAttributes) return attributes;
1792 if (!attributes || !Array.isArray(attributes)) return attributes;
1793
1794 for (const attribute of attributes) {
1795 if (
1796 this._virtualAttributes.has(attribute)
1797 && this.rawAttributes[attribute].type.fields
1798 ) {
1799 attributes = attributes.concat(this.rawAttributes[attribute].type.fields);
1800 }
1801 }
1802
1803 attributes = _.uniq(attributes);
1804
1805 return attributes;
1806 }
1807
1808 static _findSeparate(results, options) {
1809 if (!options.include || options.raw || !results) return Promise.resolve(results);
1810
1811 const original = results;
1812 if (options.plain) results = [results];
1813
1814 if (!results.length) return original;
1815
1816 return Promise.map(options.include, include => {
1817 if (!include.separate) {
1818 return Model._findSeparate(
1819 results.reduce((memo, result) => {
1820 let associations = result.get(include.association.as);
1821
1822 // Might be an empty belongsTo relation
1823 if (!associations) return memo;
1824
1825 // Force array so we can concat no matter if it's 1:1 or :M
1826 if (!Array.isArray(associations)) associations = [associations];
1827
1828 for (let i = 0, len = associations.length; i !== len; ++i) {
1829 memo.push(associations[i]);
1830 }
1831 return memo;
1832 }, []),
1833 Object.assign(
1834 {},
1835 _.omit(options, 'include', 'attributes', 'order', 'where', 'limit', 'offset', 'plain', 'scope'),
1836 { include: include.include || [] }
1837 )
1838 );
1839 }
1840
1841 return include.association.get(results, Object.assign(
1842 {},
1843 _.omit(options, nonCascadingOptions),
1844 _.omit(include, ['parent', 'association', 'as', 'originalAttributes'])
1845 )).then(map => {
1846 for (const result of results) {
1847 result.set(
1848 include.association.as,
1849 map[result.get(include.association.sourceKey)],
1850 { raw: true }
1851 );
1852 }
1853 });
1854 }).return(original);
1855 }
1856
1857 /**
1858 * Search for a single instance by its primary key._
1859 *
1860 * @param {number|string|Buffer} param The value of the desired instance's primary key.
1861 * @param {Object} [options] find options
1862 * @param {Transaction} [options.transaction] Transaction to run query under
1863 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1864 *
1865 * @see
1866 * {@link Model.findAll} for a full explanation of options, Note that options.where is not supported.
1867 *
1868 * @returns {Promise<Model>}
1869 */
1870 static findByPk(param, options) {
1871 // return Promise resolved with null if no arguments are passed
1872 if ([null, undefined].includes(param)) {
1873 return Promise.resolve(null);
1874 }
1875
1876 options = Utils.cloneDeep(options) || {};
1877
1878 if (typeof param === 'number' || typeof param === 'string' || Buffer.isBuffer(param)) {
1879 options.where = {
1880 [this.primaryKeyAttribute]: param
1881 };
1882 } else {
1883 throw new Error(`Argument passed to findByPk is invalid: ${param}`);
1884 }
1885
1886 // Bypass a possible overloaded findOne
1887 return this.findOne(options);
1888 }
1889
1890 /**
1891 * Search for a single instance. This applies LIMIT 1, so the listener will always be called with a single instance.
1892 *
1893 * __Alias__: _find_
1894 *
1895 * @param {Object} [options] A hash of options to describe the scope of the search
1896 * @param {Transaction} [options.transaction] Transaction to run query under
1897 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1898 *
1899 * @see
1900 * {@link Model.findAll} for an explanation of options
1901 *
1902 * @returns {Promise<Model>}
1903 */
1904 static findOne(options) {
1905 if (options !== undefined && !_.isPlainObject(options)) {
1906 throw new Error('The argument passed to findOne must be an options object, use findByPk if you wish to pass a single primary key value');
1907 }
1908 options = Utils.cloneDeep(options);
1909
1910 if (options.limit === undefined) {
1911 const uniqueSingleColumns = _.chain(this.uniqueKeys).values().filter(c => c.fields.length === 1).map('column').value();
1912
1913 // Don't add limit if querying directly on the pk or a unique column
1914 if (!options.where || !_.some(options.where, (value, key) =>
1915 (key === this.primaryKeyAttribute || uniqueSingleColumns.includes(key)) &&
1916 (Utils.isPrimitive(value) || Buffer.isBuffer(value))
1917 )) {
1918 options.limit = 1;
1919 }
1920 }
1921
1922 // Bypass a possible overloaded findAll.
1923 return this.findAll(_.defaults(options, {
1924 plain: true
1925 }));
1926 }
1927
1928 /**
1929 * Run an aggregation method on the specified field
1930 *
1931 * @param {string} attribute The attribute to aggregate over. Can be a field name or *
1932 * @param {string} aggregateFunction The function to use for aggregation, e.g. sum, max etc.
1933 * @param {Object} [options] Query options. See sequelize.query for full options
1934 * @param {Object} [options.where] A hash of search attributes.
1935 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1936 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1937 * @param {DataTypes|string} [options.dataType] The type of the result. If `field` is a field in this Model, the default will be the type of that field, otherwise defaults to float.
1938 * @param {boolean} [options.distinct] Applies DISTINCT to the field being aggregated over
1939 * @param {Transaction} [options.transaction] Transaction to run query under
1940 * @param {boolean} [options.plain] When `true`, the first returned value of `aggregateFunction` is cast to `dataType` and returned. If additional attributes are specified, along with `group` clauses, set `plain` to `false` to return all values of all returned rows. Defaults to `true`
1941 *
1942 * @returns {Promise<DataTypes|Object>} Returns the aggregate result cast to `options.dataType`, unless `options.plain` is false, in which case the complete data result is returned.
1943 */
1944 static aggregate(attribute, aggregateFunction, options) {
1945 options = Utils.cloneDeep(options);
1946
1947 // We need to preserve attributes here as the `injectScope` call would inject non aggregate columns.
1948 const prevAttributes = options.attributes;
1949 this._injectScope(options);
1950 options.attributes = prevAttributes;
1951 this._conformIncludes(options, this);
1952
1953 if (options.include) {
1954 this._expandIncludeAll(options);
1955 this._validateIncludedElements(options);
1956 }
1957
1958 const attrOptions = this.rawAttributes[attribute];
1959 const field = attrOptions && attrOptions.field || attribute;
1960 let aggregateColumn = this.sequelize.col(field);
1961
1962 if (options.distinct) {
1963 aggregateColumn = this.sequelize.fn('DISTINCT', aggregateColumn);
1964 }
1965
1966 let { group } = options;
1967 if (Array.isArray(group) && Array.isArray(group[0])) {
1968 noDoubleNestedGroup();
1969 group = _.flatten(group);
1970 }
1971 options.attributes = _.unionBy(
1972 options.attributes,
1973 group,
1974 [[this.sequelize.fn(aggregateFunction, aggregateColumn), aggregateFunction]],
1975 a => Array.isArray(a) ? a[1] : a
1976 );
1977
1978 if (!options.dataType) {
1979 if (attrOptions) {
1980 options.dataType = attrOptions.type;
1981 } else {
1982 // Use FLOAT as fallback
1983 options.dataType = new DataTypes.FLOAT();
1984 }
1985 } else {
1986 options.dataType = this.sequelize.normalizeDataType(options.dataType);
1987 }
1988
1989 Utils.mapOptionFieldNames(options, this);
1990 options = this._paranoidClause(this, options);
1991
1992 return this.QueryInterface.rawSelect(this.getTableName(options), options, aggregateFunction, this).then( value => {
1993 if (value === null) {
1994 return 0;
1995 }
1996 return value;
1997 });
1998 }
1999
2000 /**
2001 * Count the number of records matching the provided where clause.
2002 *
2003 * If you provide an `include` option, the number of matching associations will be counted instead.
2004 *
2005 * @param {Object} [options] options
2006 * @param {Object} [options.where] A hash of search attributes.
2007 * @param {Object} [options.include] Include options. See `find` for details
2008 * @param {boolean} [options.paranoid=true] Set `true` to count only non-deleted records. Can be used on models with `paranoid` enabled
2009 * @param {boolean} [options.distinct] Apply COUNT(DISTINCT(col)) on primary key or on options.col.
2010 * @param {string} [options.col] Column on which COUNT() should be applied
2011 * @param {Array} [options.attributes] Used in conjunction with `group`
2012 * @param {Array} [options.group] For creating complex counts. Will return multiple rows as needed.
2013 * @param {Transaction} [options.transaction] Transaction to run query under
2014 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2015 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2016 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2017 *
2018 * @returns {Promise<number>}
2019 */
2020 static count(options) {
2021 return Promise.try(() => {
2022 options = Utils.cloneDeep(options);
2023 options = _.defaults(options, { hooks: true });
2024 options.raw = true;
2025 if (options.hooks) {
2026 return this.runHooks('beforeCount', options);
2027 }
2028 }).then(() => {
2029 let col = options.col || '*';
2030 if (options.include) {
2031 col = `${this.name}.${options.col || this.primaryKeyField}`;
2032 }
2033
2034 options.plain = !options.group;
2035 options.dataType = new DataTypes.INTEGER();
2036 options.includeIgnoreAttributes = false;
2037
2038 // No limit, offset or order for the options max be given to count()
2039 // Set them to null to prevent scopes setting those values
2040 options.limit = null;
2041 options.offset = null;
2042 options.order = null;
2043
2044 return this.aggregate(col, 'count', options);
2045 });
2046 }
2047
2048 /**
2049 * Find all the rows matching your query, within a specified offset / limit, and get the total number of rows matching your query. This is very useful for paging
2050 *
2051 * @example
2052 * Model.findAndCountAll({
2053 * where: ...,
2054 * limit: 12,
2055 * offset: 12
2056 * }).then(result => {
2057 * ...
2058 * })
2059 *
2060 * # In the above example, `result.rows` will contain rows 13 through 24, while `result.count` will return the total number of rows that matched your query.
2061 *
2062 * # When you add includes, only those which are required (either because they have a where clause, or because `required` is explicitly set to true on the include) will be added to the count part.
2063 *
2064 * # Suppose you want to find all users who have a profile attached:
2065 *
2066 * User.findAndCountAll({
2067 * include: [
2068 * { model: Profile, required: true}
2069 * ],
2070 * limit 3
2071 * });
2072 *
2073 * # Because the include for `Profile` has `required` set it will result in an inner join, and only the users who have a profile will be counted. If we remove `required` from the include, both users with and without profiles will be counted
2074 *
2075 * @param {Object} [options] See findAll options
2076 *
2077 * @see
2078 * {@link Model.findAll} for a specification of find and query options
2079 * @see
2080 * {@link Model.count} for a specification of count options
2081 *
2082 * @returns {Promise<{count: number, rows: Model[]}>}
2083 */
2084 static findAndCountAll(options) {
2085 if (options !== undefined && !_.isPlainObject(options)) {
2086 throw new Error('The argument passed to findAndCountAll must be an options object, use findByPk if you wish to pass a single primary key value');
2087 }
2088
2089 const countOptions = Utils.cloneDeep(options);
2090
2091 if (countOptions.attributes) {
2092 countOptions.attributes = undefined;
2093 }
2094
2095 return Promise.all([
2096 this.count(countOptions),
2097 this.findAll(options)
2098 ])
2099 .then(([count, rows]) => ({
2100 count,
2101 rows: count === 0 ? [] : rows
2102 }));
2103 }
2104
2105 /**
2106 * Find the maximum value of field
2107 *
2108 * @param {string} field attribute / field name
2109 * @param {Object} [options] See aggregate
2110 *
2111 * @see
2112 * {@link Model.aggregate} for options
2113 *
2114 * @returns {Promise<*>}
2115 */
2116 static max(field, options) {
2117 return this.aggregate(field, 'max', options);
2118 }
2119
2120 /**
2121 * Find the minimum value of field
2122 *
2123 * @param {string} field attribute / field name
2124 * @param {Object} [options] See aggregate
2125 *
2126 * @see
2127 * {@link Model.aggregate} for options
2128 *
2129 * @returns {Promise<*>}
2130 */
2131 static min(field, options) {
2132 return this.aggregate(field, 'min', options);
2133 }
2134
2135 /**
2136 * Find the sum of field
2137 *
2138 * @param {string} field attribute / field name
2139 * @param {Object} [options] See aggregate
2140 *
2141 * @see
2142 * {@link Model.aggregate} for options
2143 *
2144 * @returns {Promise<number>}
2145 */
2146 static sum(field, options) {
2147 return this.aggregate(field, 'sum', options);
2148 }
2149
2150 /**
2151 * Builds a new model instance.
2152 *
2153 * @param {Object|Array} values An object of key value pairs or an array of such. If an array, the function will return an array of instances.
2154 * @param {Object} [options] Instance build options
2155 * @param {boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
2156 * @param {boolean} [options.isNewRecord=true] Is this new record
2157 * @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances. See `set`
2158 *
2159 * @returns {Model|Array<Model>}
2160 */
2161 static build(values, options) {
2162 if (Array.isArray(values)) {
2163 return this.bulkBuild(values, options);
2164 }
2165
2166 return new this(values, options);
2167 }
2168
2169 static bulkBuild(valueSets, options) {
2170 options = Object.assign({
2171 isNewRecord: true
2172 }, options || {});
2173
2174 if (!options.includeValidated) {
2175 this._conformIncludes(options, this);
2176 if (options.include) {
2177 this._expandIncludeAll(options);
2178 this._validateIncludedElements(options);
2179 }
2180 }
2181
2182 if (options.attributes) {
2183 options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
2184 }
2185
2186 return valueSets.map(values => this.build(values, options));
2187 }
2188
2189 /**
2190 * Builds a new model instance and calls save on it.
2191
2192 * @see
2193 * {@link Model.build}
2194 * @see
2195 * {@link Model.save}
2196 *
2197 * @param {Object} values hash of data values to create new record with
2198 * @param {Object} [options] build and query options
2199 * @param {boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
2200 * @param {boolean} [options.isNewRecord=true] Is this new record
2201 * @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances. See `set`
2202 * @param {Array} [options.fields] If set, only columns matching those in fields will be saved
2203 * @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
2204 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
2205 * @param {boolean} [options.validate=true] If false, validations won't be run.
2206 * @param {boolean} [options.hooks=true] Run before and after create / update + validate hooks
2207 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2208 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2209 * @param {Transaction} [options.transaction] Transaction to run query under
2210 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2211 * @param {boolean} [options.returning=true] Return the affected rows (only for postgres)
2212 *
2213 * @returns {Promise<Model>}
2214 *
2215 */
2216 static create(values, options) {
2217 options = Utils.cloneDeep(options || {});
2218
2219 return this.build(values, {
2220 isNewRecord: true,
2221 attributes: options.fields,
2222 include: options.include,
2223 raw: options.raw,
2224 silent: options.silent
2225 }).save(options);
2226 }
2227
2228 /**
2229 * Find a row that matches the query, or build (but don't save) the row if none is found.
2230 * The successful result of the promise will be (instance, built)
2231 *
2232 * @param {Object} options find options
2233 * @param {Object} options.where A hash of search attributes. If `where` is a plain object it will be appended with defaults to build a new instance.
2234 * @param {Object} [options.defaults] Default values to use if building a new instance
2235 * @param {Object} [options.transaction] Transaction to run query under
2236 *
2237 * @returns {Promise<Model,boolean>}
2238 */
2239 static findOrBuild(options) {
2240 if (!options || !options.where || arguments.length > 1) {
2241 throw new Error(
2242 'Missing where attribute in the options parameter passed to findOrBuild. ' +
2243 'Please note that the API has changed, and is now options only (an object with where, defaults keys, transaction etc.)'
2244 );
2245 }
2246
2247 let values;
2248
2249 return this.findOne(options).then(instance => {
2250 if (instance === null) {
2251 values = _.clone(options.defaults) || {};
2252 if (_.isPlainObject(options.where)) {
2253 values = Utils.defaults(values, options.where);
2254 }
2255
2256 instance = this.build(values, options);
2257
2258 return Promise.resolve([instance, true]);
2259 }
2260
2261 return Promise.resolve([instance, false]);
2262 });
2263 }
2264
2265 /**
2266 * Find a row that matches the query, or build and save the row if none is found
2267 * The successful result of the promise will be (instance, created)
2268 *
2269 * If no transaction is passed in the `options` object, a new transaction will be created internally, to prevent the race condition where a matching row is created by another connection after the find but before the insert call.
2270 * However, it is not always possible to handle this case in SQLite, specifically if one transaction inserts and another tries to select before the first one has committed. In this case, an instance of sequelize. TimeoutError will be thrown instead.
2271 * If a transaction is created, a savepoint will be created instead, and any unique constraint violation will be handled internally.
2272 *
2273 * @see
2274 * {@link Model.findAll} for a full specification of find and options
2275 *
2276 * @param {Object} options find and create options
2277 * @param {Object} options.where where A hash of search attributes. If `where` is a plain object it will be appended with defaults to build a new instance.
2278 * @param {Object} [options.defaults] Default values to use if creating a new instance
2279 * @param {Transaction} [options.transaction] Transaction to run query under
2280 *
2281 * @returns {Promise<Model,boolean>}
2282 */
2283 static findOrCreate(options) {
2284 if (!options || !options.where || arguments.length > 1) {
2285 throw new Error(
2286 'Missing where attribute in the options parameter passed to findOrCreate. ' +
2287 'Please note that the API has changed, and is now options only (an object with where, defaults keys, transaction etc.)'
2288 );
2289 }
2290
2291 options = Object.assign({}, options);
2292
2293 if (options.defaults) {
2294 const defaults = Object.keys(options.defaults);
2295 const unknownDefaults = defaults.filter(name => !this.rawAttributes[name]);
2296
2297 if (unknownDefaults.length) {
2298 logger.warn(`Unknown attributes (${unknownDefaults}) passed to defaults option of findOrCreate`);
2299 }
2300 }
2301
2302 if (options.transaction === undefined && this.sequelize.constructor._cls) {
2303 const t = this.sequelize.constructor._cls.get('transaction');
2304 if (t) {
2305 options.transaction = t;
2306 }
2307 }
2308
2309 const internalTransaction = !options.transaction;
2310 let values;
2311 let transaction;
2312
2313 // Create a transaction or a savepoint, depending on whether a transaction was passed in
2314 return this.sequelize.transaction(options).then(t => {
2315 transaction = t;
2316 options.transaction = t;
2317
2318 return this.findOne(Utils.defaults({ transaction }, options));
2319 }).then(instance => {
2320 if (instance !== null) {
2321 return [instance, false];
2322 }
2323
2324 values = _.clone(options.defaults) || {};
2325 if (_.isPlainObject(options.where)) {
2326 values = Utils.defaults(values, options.where);
2327 }
2328
2329 options.exception = true;
2330
2331 return this.create(values, options).then(instance => {
2332 if (instance.get(this.primaryKeyAttribute, { raw: true }) === null) {
2333 // If the query returned an empty result for the primary key, we know that this was actually a unique constraint violation
2334 throw new sequelizeErrors.UniqueConstraintError();
2335 }
2336
2337 return [instance, true];
2338 }).catch(sequelizeErrors.UniqueConstraintError, err => {
2339 const flattenedWhere = Utils.flattenObjectDeep(options.where);
2340 const flattenedWhereKeys = Object.keys(flattenedWhere).map(name => _.last(name.split('.')));
2341 const whereFields = flattenedWhereKeys.map(name => _.get(this.rawAttributes, `${name}.field`, name));
2342 const defaultFields = options.defaults && Object.keys(options.defaults)
2343 .filter(name => this.rawAttributes[name])
2344 .map(name => this.rawAttributes[name].field || name);
2345
2346 const errFieldKeys = Object.keys(err.fields);
2347 const errFieldsWhereIntersects = Utils.intersects(errFieldKeys, whereFields);
2348 if (defaultFields && !errFieldsWhereIntersects && Utils.intersects(errFieldKeys, defaultFields)) {
2349 throw err;
2350 }
2351
2352 if (errFieldsWhereIntersects) {
2353 _.each(err.fields, (value, key) => {
2354 const name = this.fieldRawAttributesMap[key].fieldName;
2355 if (value.toString() !== options.where[name].toString()) {
2356 throw new Error(`${this.name}#findOrCreate: value used for ${name} was not equal for both the find and the create calls, '${options.where[name]}' vs '${value}'`);
2357 }
2358 });
2359 }
2360
2361 // Someone must have created a matching instance inside the same transaction since we last did a find. Let's find it!
2362 return this.findOne(Utils.defaults({
2363 transaction: internalTransaction ? null : transaction
2364 }, options)).then(instance => {
2365 // Sanity check, ideally we caught this at the defaultFeilds/err.fields check
2366 // But if we didn't and instance is null, we will throw
2367 if (instance === null) throw err;
2368 return [instance, false];
2369 });
2370 });
2371 }).finally(() => {
2372 if (internalTransaction && transaction) {
2373 // If we created a transaction internally (and not just a savepoint), we should clean it up
2374 return transaction.commit();
2375 }
2376 });
2377 }
2378
2379 /**
2380 * A more performant findOrCreate that will not work under a transaction (at least not in postgres)
2381 * Will execute a find call, if empty then attempt to create, if unique constraint then attempt to find again
2382 *
2383 * @see
2384 * {@link Model.findAll} for a full specification of find and options
2385 *
2386 * @param {Object} options find options
2387 * @param {Object} options.where A hash of search attributes. If `where` is a plain object it will be appended with defaults to build a new instance.
2388 * @param {Object} [options.defaults] Default values to use if creating a new instance
2389 *
2390 * @returns {Promise<Model,boolean>}
2391 */
2392 static findCreateFind(options) {
2393 if (!options || !options.where) {
2394 throw new Error(
2395 'Missing where attribute in the options parameter passed to findCreateFind.'
2396 );
2397 }
2398
2399 let values = _.clone(options.defaults) || {};
2400 if (_.isPlainObject(options.where)) {
2401 values = Utils.defaults(values, options.where);
2402 }
2403
2404
2405 return this.findOne(options).then(result => {
2406 if (result) return [result, false];
2407
2408 return this.create(values, options)
2409 .then(result => [result, true])
2410 .catch(sequelizeErrors.UniqueConstraintError, () => this.findOne(options).then(result => [result, false]));
2411 });
2412 }
2413
2414 /**
2415 * Insert or update a single row. An update will be executed if a row which matches the supplied values on either the primary key or a unique key is found. Note that the unique index must be defined in your sequelize model and not just in the table. Otherwise you may experience a unique constraint violation, because sequelize fails to identify the row that should be updated.
2416 *
2417 * **Implementation details:**
2418 *
2419 * * MySQL - Implemented as a single query `INSERT values ON DUPLICATE KEY UPDATE values`
2420 * * PostgreSQL - Implemented as a temporary function with exception handling: INSERT EXCEPTION WHEN unique_constraint UPDATE
2421 * * SQLite - Implemented as two queries `INSERT; UPDATE`. This means that the update is executed regardless of whether the row already existed or not
2422 * * MSSQL - Implemented as a single query using `MERGE` and `WHEN (NOT) MATCHED THEN`
2423 * **Note** that SQLite returns undefined for created, no matter if the row was created or updated. This is because SQLite always runs INSERT OR IGNORE + UPDATE, in a single query, so there is no way to know whether the row was inserted or not.
2424 *
2425 * @param {Object} values hash of values to upsert
2426 * @param {Object} [options] upsert options
2427 * @param {boolean} [options.validate=true] Run validations before the row is inserted
2428 * @param {Array} [options.fields=Object.keys(this.attributes)] The fields to insert / update. Defaults to all changed fields
2429 * @param {boolean} [options.hooks=true] Run before / after upsert hooks?
2430 * @param {boolean} [options.returning=false] Append RETURNING * to get back auto generated values (Postgres only)
2431 * @param {Transaction} [options.transaction] Transaction to run query under
2432 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2433 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2434 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2435 *
2436 * @returns {Promise<boolean>} Returns a boolean indicating whether the row was created or updated. For MySQL/MariaDB, it returns `true` when inserted and `false` when updated. For Postgres/MSSQL with (options.returning=true), it returns record and created boolean with signature `<Model, created>`.
2437 */
2438 static upsert(values, options) {
2439 options = Object.assign({
2440 hooks: true,
2441 returning: false,
2442 validate: true
2443 }, Utils.cloneDeep(options || {}));
2444
2445 options.model = this;
2446
2447 const createdAtAttr = this._timestampAttributes.createdAt;
2448 const updatedAtAttr = this._timestampAttributes.updatedAt;
2449 const hasPrimary = this.primaryKeyField in values || this.primaryKeyAttribute in values;
2450 const instance = this.build(values);
2451
2452 if (!options.fields) {
2453 options.fields = Object.keys(instance._changed);
2454 }
2455
2456 return Promise.try(() => {
2457 if (options.validate) {
2458 return instance.validate(options);
2459 }
2460 }).then(() => {
2461 // Map field names
2462 const updatedDataValues = _.pick(instance.dataValues, Object.keys(instance._changed));
2463 const insertValues = Utils.mapValueFieldNames(instance.dataValues, Object.keys(instance.rawAttributes), this);
2464 const updateValues = Utils.mapValueFieldNames(updatedDataValues, options.fields, this);
2465 const now = Utils.now(this.sequelize.options.dialect);
2466
2467 // Attach createdAt
2468 if (createdAtAttr && !updateValues[createdAtAttr]) {
2469 const field = this.rawAttributes[createdAtAttr].field || createdAtAttr;
2470 insertValues[field] = this._getDefaultTimestamp(createdAtAttr) || now;
2471 }
2472 if (updatedAtAttr && !insertValues[updatedAtAttr]) {
2473 const field = this.rawAttributes[updatedAtAttr].field || updatedAtAttr;
2474 insertValues[field] = updateValues[field] = this._getDefaultTimestamp(updatedAtAttr) || now;
2475 }
2476
2477 // Build adds a null value for the primary key, if none was given by the user.
2478 // We need to remove that because of some Postgres technicalities.
2479 if (!hasPrimary && this.primaryKeyAttribute && !this.rawAttributes[this.primaryKeyAttribute].defaultValue) {
2480 delete insertValues[this.primaryKeyField];
2481 delete updateValues[this.primaryKeyField];
2482 }
2483
2484 return Promise.try(() => {
2485 if (options.hooks) {
2486 return this.runHooks('beforeUpsert', values, options);
2487 }
2488 })
2489 .then(() => {
2490 return this.QueryInterface.upsert(this.getTableName(options), insertValues, updateValues, instance.where(), this, options);
2491 })
2492 .then(([created, primaryKey]) => {
2493 if (options.returning === true && primaryKey) {
2494 return this.findByPk(primaryKey, options).then(record => [record, created]);
2495 }
2496
2497 return created;
2498 })
2499 .tap(result => {
2500 if (options.hooks) {
2501 return this.runHooks('afterUpsert', result, options);
2502 }
2503 });
2504 });
2505 }
2506
2507 /**
2508 * Create and insert multiple instances in bulk.
2509 *
2510 * The success handler is passed an array of instances, but please notice that these may not completely represent the state of the rows in the DB. This is because MySQL
2511 * and SQLite do not make it easy to obtain back automatically generated IDs and other default values in a way that can be mapped to multiple records.
2512 * To obtain Instances for the newly created values, you will need to query for them again.
2513 *
2514 * If validation fails, the promise is rejected with an array-like [AggregateError](http://bluebirdjs.com/docs/api/aggregateerror.html)
2515 *
2516 * @param {Array} records List of objects (key/value pairs) to create instances from
2517 * @param {Object} [options] Bulk create options
2518 * @param {Array} [options.fields] Fields to insert (defaults to all fields)
2519 * @param {boolean} [options.validate=false] Should each row be subject to validation before it is inserted. The whole insert will fail if one row fails validation
2520 * @param {boolean} [options.hooks=true] Run before / after bulk create hooks?
2521 * @param {boolean} [options.individualHooks=false] Run before / after create hooks for each individual Instance? BulkCreate hooks will still be run if options.hooks is true.
2522 * @param {boolean} [options.ignoreDuplicates=false] Ignore duplicate values for primary keys? (not supported by MSSQL or Postgres < 9.5)
2523 * @param {Array} [options.updateOnDuplicate] Fields to update if row key already exists (on duplicate key update)? (only supported by MySQL, MariaDB, SQLite >= 3.24.0 & Postgres >= 9.5). By default, all fields are updated.
2524 * @param {Transaction} [options.transaction] Transaction to run query under
2525 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2526 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2527 * @param {boolean|Array} [options.returning=false] If true, append RETURNING * to get back all values; if an array of column names, append RETURNING <columns> to get back specific columns (Postgres only)
2528 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2529 *
2530 * @returns {Promise<Array<Model>>}
2531 */
2532 static bulkCreate(records, options = {}) {
2533 if (!records.length) {
2534 return Promise.resolve([]);
2535 }
2536
2537 const dialect = this.sequelize.options.dialect;
2538 const now = Utils.now(this.sequelize.options.dialect);
2539
2540 options.model = this;
2541
2542 if (!options.includeValidated) {
2543 this._conformIncludes(options, this);
2544 if (options.include) {
2545 this._expandIncludeAll(options);
2546 this._validateIncludedElements(options);
2547 }
2548 }
2549
2550 const instances = records.map(values => this.build(values, { isNewRecord: true, include: options.include }));
2551
2552 const recursiveBulkCreate = (instances, options) => {
2553 options = Object.assign({
2554 validate: false,
2555 hooks: true,
2556 individualHooks: false,
2557 ignoreDuplicates: false
2558 }, options);
2559
2560 if (options.returning === undefined) {
2561 if (options.association) {
2562 options.returning = false;
2563 } else {
2564 options.returning = true;
2565 }
2566 }
2567
2568 if (options.ignoreDuplicates && ['mssql'].includes(dialect)) {
2569 return Promise.reject(new Error(`${dialect} does not support the ignoreDuplicates option.`));
2570 }
2571 if (options.updateOnDuplicate && (dialect !== 'mysql' && dialect !== 'mariadb' && dialect !== 'sqlite' && dialect !== 'postgres')) {
2572 return Promise.reject(new Error(`${dialect} does not support the updateOnDuplicate option.`));
2573 }
2574
2575 const model = options.model;
2576
2577 options.fields = options.fields || Object.keys(model.rawAttributes);
2578 const createdAtAttr = model._timestampAttributes.createdAt;
2579 const updatedAtAttr = model._timestampAttributes.updatedAt;
2580
2581 if (options.updateOnDuplicate !== undefined) {
2582 if (Array.isArray(options.updateOnDuplicate) && options.updateOnDuplicate.length) {
2583 options.updateOnDuplicate = _.intersection(
2584 _.without(Object.keys(model.tableAttributes), createdAtAttr),
2585 options.updateOnDuplicate
2586 );
2587 } else {
2588 return Promise.reject(new Error('updateOnDuplicate option only supports non-empty array.'));
2589 }
2590 }
2591
2592 return Promise.try(() => {
2593 // Run before hook
2594 if (options.hooks) {
2595 return model.runHooks('beforeBulkCreate', instances, options);
2596 }
2597 }).then(() => {
2598 // Validate
2599 if (options.validate) {
2600 const errors = new Promise.AggregateError();
2601 const validateOptions = _.clone(options);
2602 validateOptions.hooks = options.individualHooks;
2603
2604 return Promise.map(instances, instance =>
2605 instance.validate(validateOptions).catch(err => {
2606 errors.push(new sequelizeErrors.BulkRecordError(err, instance));
2607 })
2608 ).then(() => {
2609 delete options.skip;
2610 if (errors.length) {
2611 throw errors;
2612 }
2613 });
2614 }
2615 }).then(() => {
2616 if (options.individualHooks) {
2617 // Create each instance individually
2618 return Promise.map(instances, instance => {
2619 const individualOptions = _.clone(options);
2620 delete individualOptions.fields;
2621 delete individualOptions.individualHooks;
2622 delete individualOptions.ignoreDuplicates;
2623 individualOptions.validate = false;
2624 individualOptions.hooks = true;
2625
2626 return instance.save(individualOptions);
2627 });
2628 }
2629
2630 return Promise.resolve().then(() => {
2631 if (!options.include || !options.include.length) return;
2632
2633 // Nested creation for BelongsTo relations
2634 return Promise.map(options.include.filter(include => include.association instanceof BelongsTo), include => {
2635 const associationInstances = [];
2636 const associationInstanceIndexToInstanceMap = [];
2637
2638 for (const instance of instances) {
2639 const associationInstance = instance.get(include.as);
2640 if (associationInstance) {
2641 associationInstances.push(associationInstance);
2642 associationInstanceIndexToInstanceMap.push(instance);
2643 }
2644 }
2645
2646 if (!associationInstances.length) {
2647 return;
2648 }
2649
2650 const includeOptions = _(Utils.cloneDeep(include))
2651 .omit(['association'])
2652 .defaults({
2653 transaction: options.transaction,
2654 logging: options.logging
2655 }).value();
2656
2657 return recursiveBulkCreate(associationInstances, includeOptions).then(associationInstances => {
2658 for (const idx in associationInstances) {
2659 const associationInstance = associationInstances[idx];
2660 const instance = associationInstanceIndexToInstanceMap[idx];
2661
2662 instance[include.association.accessors.set](associationInstance, { save: false, logging: options.logging });
2663 }
2664 });
2665 });
2666 }).then(() => {
2667 // Create all in one query
2668 // Recreate records from instances to represent any changes made in hooks or validation
2669 records = instances.map(instance => {
2670 const values = instance.dataValues;
2671
2672 // set createdAt/updatedAt attributes
2673 if (createdAtAttr && !values[createdAtAttr]) {
2674 values[createdAtAttr] = now;
2675 if (!options.fields.includes(createdAtAttr)) {
2676 options.fields.push(createdAtAttr);
2677 }
2678 }
2679 if (updatedAtAttr && !values[updatedAtAttr]) {
2680 values[updatedAtAttr] = now;
2681 if (!options.fields.includes(updatedAtAttr)) {
2682 options.fields.push(updatedAtAttr);
2683 }
2684 }
2685
2686 const out = Object.assign({}, Utils.mapValueFieldNames(values, options.fields, model));
2687 for (const key of model._virtualAttributes) {
2688 delete out[key];
2689 }
2690 return out;
2691 });
2692
2693 // Map attributes to fields for serial identification
2694 const fieldMappedAttributes = {};
2695 for (const attr in model.tableAttributes) {
2696 fieldMappedAttributes[model.rawAttributes[attr].field || attr] = model.rawAttributes[attr];
2697 }
2698
2699 // Map updateOnDuplicate attributes to fields
2700 if (options.updateOnDuplicate) {
2701 options.updateOnDuplicate = options.updateOnDuplicate.map(attr => model.rawAttributes[attr].field || attr);
2702 // Get primary keys for postgres to enable updateOnDuplicate
2703 options.upsertKeys = _.chain(model.primaryKeys).values().map('field').value();
2704 if (Object.keys(model.uniqueKeys).length > 0) {
2705 options.upsertKeys = _.chain(model.uniqueKeys).values().filter(c => c.fields.length === 1).map('column').value();
2706 }
2707 }
2708
2709 // Map returning attributes to fields
2710 if (options.returning && Array.isArray(options.returning)) {
2711 options.returning = options.returning.map(attr => model.rawAttributes[attr].field || attr);
2712 }
2713
2714 return model.QueryInterface.bulkInsert(model.getTableName(options), records, options, fieldMappedAttributes).then(results => {
2715 if (Array.isArray(results)) {
2716 results.forEach((result, i) => {
2717 const instance = instances[i];
2718
2719 for (const key in result) {
2720 if (!instance || key === model.primaryKeyAttribute &&
2721 instance.get(model.primaryKeyAttribute) &&
2722 ['mysql', 'mariadb', 'sqlite'].includes(dialect)) {
2723 // The query.js for these DBs is blind, it autoincrements the
2724 // primarykey value, even if it was set manually. Also, it can
2725 // return more results than instances, bug?.
2726 continue;
2727 }
2728 if (Object.prototype.hasOwnProperty.call(result, key)) {
2729 const record = result[key];
2730
2731 const attr = _.find(model.rawAttributes, attribute => attribute.fieldName === key || attribute.field === key);
2732
2733 instance.dataValues[attr && attr.fieldName || key] = record;
2734 }
2735 }
2736 });
2737 }
2738 return results;
2739 });
2740 });
2741 }).then(() => {
2742 if (!options.include || !options.include.length) return;
2743
2744 // Nested creation for HasOne/HasMany/BelongsToMany relations
2745 return Promise.map(options.include.filter(include => !(include.association instanceof BelongsTo ||
2746 include.parent && include.parent.association instanceof BelongsToMany)), include => {
2747 const associationInstances = [];
2748 const associationInstanceIndexToInstanceMap = [];
2749
2750 for (const instance of instances) {
2751 let associated = instance.get(include.as);
2752 if (!Array.isArray(associated)) associated = [associated];
2753
2754 for (const associationInstance of associated) {
2755 if (associationInstance) {
2756 if (!(include.association instanceof BelongsToMany)) {
2757 associationInstance.set(include.association.foreignKey, instance.get(include.association.sourceKey || instance.constructor.primaryKeyAttribute, { raw: true }), { raw: true });
2758 Object.assign(associationInstance, include.association.scope);
2759 }
2760 associationInstances.push(associationInstance);
2761 associationInstanceIndexToInstanceMap.push(instance);
2762 }
2763 }
2764 }
2765
2766 if (!associationInstances.length) {
2767 return;
2768 }
2769
2770 const includeOptions = _(Utils.cloneDeep(include))
2771 .omit(['association'])
2772 .defaults({
2773 transaction: options.transaction,
2774 logging: options.logging
2775 }).value();
2776
2777 return recursiveBulkCreate(associationInstances, includeOptions).then(associationInstances => {
2778 if (include.association instanceof BelongsToMany) {
2779 const valueSets = [];
2780
2781 for (const idx in associationInstances) {
2782 const associationInstance = associationInstances[idx];
2783 const instance = associationInstanceIndexToInstanceMap[idx];
2784
2785 const values = {};
2786 values[include.association.foreignKey] = instance.get(instance.constructor.primaryKeyAttribute, { raw: true });
2787 values[include.association.otherKey] = associationInstance.get(associationInstance.constructor.primaryKeyAttribute, { raw: true });
2788
2789 // Include values defined in the association
2790 Object.assign(values, include.association.through.scope);
2791 if (associationInstance[include.association.through.model.name]) {
2792 for (const attr of Object.keys(include.association.through.model.rawAttributes)) {
2793 if (include.association.through.model.rawAttributes[attr]._autoGenerated ||
2794 attr === include.association.foreignKey ||
2795 attr === include.association.otherKey ||
2796 typeof associationInstance[include.association.through.model.name][attr] === undefined) {
2797 continue;
2798 }
2799 values[attr] = associationInstance[include.association.through.model.name][attr];
2800 }
2801 }
2802
2803 valueSets.push(values);
2804 }
2805
2806 const throughOptions = _(Utils.cloneDeep(include))
2807 .omit(['association', 'attributes'])
2808 .defaults({
2809 transaction: options.transaction,
2810 logging: options.logging
2811 }).value();
2812 throughOptions.model = include.association.throughModel;
2813 const throughInstances = include.association.throughModel.bulkBuild(valueSets, throughOptions);
2814
2815 return recursiveBulkCreate(throughInstances, throughOptions);
2816 }
2817 });
2818 });
2819 }).then(() => {
2820 // map fields back to attributes
2821 instances.forEach(instance => {
2822 for (const attr in model.rawAttributes) {
2823 if (model.rawAttributes[attr].field &&
2824 instance.dataValues[model.rawAttributes[attr].field] !== undefined &&
2825 model.rawAttributes[attr].field !== attr
2826 ) {
2827 instance.dataValues[attr] = instance.dataValues[model.rawAttributes[attr].field];
2828 delete instance.dataValues[model.rawAttributes[attr].field];
2829 }
2830 instance._previousDataValues[attr] = instance.dataValues[attr];
2831 instance.changed(attr, false);
2832 }
2833 instance.isNewRecord = false;
2834 });
2835
2836 // Run after hook
2837 if (options.hooks) {
2838 return model.runHooks('afterBulkCreate', instances, options);
2839 }
2840 }).then(() => instances);
2841 };
2842
2843 return recursiveBulkCreate(instances, options);
2844 }
2845
2846 /**
2847 * Truncate all instances of the model. This is a convenient method for Model.destroy({ truncate: true }).
2848 *
2849 * @param {Object} [options] The options passed to Model.destroy in addition to truncate
2850 * @param {boolean|Function} [options.cascade = false] Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE.
2851 * @param {boolean} [options.restartIdentity=false] Automatically restart sequences owned by columns of the truncated table.
2852 * @param {Transaction} [options.transaction] Transaction to run query under
2853 * @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging
2854 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2855 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2856 *
2857 * @returns {Promise}
2858 *
2859 * @see
2860 * {@link Model.destroy} for more information
2861 */
2862 static truncate(options) {
2863 options = Utils.cloneDeep(options) || {};
2864 options.truncate = true;
2865 return this.destroy(options);
2866 }
2867
2868 /**
2869 * Delete multiple instances, or set their deletedAt timestamp to the current time if `paranoid` is enabled.
2870 *
2871 * @param {Object} options destroy options
2872 * @param {Object} [options.where] Filter the destroy
2873 * @param {boolean} [options.hooks=true] Run before / after bulk destroy hooks?
2874 * @param {boolean} [options.individualHooks=false] If set to true, destroy will SELECT all records matching the where parameter and will execute before / after destroy hooks on each row
2875 * @param {number} [options.limit] How many rows to delete
2876 * @param {boolean} [options.force=false] Delete instead of setting deletedAt to current timestamp (only applicable if `paranoid` is enabled)
2877 * @param {boolean} [options.truncate=false] 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
2878 * @param {boolean} [options.cascade=false] Only used in conjunction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE.
2879 * @param {boolean} [options.restartIdentity=false] Only used in conjunction with TRUNCATE. Automatically restart sequences owned by columns of the truncated table.
2880 * @param {Transaction} [options.transaction] Transaction to run query under
2881 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2882 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2883 *
2884 * @returns {Promise<number>} The number of destroyed rows
2885 */
2886 static destroy(options) {
2887 options = Utils.cloneDeep(options);
2888
2889 this._injectScope(options);
2890
2891 if (!options || !(options.where || options.truncate)) {
2892 throw new Error('Missing where or truncate attribute in the options parameter of model.destroy.');
2893 }
2894
2895 if (!options.truncate && !_.isPlainObject(options.where) && !Array.isArray(options.where) && !(options.where instanceof Utils.SequelizeMethod)) {
2896 throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.destroy.');
2897 }
2898
2899 options = _.defaults(options, {
2900 hooks: true,
2901 individualHooks: false,
2902 force: false,
2903 cascade: false,
2904 restartIdentity: false
2905 });
2906
2907 options.type = QueryTypes.BULKDELETE;
2908
2909 Utils.mapOptionFieldNames(options, this);
2910 options.model = this;
2911
2912 let instances;
2913
2914 return Promise.try(() => {
2915 // Run before hook
2916 if (options.hooks) {
2917 return this.runHooks('beforeBulkDestroy', options);
2918 }
2919 }).then(() => {
2920 // Get daos and run beforeDestroy hook on each record individually
2921 if (options.individualHooks) {
2922 return this.findAll({ where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark })
2923 .map(instance => this.runHooks('beforeDestroy', instance, options).then(() => instance))
2924 .then(_instances => {
2925 instances = _instances;
2926 });
2927 }
2928 }).then(() => {
2929 // Run delete query (or update if paranoid)
2930 if (this._timestampAttributes.deletedAt && !options.force) {
2931 // Set query type appropriately when running soft delete
2932 options.type = QueryTypes.BULKUPDATE;
2933
2934 const attrValueHash = {};
2935 const deletedAtAttribute = this.rawAttributes[this._timestampAttributes.deletedAt];
2936 const field = this.rawAttributes[this._timestampAttributes.deletedAt].field;
2937 const where = {
2938 [field]: Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null
2939 };
2940
2941
2942 attrValueHash[field] = Utils.now(this.sequelize.options.dialect);
2943 return this.QueryInterface.bulkUpdate(this.getTableName(options), attrValueHash, Object.assign(where, options.where), options, this.rawAttributes);
2944 }
2945 return this.QueryInterface.bulkDelete(this.getTableName(options), options.where, options, this);
2946 }).tap(() => {
2947 // Run afterDestroy hook on each record individually
2948 if (options.individualHooks) {
2949 return Promise.map(instances, instance => this.runHooks('afterDestroy', instance, options));
2950 }
2951 }).tap(() => {
2952 // Run after hook
2953 if (options.hooks) {
2954 return this.runHooks('afterBulkDestroy', options);
2955 }
2956 });
2957 }
2958
2959 /**
2960 * Restore multiple instances if `paranoid` is enabled.
2961 *
2962 * @param {Object} options restore options
2963 * @param {Object} [options.where] Filter the restore
2964 * @param {boolean} [options.hooks=true] Run before / after bulk restore hooks?
2965 * @param {boolean} [options.individualHooks=false] If set to true, restore will find all records within the where parameter and will execute before / after bulkRestore hooks on each row
2966 * @param {number} [options.limit] How many rows to undelete (only for mysql)
2967 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2968 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2969 * @param {Transaction} [options.transaction] Transaction to run query under
2970 *
2971 * @returns {Promise}
2972 */
2973 static restore(options) {
2974 if (!this._timestampAttributes.deletedAt) throw new Error('Model is not paranoid');
2975
2976 options = Object.assign({
2977 hooks: true,
2978 individualHooks: false
2979 }, options || {});
2980
2981 options.type = QueryTypes.RAW;
2982 options.model = this;
2983
2984 let instances;
2985
2986 Utils.mapOptionFieldNames(options, this);
2987
2988 return Promise.try(() => {
2989 // Run before hook
2990 if (options.hooks) {
2991 return this.runHooks('beforeBulkRestore', options);
2992 }
2993 }).then(() => {
2994 // Get daos and run beforeRestore hook on each record individually
2995 if (options.individualHooks) {
2996 return this.findAll({ where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark, paranoid: false })
2997 .map(instance => this.runHooks('beforeRestore', instance, options).then(() => instance))
2998 .then(_instances => {
2999 instances = _instances;
3000 });
3001 }
3002 }).then(() => {
3003 // Run undelete query
3004 const attrValueHash = {};
3005 const deletedAtCol = this._timestampAttributes.deletedAt;
3006 const deletedAtAttribute = this.rawAttributes[deletedAtCol];
3007 const deletedAtDefaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
3008
3009 attrValueHash[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
3010 options.omitNull = false;
3011 return this.QueryInterface.bulkUpdate(this.getTableName(options), attrValueHash, options.where, options, this.rawAttributes);
3012 }).tap(() => {
3013 // Run afterDestroy hook on each record individually
3014 if (options.individualHooks) {
3015 return Promise.map(instances, instance => this.runHooks('afterRestore', instance, options));
3016 }
3017 }).tap(() => {
3018 // Run after hook
3019 if (options.hooks) {
3020 return this.runHooks('afterBulkRestore', options);
3021 }
3022 });
3023 }
3024
3025 /**
3026 * Update multiple instances that match the where options.
3027 *
3028 * @param {Object} values hash of values to update
3029 * @param {Object} options update options
3030 * @param {Object} options.where Options to describe the scope of the search.
3031 * @param {boolean} [options.paranoid=true] If true, only non-deleted records will be updated. If false, both deleted and non-deleted records will be updated. Only applies if `options.paranoid` is true for the model.
3032 * @param {Array} [options.fields] Fields to update (defaults to all fields)
3033 * @param {boolean} [options.validate=true] Should each row be subject to validation before it is inserted. The whole insert will fail if one row fails validation
3034 * @param {boolean} [options.hooks=true] Run before / after bulk update hooks?
3035 * @param {boolean} [options.sideEffects=true] Whether or not to update the side effects of any virtual setters.
3036 * @param {boolean} [options.individualHooks=false] Run before / after update hooks?. If true, this will execute a SELECT followed by individual UPDATEs. A select is needed, because the row data needs to be passed to the hooks
3037 * @param {boolean} [options.returning=false] Return the affected rows (only for postgres)
3038 * @param {number} [options.limit] How many rows to update (only for mysql and mariadb, implemented as TOP(n) for MSSQL; for sqlite it is supported only when rowid is present)
3039 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
3040 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
3041 * @param {Transaction} [options.transaction] Transaction to run query under
3042 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
3043 *
3044 * @returns {Promise<Array<number,number>>} The promise returns an array with one or two elements. The first element is always the number
3045 * of affected rows, while the second element is the actual affected rows (only supported in postgres with `options.returning` true.)
3046 *
3047 */
3048 static update(values, options) {
3049 options = Utils.cloneDeep(options);
3050
3051 this._injectScope(options);
3052 this._optionsMustContainWhere(options);
3053
3054 options = this._paranoidClause(this, _.defaults(options, {
3055 validate: true,
3056 hooks: true,
3057 individualHooks: false,
3058 returning: false,
3059 force: false,
3060 sideEffects: true
3061 }));
3062
3063 options.type = QueryTypes.BULKUPDATE;
3064
3065 // Clone values so it doesn't get modified for caller scope and ignore undefined values
3066 values = _.omitBy(values, value => value === undefined);
3067
3068 // Remove values that are not in the options.fields
3069 if (options.fields && options.fields instanceof Array) {
3070 for (const key of Object.keys(values)) {
3071 if (!options.fields.includes(key)) {
3072 delete values[key];
3073 }
3074 }
3075 } else {
3076 const updatedAtAttr = this._timestampAttributes.updatedAt;
3077 options.fields = _.intersection(Object.keys(values), Object.keys(this.tableAttributes));
3078 if (updatedAtAttr && !options.fields.includes(updatedAtAttr)) {
3079 options.fields.push(updatedAtAttr);
3080 }
3081 }
3082
3083 if (this._timestampAttributes.updatedAt && !options.silent) {
3084 values[this._timestampAttributes.updatedAt] = this._getDefaultTimestamp(this._timestampAttributes.updatedAt) || Utils.now(this.sequelize.options.dialect);
3085 }
3086
3087 options.model = this;
3088
3089 let instances;
3090 let valuesUse;
3091
3092 return Promise.try(() => {
3093 // Validate
3094 if (options.validate) {
3095 const build = this.build(values);
3096 build.set(this._timestampAttributes.updatedAt, values[this._timestampAttributes.updatedAt], { raw: true });
3097
3098 if (options.sideEffects) {
3099 values = Object.assign(values, _.pick(build.get(), build.changed()));
3100 options.fields = _.union(options.fields, Object.keys(values));
3101 }
3102
3103 // We want to skip validations for all other fields
3104 options.skip = _.difference(Object.keys(this.rawAttributes), Object.keys(values));
3105 return build.validate(options).then(attributes => {
3106 options.skip = undefined;
3107 if (attributes && attributes.dataValues) {
3108 values = _.pick(attributes.dataValues, Object.keys(values));
3109 }
3110 });
3111 }
3112 return null;
3113 }).then(() => {
3114 // Run before hook
3115 if (options.hooks) {
3116 options.attributes = values;
3117 return this.runHooks('beforeBulkUpdate', options).then(() => {
3118 values = options.attributes;
3119 delete options.attributes;
3120 });
3121 }
3122 return null;
3123 }).then(() => {
3124 valuesUse = values;
3125
3126 // Get instances and run beforeUpdate hook on each record individually
3127 if (options.individualHooks) {
3128 return this.findAll({
3129 where: options.where,
3130 transaction: options.transaction,
3131 logging: options.logging,
3132 benchmark: options.benchmark,
3133 paranoid: options.paranoid
3134 }).then(_instances => {
3135 instances = _instances;
3136 if (!instances.length) {
3137 return [];
3138 }
3139
3140 // Run beforeUpdate hooks on each record and check whether beforeUpdate hook changes values uniformly
3141 // i.e. whether they change values for each record in the same way
3142 let changedValues;
3143 let different = false;
3144
3145 return Promise.map(instances, instance => {
3146 // Record updates in instances dataValues
3147 Object.assign(instance.dataValues, values);
3148 // Set the changed fields on the instance
3149 _.forIn(valuesUse, (newValue, attr) => {
3150 if (newValue !== instance._previousDataValues[attr]) {
3151 instance.setDataValue(attr, newValue);
3152 }
3153 });
3154
3155 // Run beforeUpdate hook
3156 return this.runHooks('beforeUpdate', instance, options).then(() => {
3157 if (!different) {
3158 const thisChangedValues = {};
3159 _.forIn(instance.dataValues, (newValue, attr) => {
3160 if (newValue !== instance._previousDataValues[attr]) {
3161 thisChangedValues[attr] = newValue;
3162 }
3163 });
3164
3165 if (!changedValues) {
3166 changedValues = thisChangedValues;
3167 } else {
3168 different = !_.isEqual(changedValues, thisChangedValues);
3169 }
3170 }
3171
3172 return instance;
3173 });
3174 }).then(_instances => {
3175 instances = _instances;
3176
3177 if (!different) {
3178 const keys = Object.keys(changedValues);
3179 // Hooks do not change values or change them uniformly
3180 if (keys.length) {
3181 // Hooks change values - record changes in valuesUse so they are executed
3182 valuesUse = changedValues;
3183 options.fields = _.union(options.fields, keys);
3184 }
3185 return;
3186 }
3187 // Hooks change values in a different way for each record
3188 // Do not run original query but save each record individually
3189 return Promise.map(instances, instance => {
3190 const individualOptions = _.clone(options);
3191 delete individualOptions.individualHooks;
3192 individualOptions.hooks = false;
3193 individualOptions.validate = false;
3194
3195 return instance.save(individualOptions);
3196 }).tap(_instances => {
3197 instances = _instances;
3198 });
3199 });
3200 });
3201 }
3202 }).then(results => {
3203 // Update already done row-by-row - exit
3204 if (results) {
3205 return [results.length, results];
3206 }
3207
3208 // only updatedAt is being passed, then skip update
3209 if (
3210 _.isEmpty(valuesUse)
3211 || Object.keys(valuesUse).length === 1 && valuesUse[this._timestampAttributes.updatedAt]
3212 ) {
3213 return [0];
3214 }
3215
3216 valuesUse = Utils.mapValueFieldNames(valuesUse, options.fields, this);
3217 options = Utils.mapOptionFieldNames(options, this);
3218 options.hasTrigger = this.options ? this.options.hasTrigger : false;
3219
3220 // Run query to update all rows
3221 return this.QueryInterface.bulkUpdate(this.getTableName(options), valuesUse, options.where, options, this.tableAttributes).then(affectedRows => {
3222 if (options.returning) {
3223 instances = affectedRows;
3224 return [affectedRows.length, affectedRows];
3225 }
3226
3227 return [affectedRows];
3228 });
3229 }).tap(result => {
3230 if (options.individualHooks) {
3231 return Promise.map(instances, instance => {
3232 return this.runHooks('afterUpdate', instance, options);
3233 }).then(() => {
3234 result[1] = instances;
3235 });
3236 }
3237 }).tap(() => {
3238 // Run after hook
3239 if (options.hooks) {
3240 options.attributes = values;
3241 return this.runHooks('afterBulkUpdate', options).then(() => {
3242 delete options.attributes;
3243 });
3244 }
3245 });
3246 }
3247
3248 /**
3249 * Run a describe query on the table.
3250 *
3251 * @param {string} [schema] schema name to search table in
3252 * @param {Object} [options] query options
3253 *
3254 * @returns {Promise} hash of attributes and their types
3255 */
3256 static describe(schema, options) {
3257 return this.QueryInterface.describeTable(this.tableName, Object.assign({ schema: schema || this._schema || undefined }, options));
3258 }
3259
3260 static _getDefaultTimestamp(attr) {
3261 if (!!this.rawAttributes[attr] && !!this.rawAttributes[attr].defaultValue) {
3262 return Utils.toDefaultValue(this.rawAttributes[attr].defaultValue, this.sequelize.options.dialect);
3263 }
3264 return undefined;
3265 }
3266
3267 static _expandAttributes(options) {
3268 if (!_.isPlainObject(options.attributes)) {
3269 return;
3270 }
3271 let attributes = Object.keys(this.rawAttributes);
3272
3273 if (options.attributes.exclude) {
3274 attributes = attributes.filter(elem => !options.attributes.exclude.includes(elem));
3275 }
3276
3277 if (options.attributes.include) {
3278 attributes = attributes.concat(options.attributes.include);
3279 }
3280
3281 options.attributes = attributes;
3282 }
3283
3284 // Inject _scope into options.
3285 static _injectScope(options) {
3286 const scope = Utils.cloneDeep(this._scope);
3287 this._defaultsOptions(options, scope);
3288 }
3289
3290 static [Symbol.for('nodejs.util.inspect.custom')]() {
3291 return this.name;
3292 }
3293
3294 static inspect() {
3295 return this.name;
3296 }
3297
3298 static hasAlias(alias) {
3299 return Object.prototype.hasOwnProperty.call(this.associations, alias);
3300 }
3301
3302 /**
3303 * Increment the value of one or more columns. This is done in the database, which means it does not use the values currently stored on the Instance. The increment is done using a
3304 * ``` SET column = column + X WHERE foo = 'bar' ``` query. To get the correct value after an increment into the Instance you should do a reload.
3305 *
3306 * @example <caption>increment number by 1</caption>
3307 * Model.increment('number', { where: { foo: 'bar' });
3308 *
3309 * @example <caption>increment number and count by 2</caption>
3310 * Model.increment(['number', 'count'], { by: 2, where: { foo: 'bar' } });
3311 *
3312 * @example <caption>increment answer by 42, and decrement tries by 1</caption>
3313 * // `by` is ignored, as each column has its own value
3314 * Model.increment({ answer: 42, tries: -1}, { by: 2, where: { foo: 'bar' } });
3315 *
3316 * @see
3317 * {@link Model#reload}
3318 *
3319 * @param {string|Array|Object} fields If a string is provided, that column is incremented by the value of `by` given in options. If an array is provided, the same is true for each column. If and object is provided, each column is incremented by the value given.
3320 * @param {Object} options increment options
3321 * @param {Object} options.where conditions hash
3322 * @param {number} [options.by=1] The number to increment by
3323 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
3324 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
3325 * @param {Transaction} [options.transaction] Transaction to run query under
3326 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
3327 *
3328 * @returns {Promise<Model[],?number>} returns an array of affected rows and affected count with `options.returning: true`, whenever supported by dialect
3329 */
3330 static increment(fields, options) {
3331 options = options || {};
3332
3333 this._injectScope(options);
3334 this._optionsMustContainWhere(options);
3335
3336 const updatedAtAttr = this._timestampAttributes.updatedAt;
3337 const versionAttr = this._versionAttribute;
3338 const updatedAtAttribute = this.rawAttributes[updatedAtAttr];
3339 options = Utils.defaults({}, options, {
3340 by: 1,
3341 attributes: {},
3342 where: {},
3343 increment: true
3344 });
3345
3346 Utils.mapOptionFieldNames(options, this);
3347
3348 const where = Object.assign({}, options.where);
3349 let values = {};
3350
3351 if (typeof fields === 'string') {
3352 values[fields] = options.by;
3353 } else if (Array.isArray(fields)) {
3354 fields.forEach(field => {
3355 values[field] = options.by;
3356 });
3357 } else { // Assume fields is key-value pairs
3358 values = fields;
3359 }
3360
3361 if (!options.silent && updatedAtAttr && !values[updatedAtAttr]) {
3362 options.attributes[updatedAtAttribute.field || updatedAtAttr] = this._getDefaultTimestamp(updatedAtAttr) || Utils.now(this.sequelize.options.dialect);
3363 }
3364 if (versionAttr) {
3365 values[versionAttr] = options.increment ? 1 : -1;
3366 }
3367
3368 for (const attr of Object.keys(values)) {
3369 // Field name mapping
3370 if (this.rawAttributes[attr] && this.rawAttributes[attr].field && this.rawAttributes[attr].field !== attr) {
3371 values[this.rawAttributes[attr].field] = values[attr];
3372 delete values[attr];
3373 }
3374 }
3375
3376 let promise;
3377 if (!options.increment) {
3378 promise = this.QueryInterface.decrement(this, this.getTableName(options), values, where, options);
3379 } else {
3380 promise = this.QueryInterface.increment(this, this.getTableName(options), values, where, options);
3381 }
3382
3383 return promise.then(affectedRows => {
3384 if (options.returning) {
3385 return [affectedRows, affectedRows.length];
3386 }
3387
3388 return [affectedRows];
3389 });
3390 }
3391
3392 /**
3393 * Decrement the value of one or more columns. This is done in the database, which means it does not use the values currently stored on the Instance. The decrement is done using a
3394 * ```sql SET column = column - X WHERE foo = 'bar'``` query. To get the correct value after a decrement into the Instance you should do a reload.
3395 *
3396 * @example <caption>decrement number by 1</caption>
3397 * Model.decrement('number', { where: { foo: 'bar' });
3398 *
3399 * @example <caption>decrement number and count by 2</caption>
3400 * Model.decrement(['number', 'count'], { by: 2, where: { foo: 'bar' } });
3401 *
3402 * @example <caption>decrement answer by 42, and decrement tries by -1</caption>
3403 * // `by` is ignored, since each column has its own value
3404 * Model.decrement({ answer: 42, tries: -1}, { by: 2, where: { foo: 'bar' } });
3405 *
3406 * @param {string|Array|Object} fields If a string is provided, that column is incremented by the value of `by` given in options. If an array is provided, the same is true for each column. If and object is provided, each column is incremented by the value given.
3407 * @param {Object} options decrement options, similar to increment
3408 *
3409 * @see
3410 * {@link Model.increment}
3411 * @see
3412 * {@link Model#reload}
3413 * @since 4.36.0
3414
3415 * @returns {Promise<Model[],?number>} returns an array of affected rows and affected count with `options.returning: true`, whenever supported by dialect
3416 */
3417 static decrement(fields, options) {
3418 options = _.defaults({ increment: false }, options, {
3419 by: 1
3420 });
3421
3422 return this.increment(fields, options);
3423 }
3424
3425 static _optionsMustContainWhere(options) {
3426 assert(options && options.where, 'Missing where attribute in the options parameter');
3427 assert(_.isPlainObject(options.where) || Array.isArray(options.where) || options.where instanceof Utils.SequelizeMethod,
3428 'Expected plain object, array or sequelize method in the options.where parameter');
3429 }
3430
3431 /**
3432 * Get an object representing the query for this instance, use with `options.where`
3433 *
3434 * @param {boolean} [checkVersion=false] include version attribute in where hash
3435 *
3436 * @returns {Object}
3437 */
3438 where(checkVersion) {
3439 const where = this.constructor.primaryKeyAttributes.reduce((result, attribute) => {
3440 result[attribute] = this.get(attribute, { raw: true });
3441 return result;
3442 }, {});
3443
3444 if (_.size(where) === 0) {
3445 return this._modelOptions.whereCollection;
3446 }
3447 const versionAttr = this.constructor._versionAttribute;
3448 if (checkVersion && versionAttr) {
3449 where[versionAttr] = this.get(versionAttr, { raw: true });
3450 }
3451 return Utils.mapWhereFieldNames(where, this.constructor);
3452 }
3453
3454 toString() {
3455 return `[object SequelizeInstance:${this.constructor.name}]`;
3456 }
3457
3458 /**
3459 * Get the value of the underlying data value
3460 *
3461 * @param {string} key key to look in instance data store
3462 *
3463 * @returns {any}
3464 */
3465 getDataValue(key) {
3466 return this.dataValues[key];
3467 }
3468
3469 /**
3470 * Update the underlying data value
3471 *
3472 * @param {string} key key to set in instance data store
3473 * @param {any} value new value for given key
3474 *
3475 */
3476 setDataValue(key, value) {
3477 const originalValue = this._previousDataValues[key];
3478
3479 if (!Utils.isPrimitive(value) && value !== null || value !== originalValue) {
3480 this.changed(key, true);
3481 }
3482
3483 this.dataValues[key] = value;
3484 }
3485
3486 /**
3487 * If no key is given, returns all values of the instance, also invoking virtual getters.
3488 *
3489 * If key is given and a field or virtual getter is present for the key it will call that getter - else it will return the value for key.
3490 *
3491 * @param {string} [key] key to get value of
3492 * @param {Object} [options] get options
3493 * @param {boolean} [options.plain=false] If set to true, included instances will be returned as plain objects
3494 * @param {boolean} [options.raw=false] If set to true, field and virtual setters will be ignored
3495 *
3496 * @returns {Object|any}
3497 */
3498 get(key, options) {
3499 if (options === undefined && typeof key === 'object') {
3500 options = key;
3501 key = undefined;
3502 }
3503
3504 options = options || {};
3505
3506 if (key) {
3507 if (Object.prototype.hasOwnProperty.call(this._customGetters, key) && !options.raw) {
3508 return this._customGetters[key].call(this, key, options);
3509 }
3510
3511 if (options.plain && this._options.include && this._options.includeNames.includes(key)) {
3512 if (Array.isArray(this.dataValues[key])) {
3513 return this.dataValues[key].map(instance => instance.get(options));
3514 }
3515 if (this.dataValues[key] instanceof Model) {
3516 return this.dataValues[key].get(options);
3517 }
3518 return this.dataValues[key];
3519 }
3520
3521 return this.dataValues[key];
3522 }
3523
3524 if (
3525 this._hasCustomGetters
3526 || options.plain && this._options.include
3527 || options.clone
3528 ) {
3529 const values = {};
3530 let _key;
3531
3532 if (this._hasCustomGetters) {
3533 for (_key in this._customGetters) {
3534 if (
3535 this._options.attributes
3536 && !this._options.attributes.includes(_key)
3537 ) {
3538 continue;
3539 }
3540
3541 if (Object.prototype.hasOwnProperty.call(this._customGetters, _key)) {
3542 values[_key] = this.get(_key, options);
3543 }
3544 }
3545 }
3546
3547 for (_key in this.dataValues) {
3548 if (
3549 !Object.prototype.hasOwnProperty.call(values, _key)
3550 && Object.prototype.hasOwnProperty.call(this.dataValues, _key)
3551 ) {
3552 values[_key] = this.get(_key, options);
3553 }
3554 }
3555
3556 return values;
3557 }
3558
3559 return this.dataValues;
3560 }
3561
3562 /**
3563 * Set is used to update values on the instance (the sequelize representation of the instance that is, remember that nothing will be persisted before you actually call `save`).
3564 * In its most basic form `set` will update a value stored in the underlying `dataValues` object. However, if a custom setter function is defined for the key, that function
3565 * will be called instead. To bypass the setter, you can pass `raw: true` in the options object.
3566 *
3567 * If set is called with an object, it will loop over the object, and call set recursively for each key, value pair. If you set raw to true, the underlying dataValues will either be
3568 * set directly to the object passed, or used to extend dataValues, if dataValues already contain values.
3569 *
3570 * When set is called, the previous value of the field is stored and sets a changed flag(see `changed`).
3571 *
3572 * Set can also be used to build instances for associations, if you have values for those.
3573 * When using set with associations you need to make sure the property key matches the alias of the association
3574 * while also making sure that the proper include options have been set (from .build() or .findOne())
3575 *
3576 * If called with a dot.separated key on a JSON/JSONB attribute it will set the value nested and flag the entire object as changed.
3577 *
3578 * @see
3579 * {@link Model.findAll} for more information about includes
3580 *
3581 * @param {string|Object} key key to set, it can be string or object. When string it will set that key, for object it will loop over all object properties nd set them.
3582 * @param {any} value value to set
3583 * @param {Object} [options] set options
3584 * @param {boolean} [options.raw=false] If set to true, field and virtual setters will be ignored
3585 * @param {boolean} [options.reset=false] Clear all previously set data values
3586 *
3587 * @returns {Model}
3588 */
3589 set(key, value, options) {
3590 let values;
3591 let originalValue;
3592
3593 if (typeof key === 'object' && key !== null) {
3594 values = key;
3595 options = value || {};
3596
3597 if (options.reset) {
3598 this.dataValues = {};
3599 for (const key in values) {
3600 this.changed(key, false);
3601 }
3602 }
3603
3604 // If raw, and we're not dealing with includes or special attributes, just set it straight on the dataValues object
3605 if (options.raw && !(this._options && this._options.include) && !(options && options.attributes) && !this.constructor._hasDateAttributes && !this.constructor._hasBooleanAttributes) {
3606 if (Object.keys(this.dataValues).length) {
3607 this.dataValues = Object.assign(this.dataValues, values);
3608 } else {
3609 this.dataValues = values;
3610 }
3611 // If raw, .changed() shouldn't be true
3612 this._previousDataValues = _.clone(this.dataValues);
3613 } else {
3614 // Loop and call set
3615 if (options.attributes) {
3616 const setKeys = data => {
3617 for (const k of data) {
3618 if (values[k] === undefined) {
3619 continue;
3620 }
3621 this.set(k, values[k], options);
3622 }
3623 };
3624 setKeys(options.attributes);
3625 if (this.constructor._hasVirtualAttributes) {
3626 setKeys(this.constructor._virtualAttributes);
3627 }
3628 if (this._options.includeNames) {
3629 setKeys(this._options.includeNames);
3630 }
3631 } else {
3632 for (const key in values) {
3633 this.set(key, values[key], options);
3634 }
3635 }
3636
3637 if (options.raw) {
3638 // If raw, .changed() shouldn't be true
3639 this._previousDataValues = _.clone(this.dataValues);
3640 }
3641 }
3642 return this;
3643 }
3644 if (!options)
3645 options = {};
3646 if (!options.raw) {
3647 originalValue = this.dataValues[key];
3648 }
3649
3650 // If not raw, and there's a custom setter
3651 if (!options.raw && this._customSetters[key]) {
3652 this._customSetters[key].call(this, value, key);
3653 // custom setter should have changed value, get that changed value
3654 // TODO: v5 make setters return new value instead of changing internal store
3655 const newValue = this.dataValues[key];
3656 if (!Utils.isPrimitive(newValue) && newValue !== null || newValue !== originalValue) {
3657 this._previousDataValues[key] = originalValue;
3658 this.changed(key, true);
3659 }
3660 } else {
3661 // Check if we have included models, and if this key matches the include model names/aliases
3662 if (this._options && this._options.include && this._options.includeNames.includes(key)) {
3663 // Pass it on to the include handler
3664 this._setInclude(key, value, options);
3665 return this;
3666 }
3667 // Bunch of stuff we won't do when it's raw
3668 if (!options.raw) {
3669 // If attribute is not in model definition, return
3670 if (!this._isAttribute(key)) {
3671 if (key.includes('.') && this.constructor._jsonAttributes.has(key.split('.')[0])) {
3672 const previousNestedValue = Dottie.get(this.dataValues, key);
3673 if (!_.isEqual(previousNestedValue, value)) {
3674 Dottie.set(this.dataValues, key, value);
3675 this.changed(key.split('.')[0], true);
3676 }
3677 }
3678 return this;
3679 }
3680
3681 // If attempting to set primary key and primary key is already defined, return
3682 if (this.constructor._hasPrimaryKeys && originalValue && this.constructor._isPrimaryKey(key)) {
3683 return this;
3684 }
3685
3686 // If attempting to set read only attributes, return
3687 if (!this.isNewRecord && this.constructor._hasReadOnlyAttributes && this.constructor._readOnlyAttributes.has(key)) {
3688 return this;
3689 }
3690 }
3691
3692 // If there's a data type sanitizer
3693 if (
3694 !(value instanceof Utils.SequelizeMethod)
3695 && Object.prototype.hasOwnProperty.call(this.constructor._dataTypeSanitizers, key)
3696 ) {
3697 value = this.constructor._dataTypeSanitizers[key].call(this, value, options);
3698 }
3699
3700 // Set when the value has changed and not raw
3701 if (
3702 !options.raw &&
3703 (
3704 // True when sequelize method
3705 value instanceof Utils.SequelizeMethod ||
3706 // Check for data type type comparators
3707 !(value instanceof Utils.SequelizeMethod) && this.constructor._dataTypeChanges[key] && this.constructor._dataTypeChanges[key].call(this, value, originalValue, options) ||
3708 // Check default
3709 !this.constructor._dataTypeChanges[key] && (!Utils.isPrimitive(value) && value !== null || value !== originalValue)
3710 )
3711 ) {
3712 this._previousDataValues[key] = originalValue;
3713 this.changed(key, true);
3714 }
3715
3716 // set data value
3717 this.dataValues[key] = value;
3718 }
3719 return this;
3720 }
3721
3722 setAttributes(updates) {
3723 return this.set(updates);
3724 }
3725
3726 /**
3727 * If changed is called with a string it will return a boolean indicating whether the value of that key in `dataValues` is different from the value in `_previousDataValues`.
3728 *
3729 * If changed is called without an argument, it will return an array of keys that have changed.
3730 *
3731 * If changed is called without an argument and no keys have changed, it will return `false`.
3732 *
3733 * @param {string} [key] key to check or change status of
3734 * @param {any} [value] value to set
3735 *
3736 * @returns {boolean|Array}
3737 */
3738 changed(key, value) {
3739 if (key) {
3740 if (value !== undefined) {
3741 this._changed[key] = value;
3742 return this;
3743 }
3744 return this._changed[key] || false;
3745 }
3746
3747 const changed = Object.keys(this.dataValues).filter(key => this.changed(key));
3748
3749 return changed.length ? changed : false;
3750 }
3751
3752 /**
3753 * Returns the previous value for key from `_previousDataValues`.
3754 *
3755 * If called without a key, returns the previous values for all values which have changed
3756 *
3757 * @param {string} [key] key to get previous value of
3758 *
3759 * @returns {any|Array<any>}
3760 */
3761 previous(key) {
3762 if (key) {
3763 return this._previousDataValues[key];
3764 }
3765
3766 return _.pickBy(this._previousDataValues, (value, key) => this.changed(key));
3767 }
3768
3769 _setInclude(key, value, options) {
3770 if (!Array.isArray(value)) value = [value];
3771 if (value[0] instanceof Model) {
3772 value = value.map(instance => instance.dataValues);
3773 }
3774
3775 const include = this._options.includeMap[key];
3776 const association = include.association;
3777 const accessor = key;
3778 const primaryKeyAttribute = include.model.primaryKeyAttribute;
3779 let childOptions;
3780 let isEmpty;
3781
3782 if (!isEmpty) {
3783 childOptions = {
3784 isNewRecord: this.isNewRecord,
3785 include: include.include,
3786 includeNames: include.includeNames,
3787 includeMap: include.includeMap,
3788 includeValidated: true,
3789 raw: options.raw,
3790 attributes: include.originalAttributes
3791 };
3792 }
3793 if (include.originalAttributes === undefined || include.originalAttributes.length) {
3794 if (association.isSingleAssociation) {
3795 if (Array.isArray(value)) {
3796 value = value[0];
3797 }
3798 isEmpty = value && value[primaryKeyAttribute] === null || value === null;
3799 this[accessor] = this.dataValues[accessor] = isEmpty ? null : include.model.build(value, childOptions);
3800 } else {
3801 isEmpty = value[0] && value[0][primaryKeyAttribute] === null;
3802 this[accessor] = this.dataValues[accessor] = isEmpty ? [] : include.model.bulkBuild(value, childOptions);
3803 }
3804 }
3805 }
3806
3807 /**
3808 * Validate this instance, and if the validation passes, persist it to the database. It will only save changed fields, and do nothing if no fields have changed.
3809 *
3810 * On success, the callback will be called with this instance. On validation error, the callback will be called with an instance of `Sequelize.ValidationError`.
3811 * This error will have a property for each of the fields for which validation failed, with the error message for that field.
3812 *
3813 * @param {Object} [options] save options
3814 * @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
3815 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
3816 * @param {boolean} [options.validate=true] If false, validations won't be run.
3817 * @param {boolean} [options.hooks=true] Run before and after create / update + validate hooks
3818 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
3819 * @param {Transaction} [options.transaction] Transaction to run query under
3820 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
3821 * @param {boolean} [options.returning] Append RETURNING * to get back auto generated values (Postgres only)
3822 *
3823 * @returns {Promise<Model>}
3824 */
3825 save(options) {
3826 if (arguments.length > 1) {
3827 throw new Error('The second argument was removed in favor of the options object.');
3828 }
3829
3830 options = Utils.cloneDeep(options);
3831 options = _.defaults(options, {
3832 hooks: true,
3833 validate: true
3834 });
3835
3836 if (!options.fields) {
3837 if (this.isNewRecord) {
3838 options.fields = Object.keys(this.constructor.rawAttributes);
3839 } else {
3840 options.fields = _.intersection(this.changed(), Object.keys(this.constructor.rawAttributes));
3841 }
3842
3843 options.defaultFields = options.fields;
3844 }
3845
3846 if (options.returning === undefined) {
3847 if (options.association) {
3848 options.returning = false;
3849 } else if (this.isNewRecord) {
3850 options.returning = true;
3851 }
3852 }
3853
3854 const primaryKeyName = this.constructor.primaryKeyAttribute;
3855 const primaryKeyAttribute = primaryKeyName && this.constructor.rawAttributes[primaryKeyName];
3856 const createdAtAttr = this.constructor._timestampAttributes.createdAt;
3857 const versionAttr = this.constructor._versionAttribute;
3858 const hook = this.isNewRecord ? 'Create' : 'Update';
3859 const wasNewRecord = this.isNewRecord;
3860 const now = Utils.now(this.sequelize.options.dialect);
3861 let updatedAtAttr = this.constructor._timestampAttributes.updatedAt;
3862
3863 if (updatedAtAttr && options.fields.length >= 1 && !options.fields.includes(updatedAtAttr)) {
3864 options.fields.push(updatedAtAttr);
3865 }
3866 if (versionAttr && options.fields.length >= 1 && !options.fields.includes(versionAttr)) {
3867 options.fields.push(versionAttr);
3868 }
3869
3870 if (options.silent === true && !(this.isNewRecord && this.get(updatedAtAttr, { raw: true }))) {
3871 // UpdateAtAttr might have been added as a result of Object.keys(Model.rawAttributes). In that case we have to remove it again
3872 _.remove(options.fields, val => val === updatedAtAttr);
3873 updatedAtAttr = false;
3874 }
3875
3876 if (this.isNewRecord === true) {
3877 if (createdAtAttr && !options.fields.includes(createdAtAttr)) {
3878 options.fields.push(createdAtAttr);
3879 }
3880
3881 if (primaryKeyAttribute && primaryKeyAttribute.defaultValue && !options.fields.includes(primaryKeyName)) {
3882 options.fields.unshift(primaryKeyName);
3883 }
3884 }
3885
3886 if (this.isNewRecord === false) {
3887 if (primaryKeyName && this.get(primaryKeyName, { raw: true }) === undefined) {
3888 throw new Error('You attempted to save an instance with no primary key, this is not allowed since it would result in a global update');
3889 }
3890 }
3891
3892 if (updatedAtAttr && !options.silent && options.fields.includes(updatedAtAttr)) {
3893 this.dataValues[updatedAtAttr] = this.constructor._getDefaultTimestamp(updatedAtAttr) || now;
3894 }
3895
3896 if (this.isNewRecord && createdAtAttr && !this.dataValues[createdAtAttr]) {
3897 this.dataValues[createdAtAttr] = this.constructor._getDefaultTimestamp(createdAtAttr) || now;
3898 }
3899
3900 return Promise.try(() => {
3901 // Validate
3902 if (options.validate) {
3903 return this.validate(options);
3904 }
3905 }).then(() => {
3906 // Run before hook
3907 if (options.hooks) {
3908 const beforeHookValues = _.pick(this.dataValues, options.fields);
3909 let ignoreChanged = _.difference(this.changed(), options.fields); // In case of update where it's only supposed to update the passed values and the hook values
3910 let hookChanged;
3911 let afterHookValues;
3912
3913 if (updatedAtAttr && options.fields.includes(updatedAtAttr)) {
3914 ignoreChanged = _.without(ignoreChanged, updatedAtAttr);
3915 }
3916
3917 return this.constructor.runHooks(`before${hook}`, this, options)
3918 .then(() => {
3919 if (options.defaultFields && !this.isNewRecord) {
3920 afterHookValues = _.pick(this.dataValues, _.difference(this.changed(), ignoreChanged));
3921
3922 hookChanged = [];
3923 for (const key of Object.keys(afterHookValues)) {
3924 if (afterHookValues[key] !== beforeHookValues[key]) {
3925 hookChanged.push(key);
3926 }
3927 }
3928
3929 options.fields = _.uniq(options.fields.concat(hookChanged));
3930 }
3931
3932 if (hookChanged) {
3933 if (options.validate) {
3934 // Validate again
3935
3936 options.skip = _.difference(Object.keys(this.constructor.rawAttributes), hookChanged);
3937 return this.validate(options).then(() => {
3938 delete options.skip;
3939 });
3940 }
3941 }
3942 });
3943 }
3944 }).then(() => {
3945 if (!options.fields.length) return this;
3946 if (!this.isNewRecord) return this;
3947 if (!this._options.include || !this._options.include.length) return this;
3948
3949 // Nested creation for BelongsTo relations
3950 return Promise.map(this._options.include.filter(include => include.association instanceof BelongsTo), include => {
3951 const instance = this.get(include.as);
3952 if (!instance) return Promise.resolve();
3953
3954 const includeOptions = _(Utils.cloneDeep(include))
3955 .omit(['association'])
3956 .defaults({
3957 transaction: options.transaction,
3958 logging: options.logging,
3959 parentRecord: this
3960 }).value();
3961
3962 return instance.save(includeOptions).then(() => this[include.association.accessors.set](instance, { save: false, logging: options.logging }));
3963 });
3964 }).then(() => {
3965 const realFields = options.fields.filter(field => !this.constructor._virtualAttributes.has(field));
3966 if (!realFields.length) return this;
3967 if (!this.changed() && !this.isNewRecord) return this;
3968
3969 const versionFieldName = _.get(this.constructor.rawAttributes[versionAttr], 'field') || versionAttr;
3970 let values = Utils.mapValueFieldNames(this.dataValues, options.fields, this.constructor);
3971 let query = null;
3972 let args = [];
3973 let where;
3974
3975 if (this.isNewRecord) {
3976 query = 'insert';
3977 args = [this, this.constructor.getTableName(options), values, options];
3978 } else {
3979 where = this.where(true);
3980 if (versionAttr) {
3981 values[versionFieldName] = parseInt(values[versionFieldName], 10) + 1;
3982 }
3983 query = 'update';
3984 args = [this, this.constructor.getTableName(options), values, where, options];
3985 }
3986
3987 return this.constructor.QueryInterface[query](...args)
3988 .then(([result, rowsUpdated])=> {
3989 if (versionAttr) {
3990 // Check to see that a row was updated, otherwise it's an optimistic locking error.
3991 if (rowsUpdated < 1) {
3992 throw new sequelizeErrors.OptimisticLockError({
3993 modelName: this.constructor.name,
3994 values,
3995 where
3996 });
3997 } else {
3998 result.dataValues[versionAttr] = values[versionFieldName];
3999 }
4000 }
4001
4002 // Transfer database generated values (defaults, autoincrement, etc)
4003 for (const attr of Object.keys(this.constructor.rawAttributes)) {
4004 if (this.constructor.rawAttributes[attr].field &&
4005 values[this.constructor.rawAttributes[attr].field] !== undefined &&
4006 this.constructor.rawAttributes[attr].field !== attr
4007 ) {
4008 values[attr] = values[this.constructor.rawAttributes[attr].field];
4009 delete values[this.constructor.rawAttributes[attr].field];
4010 }
4011 }
4012 values = Object.assign(values, result.dataValues);
4013
4014 result.dataValues = Object.assign(result.dataValues, values);
4015 return result;
4016 })
4017 .tap(() => {
4018 if (!wasNewRecord) return this;
4019 if (!this._options.include || !this._options.include.length) return this;
4020
4021 // Nested creation for HasOne/HasMany/BelongsToMany relations
4022 return Promise.map(this._options.include.filter(include => !(include.association instanceof BelongsTo ||
4023 include.parent && include.parent.association instanceof BelongsToMany)), include => {
4024 let instances = this.get(include.as);
4025
4026 if (!instances) return Promise.resolve();
4027 if (!Array.isArray(instances)) instances = [instances];
4028 if (!instances.length) return Promise.resolve();
4029
4030 const includeOptions = _(Utils.cloneDeep(include))
4031 .omit(['association'])
4032 .defaults({
4033 transaction: options.transaction,
4034 logging: options.logging,
4035 parentRecord: this
4036 }).value();
4037
4038 // Instances will be updated in place so we can safely treat HasOne like a HasMany
4039 return Promise.map(instances, instance => {
4040 if (include.association instanceof BelongsToMany) {
4041 return instance.save(includeOptions).then(() => {
4042 const values = {};
4043 values[include.association.foreignKey] = this.get(this.constructor.primaryKeyAttribute, { raw: true });
4044 values[include.association.otherKey] = instance.get(instance.constructor.primaryKeyAttribute, { raw: true });
4045
4046 // Include values defined in the association
4047 Object.assign(values, include.association.through.scope);
4048 if (instance[include.association.through.model.name]) {
4049 for (const attr of Object.keys(include.association.through.model.rawAttributes)) {
4050 if (include.association.through.model.rawAttributes[attr]._autoGenerated ||
4051 attr === include.association.foreignKey ||
4052 attr === include.association.otherKey ||
4053 typeof instance[include.association.through.model.name][attr] === undefined) {
4054 continue;
4055 }
4056 values[attr] = instance[include.association.through.model.name][attr];
4057 }
4058 }
4059
4060 return include.association.throughModel.create(values, includeOptions);
4061 });
4062 }
4063 instance.set(include.association.foreignKey, this.get(include.association.sourceKey || this.constructor.primaryKeyAttribute, { raw: true }), { raw: true });
4064 Object.assign(instance, include.association.scope);
4065 return instance.save(includeOptions);
4066 });
4067 });
4068 })
4069 .tap(result => {
4070 // Run after hook
4071 if (options.hooks) {
4072 return this.constructor.runHooks(`after${hook}`, result, options);
4073 }
4074 })
4075 .then(result => {
4076 for (const field of options.fields) {
4077 result._previousDataValues[field] = result.dataValues[field];
4078 this.changed(field, false);
4079 }
4080 this.isNewRecord = false;
4081 return result;
4082 });
4083 });
4084 }
4085
4086 /**
4087 * Refresh the current instance in-place, i.e. update the object with current data from the DB and return the same object.
4088 * This is different from doing a `find(Instance.id)`, because that would create and return a new instance. With this method,
4089 * all references to the Instance are updated with the new data and no new objects are created.
4090 *
4091 * @see
4092 * {@link Model.findAll}
4093 *
4094 * @param {Object} [options] Options that are passed on to `Model.find`
4095 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4096 *
4097 * @returns {Promise<Model>}
4098 */
4099 reload(options) {
4100 options = Utils.defaults({}, options, {
4101 where: this.where(),
4102 include: this._options.include || null
4103 });
4104
4105 return this.constructor.findOne(options)
4106 .tap(reload => {
4107 if (!reload) {
4108 throw new sequelizeErrors.InstanceError(
4109 'Instance could not be reloaded because it does not exist anymore (find call returned null)'
4110 );
4111 }
4112 })
4113 .then(reload => {
4114 // update the internal options of the instance
4115 this._options = reload._options;
4116 // re-set instance values
4117 this.set(reload.dataValues, {
4118 raw: true,
4119 reset: true && !options.attributes
4120 });
4121 return this;
4122 });
4123 }
4124
4125 /**
4126 * Validate the attributes of this instance according to validation rules set in the model definition.
4127 *
4128 * The promise fulfills if and only if validation successful; otherwise it rejects an Error instance containing { field name : [error msgs] } entries.
4129 *
4130 * @param {Object} [options] Options that are passed to the validator
4131 * @param {Array} [options.skip] An array of strings. All properties that are in this array will not be validated
4132 * @param {Array} [options.fields] An array of strings. Only the properties that are in this array will be validated
4133 * @param {boolean} [options.hooks=true] Run before and after validate hooks
4134 *
4135 * @returns {Promise}
4136 */
4137 validate(options) {
4138 return new InstanceValidator(this, options).validate();
4139 }
4140
4141 /**
4142 * This is the same as calling `set` and then calling `save` but it only saves the
4143 * exact values passed to it, making it more atomic and safer.
4144 *
4145 * @see
4146 * {@link Model#set}
4147 * @see
4148 * {@link Model#save}
4149 *
4150 * @param {Object} values See `set`
4151 * @param {Object} options See `save`
4152 *
4153 * @returns {Promise<Model>}
4154 */
4155 update(values, options) {
4156 // Clone values so it doesn't get modified for caller scope and ignore undefined values
4157 values = _.omitBy(values, value => value === undefined);
4158
4159 const changedBefore = this.changed() || [];
4160
4161 options = options || {};
4162 if (Array.isArray(options)) options = { fields: options };
4163
4164 options = Utils.cloneDeep(options);
4165 const setOptions = Utils.cloneDeep(options);
4166 setOptions.attributes = options.fields;
4167 this.set(values, setOptions);
4168
4169 // Now we need to figure out which fields were actually affected by the setter.
4170 const sideEffects = _.without(this.changed(), ...changedBefore);
4171 const fields = _.union(Object.keys(values), sideEffects);
4172
4173 if (!options.fields) {
4174 options.fields = _.intersection(fields, this.changed());
4175 options.defaultFields = options.fields;
4176 }
4177
4178 return this.save(options);
4179 }
4180
4181 /**
4182 * Destroy the row corresponding to this instance. Depending on your setting for paranoid, the row will either be completely deleted, or have its deletedAt timestamp set to the current time.
4183 *
4184 * @param {Object} [options={}] destroy options
4185 * @param {boolean} [options.force=false] If set to true, paranoid models will actually be deleted
4186 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4187 * @param {Transaction} [options.transaction] Transaction to run query under
4188 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
4189 *
4190 * @returns {Promise}
4191 */
4192 destroy(options) {
4193 options = Object.assign({
4194 hooks: true,
4195 force: false
4196 }, options);
4197
4198 return Promise.try(() => {
4199 // Run before hook
4200 if (options.hooks) {
4201 return this.constructor.runHooks('beforeDestroy', this, options);
4202 }
4203 }).then(() => {
4204 const where = this.where(true);
4205
4206 if (this.constructor._timestampAttributes.deletedAt && options.force === false) {
4207 const attributeName = this.constructor._timestampAttributes.deletedAt;
4208 const attribute = this.constructor.rawAttributes[attributeName];
4209 const defaultValue = Object.prototype.hasOwnProperty.call(attribute, 'defaultValue')
4210 ? attribute.defaultValue
4211 : null;
4212 const currentValue = this.getDataValue(attributeName);
4213 const undefinedOrNull = currentValue == null && defaultValue == null;
4214 if (undefinedOrNull || _.isEqual(currentValue, defaultValue)) {
4215 // only update timestamp if it wasn't already set
4216 this.setDataValue(attributeName, new Date());
4217 }
4218
4219 return this.save(_.defaults({ hooks: false }, options));
4220 }
4221 return this.constructor.QueryInterface.delete(this, this.constructor.getTableName(options), where, Object.assign({ type: QueryTypes.DELETE, limit: null }, options));
4222 }).tap(() => {
4223 // Run after hook
4224 if (options.hooks) {
4225 return this.constructor.runHooks('afterDestroy', this, options);
4226 }
4227 });
4228 }
4229
4230 /**
4231 * Helper method to determine if a instance is "soft deleted". This is
4232 * particularly useful if the implementer renamed the `deletedAt` attribute
4233 * to something different. This method requires `paranoid` to be enabled.
4234 *
4235 * @returns {boolean}
4236 */
4237 isSoftDeleted() {
4238 if (!this.constructor._timestampAttributes.deletedAt) {
4239 throw new Error('Model is not paranoid');
4240 }
4241
4242 const deletedAtAttribute = this.constructor.rawAttributes[this.constructor._timestampAttributes.deletedAt];
4243 const defaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
4244 const deletedAt = this.get(this.constructor._timestampAttributes.deletedAt) || null;
4245 const isSet = deletedAt !== defaultValue;
4246
4247 return isSet;
4248 }
4249
4250 /**
4251 * Restore the row corresponding to this instance. Only available for paranoid models.
4252 *
4253 * @param {Object} [options={}] restore options
4254 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4255 * @param {Transaction} [options.transaction] Transaction to run query under
4256 *
4257 * @returns {Promise}
4258 */
4259 restore(options) {
4260 if (!this.constructor._timestampAttributes.deletedAt) throw new Error('Model is not paranoid');
4261
4262 options = Object.assign({
4263 hooks: true,
4264 force: false
4265 }, options);
4266
4267 return Promise.try(() => {
4268 // Run before hook
4269 if (options.hooks) {
4270 return this.constructor.runHooks('beforeRestore', this, options);
4271 }
4272 }).then(() => {
4273 const deletedAtCol = this.constructor._timestampAttributes.deletedAt;
4274 const deletedAtAttribute = this.constructor.rawAttributes[deletedAtCol];
4275 const deletedAtDefaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
4276
4277 this.setDataValue(deletedAtCol, deletedAtDefaultValue);
4278 return this.save(Object.assign({}, options, { hooks: false, omitNull: false }));
4279 }).tap(() => {
4280 // Run after hook
4281 if (options.hooks) {
4282 return this.constructor.runHooks('afterRestore', this, options);
4283 }
4284 });
4285 }
4286
4287 /**
4288 * Increment the value of one or more columns. This is done in the database, which means it does not use the values currently stored on the Instance. The increment is done using a
4289 * ```sql
4290 * SET column = column + X
4291 * ```
4292 * query. The updated instance will be returned by default in Postgres. However, in other dialects, you will need to do a reload to get the new values.
4293 *
4294 * @example
4295 * instance.increment('number') // increment number by 1
4296 *
4297 * instance.increment(['number', 'count'], { by: 2 }) // increment number and count by 2
4298 *
4299 * // increment answer by 42, and tries by 1.
4300 * // `by` is ignored, since each column has its own value
4301 * instance.increment({ answer: 42, tries: 1}, { by: 2 })
4302 *
4303 * @see
4304 * {@link Model#reload}
4305 *
4306 * @param {string|Array|Object} fields If a string is provided, that column is incremented by the value of `by` given in options. If an array is provided, the same is true for each column. If and object is provided, each column is incremented by the value given.
4307 * @param {Object} [options] options
4308 * @param {number} [options.by=1] The number to increment by
4309 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
4310 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4311 * @param {Transaction} [options.transaction] Transaction to run query under
4312 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
4313 * @param {boolean} [options.returning=true] Append RETURNING * to get back auto generated values (Postgres only)
4314 *
4315 * @returns {Promise<Model>}
4316 * @since 4.0.0
4317 */
4318 increment(fields, options) {
4319 const identifier = this.where();
4320
4321 options = Utils.cloneDeep(options);
4322 options.where = Object.assign({}, options.where, identifier);
4323 options.instance = this;
4324
4325 return this.constructor.increment(fields, options).return(this);
4326 }
4327
4328 /**
4329 * Decrement the value of one or more columns. This is done in the database, which means it does not use the values currently stored on the Instance. The decrement is done using a
4330 * ```sql
4331 * SET column = column - X
4332 * ```
4333 * query. The updated instance will be returned by default in Postgres. However, in other dialects, you will need to do a reload to get the new values.
4334 *
4335 * @example
4336 * instance.decrement('number') // decrement number by 1
4337 *
4338 * instance.decrement(['number', 'count'], { by: 2 }) // decrement number and count by 2
4339 *
4340 * // decrement answer by 42, and tries by 1.
4341 * // `by` is ignored, since each column has its own value
4342 * instance.decrement({ answer: 42, tries: 1}, { by: 2 })
4343 *
4344 * @see
4345 * {@link Model#reload}
4346 * @param {string|Array|Object} fields If a string is provided, that column is decremented by the value of `by` given in options. If an array is provided, the same is true for each column. If and object is provided, each column is decremented by the value given
4347 * @param {Object} [options] decrement options
4348 * @param {number} [options.by=1] The number to decrement by
4349 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
4350 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4351 * @param {Transaction} [options.transaction] Transaction to run query under
4352 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
4353 * @param {boolean} [options.returning=true] Append RETURNING * to get back auto generated values (Postgres only)
4354 *
4355 * @returns {Promise}
4356 */
4357 decrement(fields, options) {
4358 options = _.defaults({ increment: false }, options, {
4359 by: 1
4360 });
4361
4362 return this.increment(fields, options);
4363 }
4364
4365 /**
4366 * Check whether this and `other` Instance refer to the same row
4367 *
4368 * @param {Model} other Other instance to compare against
4369 *
4370 * @returns {boolean}
4371 */
4372 equals(other) {
4373 if (!other || !other.constructor) {
4374 return false;
4375 }
4376
4377 if (!(other instanceof this.constructor)) {
4378 return false;
4379 }
4380
4381 return this.constructor.primaryKeyAttributes.every(attribute => this.get(attribute, { raw: true }) === other.get(attribute, { raw: true }));
4382 }
4383
4384 /**
4385 * Check if this is equal to one of `others` by calling equals
4386 *
4387 * @param {Array<Model>} others An array of instances to check against
4388 *
4389 * @returns {boolean}
4390 */
4391 equalsOneOf(others) {
4392 return others.some(other => this.equals(other));
4393 }
4394
4395 setValidators(attribute, validators) {
4396 this.validators[attribute] = validators;
4397 }
4398
4399 /**
4400 * Convert the instance to a JSON representation.
4401 * Proxies to calling `get` with no keys.
4402 * This means get all values gotten from the DB, and apply all custom getters.
4403 *
4404 * @see
4405 * {@link Model#get}
4406 *
4407 * @returns {Object}
4408 */
4409 toJSON() {
4410 return _.cloneDeep(
4411 this.get({
4412 plain: true
4413 })
4414 );
4415 }
4416
4417 /**
4418 * Creates a 1:m association between this (the source) and the provided target.
4419 * The foreign key is added on the target.
4420 *
4421 * @param {Model} target Target model
4422 * @param {Object} [options] hasMany association options
4423 * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
4424 * @param {string|Object} [options.as] The alias of this model. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the association, you should provide the same alias when eager loading and when getting associated models. Defaults to the pluralized name of target
4425 * @param {string|Object} [options.foreignKey] The name of the foreign key in the target table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the column. Defaults to the name of source + primary key of source
4426 * @param {string} [options.sourceKey] The name of the field to use as the key for the association in the source table. Defaults to the primary key of the source table
4427 * @param {Object} [options.scope] A key/value set that will be used for association create and find defaults on the target. (sqlite not supported for N:M)
4428 * @param {string} [options.onDelete='SET&nbsp;NULL|CASCADE'] SET NULL if foreignKey allows nulls, CASCADE if otherwise
4429 * @param {string} [options.onUpdate='CASCADE'] Set `ON UPDATE`
4430 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4431 *
4432 * @returns {HasMany}
4433 *
4434 * @example
4435 * User.hasMany(Profile) // This will add userId to the profile table
4436 */
4437 static hasMany(target, options) {} // eslint-disable-line
4438
4439 /**
4440 * Create an N:M association with a join table. Defining `through` is required.
4441 *
4442 * @param {Model} target Target model
4443 * @param {Object} options belongsToMany association options
4444 * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
4445 * @param {Model|string|Object} options.through The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it.
4446 * @param {Model} [options.through.model] The model used to join both sides of the N:M association.
4447 * @param {Object} [options.through.scope] A key/value set that will be used for association create and find defaults on the through model. (Remember to add the attributes to the through model)
4448 * @param {boolean} [options.through.unique=true] If true a unique key will be generated from the foreign keys used (might want to turn this off and create specific unique keys when using scopes)
4449 * @param {string|Object} [options.as] The alias of this association. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the association, you should provide the same alias when eager loading and when getting associated models. Defaults to the pluralized name of target
4450 * @param {string|Object} [options.foreignKey] The name of the foreign key in the join table (representing the source model) or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the column. Defaults to the name of source + primary key of source
4451 * @param {string|Object} [options.otherKey] The name of the foreign key in the join table (representing the target model) or an object representing the type definition for the other column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the column. Defaults to the name of target + primary key of target
4452 * @param {Object} [options.scope] A key/value set that will be used for association create and find defaults on the target. (sqlite not supported for N:M)
4453 * @param {boolean} [options.timestamps=sequelize.options.timestamps] Should the join model have timestamps
4454 * @param {string} [options.onDelete='SET&nbsp;NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m
4455 * @param {string} [options.onUpdate='CASCADE'] Sets `ON UPDATE`
4456 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4457 *
4458 * @returns {BelongsToMany}
4459 *
4460 * @example
4461 * // Automagically generated join model
4462 * User.belongsToMany(Project, { through: 'UserProjects' })
4463 * Project.belongsToMany(User, { through: 'UserProjects' })
4464 *
4465 * // Join model with additional attributes
4466 * const UserProjects = sequelize.define('UserProjects', {
4467 * started: Sequelize.BOOLEAN
4468 * })
4469 * User.belongsToMany(Project, { through: UserProjects })
4470 * Project.belongsToMany(User, { through: UserProjects })
4471 */
4472 static belongsToMany(target, options) {} // eslint-disable-line
4473
4474 /**
4475 * Creates an association between this (the source) and the provided target. The foreign key is added on the target.
4476 *
4477 * @param {Model} target Target model
4478 * @param {Object} [options] hasOne association options
4479 * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
4480 * @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the association, you should provide the same alias when eager loading and when getting associated models. Defaults to the singularized name of target
4481 * @param {string|Object} [options.foreignKey] The name of the foreign key attribute in the target model or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the column. Defaults to the name of source + primary key of source
4482 * @param {string} [options.sourceKey] The name of the attribute to use as the key for the association in the source table. Defaults to the primary key of the source table
4483 * @param {string} [options.onDelete='SET&nbsp;NULL|CASCADE'] SET NULL if foreignKey allows nulls, CASCADE if otherwise
4484 * @param {string} [options.onUpdate='CASCADE'] Sets 'ON UPDATE'
4485 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4486 * @param {string} [options.uniqueKey] The custom name for unique constraint.
4487 *
4488 * @returns {HasOne}
4489 *
4490 * @example
4491 * User.hasOne(Profile) // This will add userId to the profile table
4492 */
4493 static hasOne(target, options) {} // eslint-disable-line
4494
4495 /**
4496 * Creates an association between this (the source) and the provided target. The foreign key is added on the source.
4497 *
4498 * @param {Model} target The target model
4499 * @param {Object} [options] belongsTo association options
4500 * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
4501 * @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the association, you should provide the same alias when eager loading and when getting associated models. Defaults to the singularized name of target
4502 * @param {string|Object} [options.foreignKey] The name of the foreign key attribute in the source table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the column. Defaults to the name of target + primary key of target
4503 * @param {string} [options.targetKey] The name of the attribute to use as the key for the association in the target table. Defaults to the primary key of the target table
4504 * @param {string} [options.onDelete='SET&nbsp;NULL|NO&nbsp;ACTION'] SET NULL if foreignKey allows nulls, NO ACTION if otherwise
4505 * @param {string} [options.onUpdate='CASCADE'] Sets 'ON UPDATE'
4506 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4507 *
4508 * @returns {BelongsTo}
4509 *
4510 * @example
4511 * Profile.belongsTo(User) // This will add userId to the profile table
4512 */
4513 static belongsTo(target, options) {} // eslint-disable-line
4514}
4515
4516Object.assign(Model, associationsMixin);
4517Hooks.applyTo(Model, true);
4518
4519module.exports = Model;