UNPKG

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