UNPKG

192 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 if (this.options.createdAt !== false) {
1026 this._timestampAttributes.createdAt = this.options.createdAt || 'createdAt';
1027 this._readOnlyAttributes.add(this._timestampAttributes.createdAt);
1028 }
1029 if (this.options.updatedAt !== false) {
1030 this._timestampAttributes.updatedAt = this.options.updatedAt || 'updatedAt';
1031 this._readOnlyAttributes.add(this._timestampAttributes.updatedAt);
1032 }
1033 if (this.options.paranoid && this.options.deletedAt !== false) {
1034 this._timestampAttributes.deletedAt = this.options.deletedAt || 'deletedAt';
1035 this._readOnlyAttributes.add(this._timestampAttributes.deletedAt);
1036 }
1037 }
1038
1039 // setup name for version attribute
1040 if (this.options.version) {
1041 this._versionAttribute = typeof this.options.version === 'string' ? this.options.version : 'version';
1042 this._readOnlyAttributes.add(this._versionAttribute);
1043 }
1044
1045 this._hasReadOnlyAttributes = this._readOnlyAttributes.size > 0;
1046
1047 // Add head and tail default attributes (id, timestamps)
1048 this._addDefaultAttributes();
1049 this.refreshAttributes();
1050 this._findAutoIncrementAttribute();
1051
1052 this._scope = this.options.defaultScope;
1053 this._scopeNames = ['defaultScope'];
1054
1055 this.sequelize.modelManager.addModel(this);
1056 this.sequelize.runHooks('afterDefine', this);
1057
1058 return this;
1059 }
1060
1061 static refreshAttributes() {
1062 const attributeManipulation = {};
1063
1064 this.prototype._customGetters = {};
1065 this.prototype._customSetters = {};
1066
1067 ['get', 'set'].forEach(type => {
1068 const opt = `${type}terMethods`;
1069 const funcs = { ...this.options[opt] };
1070 const _custom = type === 'get' ? this.prototype._customGetters : this.prototype._customSetters;
1071
1072 _.each(funcs, (method, attribute) => {
1073 _custom[attribute] = method;
1074
1075 if (type === 'get') {
1076 funcs[attribute] = function() {
1077 return this.get(attribute);
1078 };
1079 }
1080 if (type === 'set') {
1081 funcs[attribute] = function(value) {
1082 return this.set(attribute, value);
1083 };
1084 }
1085 });
1086
1087 _.each(this.rawAttributes, (options, attribute) => {
1088 if (Object.prototype.hasOwnProperty.call(options, type)) {
1089 _custom[attribute] = options[type];
1090 }
1091
1092 if (type === 'get') {
1093 funcs[attribute] = function() {
1094 return this.get(attribute);
1095 };
1096 }
1097 if (type === 'set') {
1098 funcs[attribute] = function(value) {
1099 return this.set(attribute, value);
1100 };
1101 }
1102 });
1103
1104 _.each(funcs, (fct, name) => {
1105 if (!attributeManipulation[name]) {
1106 attributeManipulation[name] = {
1107 configurable: true
1108 };
1109 }
1110 attributeManipulation[name][type] = fct;
1111 });
1112 });
1113
1114 this._dataTypeChanges = {};
1115 this._dataTypeSanitizers = {};
1116
1117 this._hasBooleanAttributes = false;
1118 this._hasDateAttributes = false;
1119 this._jsonAttributes = new Set();
1120 this._virtualAttributes = new Set();
1121 this._defaultValues = {};
1122 this.prototype.validators = {};
1123
1124 this.fieldRawAttributesMap = {};
1125
1126 this.primaryKeys = {};
1127 this.uniqueKeys = {};
1128
1129 _.each(this.rawAttributes, (definition, name) => {
1130 definition.type = this.sequelize.normalizeDataType(definition.type);
1131
1132 definition.Model = this;
1133 definition.fieldName = name;
1134 definition._modelAttribute = true;
1135
1136 if (definition.field === undefined) {
1137 definition.field = Utils.underscoredIf(name, this.underscored);
1138 }
1139
1140 if (definition.primaryKey === true) {
1141 this.primaryKeys[name] = definition;
1142 }
1143
1144 this.fieldRawAttributesMap[definition.field] = definition;
1145
1146 if (definition.type._sanitize) {
1147 this._dataTypeSanitizers[name] = definition.type._sanitize;
1148 }
1149
1150 if (definition.type._isChanged) {
1151 this._dataTypeChanges[name] = definition.type._isChanged;
1152 }
1153
1154 if (definition.type instanceof DataTypes.BOOLEAN) {
1155 this._hasBooleanAttributes = true;
1156 } else if (definition.type instanceof DataTypes.DATE || definition.type instanceof DataTypes.DATEONLY) {
1157 this._hasDateAttributes = true;
1158 } else if (definition.type instanceof DataTypes.JSON) {
1159 this._jsonAttributes.add(name);
1160 } else if (definition.type instanceof DataTypes.VIRTUAL) {
1161 this._virtualAttributes.add(name);
1162 }
1163
1164 if (Object.prototype.hasOwnProperty.call(definition, 'defaultValue')) {
1165 this._defaultValues[name] = () => Utils.toDefaultValue(definition.defaultValue, this.sequelize.options.dialect);
1166 }
1167
1168 if (Object.prototype.hasOwnProperty.call(definition, 'unique') && definition.unique) {
1169 let idxName;
1170 if (
1171 typeof definition.unique === 'object' &&
1172 Object.prototype.hasOwnProperty.call(definition.unique, 'name')
1173 ) {
1174 idxName = definition.unique.name;
1175 } else if (typeof definition.unique === 'string') {
1176 idxName = definition.unique;
1177 } else {
1178 idxName = `${this.tableName}_${name}_unique`;
1179 }
1180
1181 const idx = this.uniqueKeys[idxName] || { fields: [] };
1182
1183 idx.fields.push(definition.field);
1184 idx.msg = idx.msg || definition.unique.msg || null;
1185 idx.name = idxName || false;
1186 idx.column = name;
1187 idx.customIndex = definition.unique !== true;
1188
1189 this.uniqueKeys[idxName] = idx;
1190 }
1191
1192 if (Object.prototype.hasOwnProperty.call(definition, 'validate')) {
1193 this.prototype.validators[name] = definition.validate;
1194 }
1195
1196 if (definition.index === true && definition.type instanceof DataTypes.JSONB) {
1197 this._indexes.push(
1198 Utils.nameIndex(
1199 this._conformIndex({
1200 fields: [definition.field || name],
1201 using: 'gin'
1202 }),
1203 this.getTableName()
1204 )
1205 );
1206
1207 delete definition.index;
1208 }
1209 });
1210
1211 // Create a map of field to attribute names
1212 this.fieldAttributeMap = _.reduce(this.fieldRawAttributesMap, (map, value, key) => {
1213 if (key !== value.fieldName) {
1214 map[key] = value.fieldName;
1215 }
1216 return map;
1217 }, {});
1218
1219 this._hasJsonAttributes = !!this._jsonAttributes.size;
1220
1221 this._hasVirtualAttributes = !!this._virtualAttributes.size;
1222
1223 this._hasDefaultValues = !_.isEmpty(this._defaultValues);
1224
1225 this.tableAttributes = _.omitBy(this.rawAttributes, (_a, key) => this._virtualAttributes.has(key));
1226
1227 this.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length;
1228 this.prototype._hasCustomSetters = Object.keys(this.prototype._customSetters).length;
1229
1230 for (const key of Object.keys(attributeManipulation)) {
1231 if (Object.prototype.hasOwnProperty.call(Model.prototype, key)) {
1232 this.sequelize.log(`Not overriding built-in method from model attribute: ${key}`);
1233 continue;
1234 }
1235 Object.defineProperty(this.prototype, key, attributeManipulation[key]);
1236 }
1237
1238 this.prototype.rawAttributes = this.rawAttributes;
1239 this.prototype._isAttribute = key => Object.prototype.hasOwnProperty.call(this.prototype.rawAttributes, key);
1240
1241 // Primary key convenience constiables
1242 this.primaryKeyAttributes = Object.keys(this.primaryKeys);
1243 this.primaryKeyAttribute = this.primaryKeyAttributes[0];
1244 if (this.primaryKeyAttribute) {
1245 this.primaryKeyField = this.rawAttributes[this.primaryKeyAttribute].field || this.primaryKeyAttribute;
1246 }
1247
1248 this._hasPrimaryKeys = this.primaryKeyAttributes.length > 0;
1249 this._isPrimaryKey = key => this.primaryKeyAttributes.includes(key);
1250 }
1251
1252 /**
1253 * Remove attribute from model definition
1254 *
1255 * @param {string} attribute name of attribute to remove
1256 */
1257 static removeAttribute(attribute) {
1258 delete this.rawAttributes[attribute];
1259 this.refreshAttributes();
1260 }
1261
1262 /**
1263 * Sync this Model to the DB, that is create the table.
1264 *
1265 * @param {object} [options] sync options
1266 *
1267 * @see
1268 * {@link Sequelize#sync} for options
1269 *
1270 * @returns {Promise<Model>}
1271 */
1272 static async sync(options) {
1273 options = { ...this.options, ...options };
1274 options.hooks = options.hooks === undefined ? true : !!options.hooks;
1275
1276 const attributes = this.tableAttributes;
1277 const rawAttributes = this.fieldRawAttributesMap;
1278
1279 if (options.hooks) {
1280 await this.runHooks('beforeSync', options);
1281 }
1282 if (options.force) {
1283 await this.drop(options);
1284 }
1285
1286 const tableName = this.getTableName(options);
1287
1288 await this.queryInterface.createTable(tableName, attributes, options, this);
1289
1290 if (options.alter) {
1291 const tableInfos = await Promise.all([
1292 this.queryInterface.describeTable(tableName, options),
1293 this.queryInterface.getForeignKeyReferencesForTable(tableName, options)
1294 ]);
1295 const columns = tableInfos[0];
1296 // Use for alter foreign keys
1297 const foreignKeyReferences = tableInfos[1];
1298 const removedConstraints = {};
1299
1300 for (const columnName in attributes) {
1301 if (!Object.prototype.hasOwnProperty.call(attributes, columnName)) continue;
1302 if (!columns[columnName] && !columns[attributes[columnName].field]) {
1303 await this.queryInterface.addColumn(tableName, attributes[columnName].field || columnName, attributes[columnName], options);
1304 }
1305 }
1306
1307 if (options.alter === true || typeof options.alter === 'object' && options.alter.drop !== false) {
1308 for (const columnName in columns) {
1309 if (!Object.prototype.hasOwnProperty.call(columns, columnName)) continue;
1310 const currentAttribute = rawAttributes[columnName];
1311 if (!currentAttribute) {
1312 await this.queryInterface.removeColumn(tableName, columnName, options);
1313 continue;
1314 }
1315 if (currentAttribute.primaryKey) continue;
1316 // Check foreign keys. If it's a foreign key, it should remove constraint first.
1317 const references = currentAttribute.references;
1318 if (currentAttribute.references) {
1319 const database = this.sequelize.config.database;
1320 const schema = this.sequelize.config.schema;
1321 // Find existed foreign keys
1322 for (const foreignKeyReference of foreignKeyReferences) {
1323 const constraintName = foreignKeyReference.constraintName;
1324 if (!!constraintName
1325 && foreignKeyReference.tableCatalog === database
1326 && (schema ? foreignKeyReference.tableSchema === schema : true)
1327 && foreignKeyReference.referencedTableName === references.model
1328 && foreignKeyReference.referencedColumnName === references.key
1329 && (schema ? foreignKeyReference.referencedTableSchema === schema : true)
1330 && !removedConstraints[constraintName]) {
1331 // Remove constraint on foreign keys.
1332 await this.queryInterface.removeConstraint(tableName, constraintName, options);
1333 removedConstraints[constraintName] = true;
1334 }
1335 }
1336 }
1337 await this.queryInterface.changeColumn(tableName, columnName, currentAttribute, options);
1338 }
1339 }
1340 }
1341 let indexes = await this.queryInterface.showIndex(tableName, options);
1342 indexes = this._indexes.filter(item1 =>
1343 !indexes.some(item2 => item1.name === item2.name)
1344 ).sort((index1, index2) => {
1345 if (this.sequelize.options.dialect === 'postgres') {
1346 // move concurrent indexes to the bottom to avoid weird deadlocks
1347 if (index1.concurrently === true) return 1;
1348 if (index2.concurrently === true) return -1;
1349 }
1350
1351 return 0;
1352 });
1353
1354 for (const index of indexes) {
1355 await this.queryInterface.addIndex(tableName, { ...options, ...index });
1356 }
1357
1358 if (options.hooks) {
1359 await this.runHooks('afterSync', options);
1360 }
1361
1362 return this;
1363 }
1364
1365 /**
1366 * Drop the table represented by this Model
1367 *
1368 * @param {object} [options] drop options
1369 * @param {boolean} [options.cascade=false] Also drop all objects depending on this table, such as views. Only works in postgres
1370 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1371 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1372 *
1373 * @returns {Promise}
1374 */
1375 static async drop(options) {
1376 return await this.queryInterface.dropTable(this.getTableName(options), options);
1377 }
1378
1379 static async dropSchema(schema) {
1380 return await this.queryInterface.dropSchema(schema);
1381 }
1382
1383 /**
1384 * Apply a schema to this model. For postgres, this will actually place the schema in front of the table name - `"schema"."tableName"`,
1385 * while the schema will be prepended to the table name for mysql and sqlite - `'schema.tablename'`.
1386 *
1387 * This method is intended for use cases where the same model is needed in multiple schemas. In such a use case it is important
1388 * to call `model.schema(schema, [options]).sync()` for each model to ensure the models are created in the correct schema.
1389 *
1390 * If a single default schema per model is needed, set the `options.schema='schema'` parameter during the `define()` call
1391 * for the model.
1392 *
1393 * @param {string} schema The name of the schema
1394 * @param {object} [options] schema options
1395 * @param {string} [options.schemaDelimiter='.'] The character(s) that separates the schema name from the table name
1396 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1397 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1398 *
1399 * @see
1400 * {@link Sequelize#define} for more information about setting a default schema.
1401 *
1402 * @returns {Model}
1403 */
1404 static schema(schema, options) {
1405
1406 const clone = class extends this {};
1407 Object.defineProperty(clone, 'name', { value: this.name });
1408
1409 clone._schema = schema;
1410
1411 if (options) {
1412 if (typeof options === 'string') {
1413 clone._schemaDelimiter = options;
1414 } else if (options.schemaDelimiter) {
1415 clone._schemaDelimiter = options.schemaDelimiter;
1416 }
1417 }
1418
1419 return clone;
1420 }
1421
1422 /**
1423 * 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,
1424 * or an object with `tableName`, `schema` and `delimiter` properties.
1425 *
1426 * @returns {string|object}
1427 */
1428 static getTableName() {
1429 return this.queryGenerator.addSchema(this);
1430 }
1431
1432 /**
1433 * Get un-scoped model
1434 *
1435 * @returns {Model}
1436 */
1437 static unscoped() {
1438 return this.scope();
1439 }
1440
1441 /**
1442 * 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.
1443 *
1444 * 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.
1445 *
1446 * @param {string} name The name of the scope. Use `defaultScope` to override the default scope
1447 * @param {object|Function} scope scope or options
1448 * @param {object} [options] scope options
1449 * @param {boolean} [options.override=false] override old scope if already defined
1450 */
1451 static addScope(name, scope, options) {
1452 options = { override: false, ...options };
1453
1454 if ((name === 'defaultScope' && Object.keys(this.options.defaultScope).length > 0 || name in this.options.scopes) && options.override === false) {
1455 throw new Error(`The scope ${name} already exists. Pass { override: true } as options to silence this error`);
1456 }
1457
1458 if (name === 'defaultScope') {
1459 this.options.defaultScope = this._scope = scope;
1460 } else {
1461 this.options.scopes[name] = scope;
1462 }
1463 }
1464
1465 /**
1466 * Apply a scope created in `define` to the model.
1467 *
1468 * @example <caption>how to create scopes</caption>
1469 * const Model = sequelize.define('model', attributes, {
1470 * defaultScope: {
1471 * where: {
1472 * username: 'dan'
1473 * },
1474 * limit: 12
1475 * },
1476 * scopes: {
1477 * isALie: {
1478 * where: {
1479 * stuff: 'cake'
1480 * }
1481 * },
1482 * complexFunction: function(email, accessLevel) {
1483 * return {
1484 * where: {
1485 * email: {
1486 * [Op.like]: email
1487 * },
1488 * access_level {
1489 * [Op.gte]: accessLevel
1490 * }
1491 * }
1492 * }
1493 * }
1494 * }
1495 * })
1496 *
1497 * # 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:
1498 *
1499 * Model.findAll() // WHERE username = 'dan'
1500 * Model.findAll({ where: { age: { [Op.gt]: 12 } } }) // WHERE age > 12 AND username = 'dan'
1501 *
1502 * @example <caption>To invoke scope functions you can do</caption>
1503 * Model.scope({ method: ['complexFunction', 'dan@sequelize.com', 42]}).findAll()
1504 * // WHERE email like 'dan@sequelize.com%' AND access_level >= 42
1505 *
1506 * @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.
1507 *
1508 * @returns {Model} A reference to the model, with the scope(s) applied. Calling scope again on the returned model will clear the previous scope.
1509 */
1510 static scope(option) {
1511 const self = class extends this {};
1512 let scope;
1513 let scopeName;
1514
1515 Object.defineProperty(self, 'name', { value: this.name });
1516
1517 self._scope = {};
1518 self._scopeNames = [];
1519 self.scoped = true;
1520
1521 if (!option) {
1522 return self;
1523 }
1524
1525 const options = _.flatten(arguments);
1526
1527 for (const option of options) {
1528 scope = null;
1529 scopeName = null;
1530
1531 if (_.isPlainObject(option)) {
1532 if (option.method) {
1533 if (Array.isArray(option.method) && !!self.options.scopes[option.method[0]]) {
1534 scopeName = option.method[0];
1535 scope = self.options.scopes[scopeName].apply(self, option.method.slice(1));
1536 }
1537 else if (self.options.scopes[option.method]) {
1538 scopeName = option.method;
1539 scope = self.options.scopes[scopeName].apply(self);
1540 }
1541 } else {
1542 scope = option;
1543 }
1544 } else if (option === 'defaultScope' && _.isPlainObject(self.options.defaultScope)) {
1545 scope = self.options.defaultScope;
1546 } else {
1547 scopeName = option;
1548 scope = self.options.scopes[scopeName];
1549 if (typeof scope === 'function') {
1550 scope = scope();
1551 }
1552 }
1553
1554 if (scope) {
1555 this._conformIncludes(scope, this);
1556 // clone scope so it doesn't get modified
1557 this._assignOptions(self._scope, Utils.cloneDeep(scope));
1558 self._scopeNames.push(scopeName ? scopeName : 'defaultScope');
1559 } else {
1560 throw new sequelizeErrors.SequelizeScopeError(`Invalid scope ${scopeName} called.`);
1561 }
1562 }
1563
1564 return self;
1565 }
1566
1567 /**
1568 * Search for multiple instances.
1569 *
1570 * @example <caption>Simple search using AND and =</caption>
1571 * Model.findAll({
1572 * where: {
1573 * attr1: 42,
1574 * attr2: 'cake'
1575 * }
1576 * })
1577 *
1578 * # WHERE attr1 = 42 AND attr2 = 'cake'
1579 *
1580 * @example <caption>Using greater than, less than etc.</caption>
1581 * const {gt, lte, ne, in: opIn} = Sequelize.Op;
1582 *
1583 * Model.findAll({
1584 * where: {
1585 * attr1: {
1586 * [gt]: 50
1587 * },
1588 * attr2: {
1589 * [lte]: 45
1590 * },
1591 * attr3: {
1592 * [opIn]: [1,2,3]
1593 * },
1594 * attr4: {
1595 * [ne]: 5
1596 * }
1597 * }
1598 * })
1599 *
1600 * # WHERE attr1 > 50 AND attr2 <= 45 AND attr3 IN (1,2,3) AND attr4 != 5
1601 *
1602 * @example <caption>Queries using OR</caption>
1603 * const {or, and, gt, lt} = Sequelize.Op;
1604 *
1605 * Model.findAll({
1606 * where: {
1607 * name: 'a project',
1608 * [or]: [
1609 * {id: [1, 2, 3]},
1610 * {
1611 * [and]: [
1612 * {id: {[gt]: 10}},
1613 * {id: {[lt]: 100}}
1614 * ]
1615 * }
1616 * ]
1617 * }
1618 * });
1619 *
1620 * # WHERE `Model`.`name` = 'a project' AND (`Model`.`id` IN (1, 2, 3) OR (`Model`.`id` > 10 AND `Model`.`id` < 100));
1621 *
1622 * @see
1623 * {@link Operators} for possible operators
1624 * __Alias__: _all_
1625 *
1626 * The promise is resolved with an array of Model instances if the query succeeds._
1627 *
1628 * @param {object} [options] A hash of options to describe the scope of the search
1629 * @param {object} [options.where] A hash of attributes to describe your search. See above for examples.
1630 * @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
1631 * @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']] }`
1632 * @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'] } }`
1633 * @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.
1634 * @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).
1635 * @param {Model} [options.include[].model] The model you want to eagerly load
1636 * @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
1637 * @param {Association} [options.include[].association] The association you want to eagerly load. (This can be used instead of providing a model/as pair)
1638 * @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`
1639 * @param {boolean} [options.include[].or=false] Whether to bind the ON and WHERE clause together by OR instead of AND.
1640 * @param {object} [options.include[].on] Supply your own ON condition for the join.
1641 * @param {Array<string>} [options.include[].attributes] A list of attributes to select from the child model
1642 * @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.
1643 * @param {boolean} [options.include[].right] If true, converts to a right join if dialect support it. Ignored if `include.required` is true.
1644 * @param {boolean} [options.include[].separate] If true, runs a separate query to fetch the associated instances, only supported for hasMany associations
1645 * @param {number} [options.include[].limit] Limit the joined rows, only supported with include.separate=true
1646 * @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.
1647 * @param {object} [options.include[].through.where] Filter on the join model for belongsToMany relations
1648 * @param {Array} [options.include[].through.attributes] A list of attributes to select from the join model for belongsToMany relations
1649 * @param {Array<object|Model|string>} [options.include[].include] Load further nested related models
1650 * @param {boolean} [options.include[].duplicating] Mark the include as duplicating, will prevent a subquery from being used.
1651 * @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.
1652 * @param {number} [options.limit] Limit for result
1653 * @param {number} [options.offset] Offset for result
1654 * @param {Transaction} [options.transaction] Transaction to run query under
1655 * @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.
1656 * @param {boolean} [options.skipLocked] Skip locked rows. Only supported in Postgres.
1657 * @param {boolean} [options.raw] Return raw result. See sequelize.query for more information.
1658 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1659 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1660 * @param {object} [options.having] Having options
1661 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1662 * @param {boolean|Error} [options.rejectOnEmpty=false] Throws an error when no records found
1663 *
1664 * @see
1665 * {@link Sequelize#query}
1666 *
1667 * @returns {Promise<Array<Model>>}
1668 */
1669 static async findAll(options) {
1670 if (options !== undefined && !_.isPlainObject(options)) {
1671 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');
1672 }
1673
1674 if (options !== undefined && options.attributes) {
1675 if (!Array.isArray(options.attributes) && !_.isPlainObject(options.attributes)) {
1676 throw new sequelizeErrors.QueryError('The attributes option must be an array of column names or an object');
1677 }
1678 }
1679
1680 this.warnOnInvalidOptions(options, Object.keys(this.rawAttributes));
1681
1682 const tableNames = {};
1683
1684 tableNames[this.getTableName(options)] = true;
1685 options = Utils.cloneDeep(options);
1686
1687 _.defaults(options, { hooks: true });
1688
1689 // set rejectOnEmpty option, defaults to model options
1690 options.rejectOnEmpty = Object.prototype.hasOwnProperty.call(options, 'rejectOnEmpty')
1691 ? options.rejectOnEmpty
1692 : this.options.rejectOnEmpty;
1693
1694 this._injectScope(options);
1695
1696 if (options.hooks) {
1697 await this.runHooks('beforeFind', options);
1698 }
1699 this._conformIncludes(options, this);
1700 this._expandAttributes(options);
1701 this._expandIncludeAll(options);
1702
1703 if (options.hooks) {
1704 await this.runHooks('beforeFindAfterExpandIncludeAll', options);
1705 }
1706 options.originalAttributes = this._injectDependentVirtualAttributes(options.attributes);
1707
1708 if (options.include) {
1709 options.hasJoin = true;
1710
1711 this._validateIncludedElements(options, tableNames);
1712
1713 // If we're not raw, we have to make sure we include the primary key for de-duplication
1714 if (
1715 options.attributes
1716 && !options.raw
1717 && this.primaryKeyAttribute
1718 && !options.attributes.includes(this.primaryKeyAttribute)
1719 && (!options.group || !options.hasSingleAssociation || options.hasMultiAssociation)
1720 ) {
1721 options.attributes = [this.primaryKeyAttribute].concat(options.attributes);
1722 }
1723 }
1724
1725 if (!options.attributes) {
1726 options.attributes = Object.keys(this.rawAttributes);
1727 options.originalAttributes = this._injectDependentVirtualAttributes(options.attributes);
1728 }
1729
1730 // whereCollection is used for non-primary key updates
1731 this.options.whereCollection = options.where || null;
1732
1733 Utils.mapFinderOptions(options, this);
1734
1735 options = this._paranoidClause(this, options);
1736
1737 if (options.hooks) {
1738 await this.runHooks('beforeFindAfterOptions', options);
1739 }
1740 const selectOptions = { ...options, tableNames: Object.keys(tableNames) };
1741 const results = await this.queryInterface.select(this, this.getTableName(selectOptions), selectOptions);
1742 if (options.hooks) {
1743 await this.runHooks('afterFind', results, options);
1744 }
1745
1746 //rejectOnEmpty mode
1747 if (_.isEmpty(results) && options.rejectOnEmpty) {
1748 if (typeof options.rejectOnEmpty === 'function') {
1749 throw new options.rejectOnEmpty();
1750 }
1751 if (typeof options.rejectOnEmpty === 'object') {
1752 throw options.rejectOnEmpty;
1753 }
1754 throw new sequelizeErrors.EmptyResultError();
1755 }
1756
1757 return await Model._findSeparate(results, options);
1758 }
1759
1760 static warnOnInvalidOptions(options, validColumnNames) {
1761 if (!_.isPlainObject(options)) {
1762 return;
1763 }
1764
1765 const unrecognizedOptions = Object.keys(options).filter(k => !validQueryKeywords.has(k));
1766 const unexpectedModelAttributes = _.intersection(unrecognizedOptions, validColumnNames);
1767 if (!options.where && unexpectedModelAttributes.length > 0) {
1768 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?`);
1769 }
1770 }
1771
1772 static _injectDependentVirtualAttributes(attributes) {
1773 if (!this._hasVirtualAttributes) return attributes;
1774 if (!attributes || !Array.isArray(attributes)) return attributes;
1775
1776 for (const attribute of attributes) {
1777 if (
1778 this._virtualAttributes.has(attribute)
1779 && this.rawAttributes[attribute].type.fields
1780 ) {
1781 attributes = attributes.concat(this.rawAttributes[attribute].type.fields);
1782 }
1783 }
1784
1785 attributes = _.uniq(attributes);
1786
1787 return attributes;
1788 }
1789
1790 static async _findSeparate(results, options) {
1791 if (!options.include || options.raw || !results) return results;
1792
1793 const original = results;
1794 if (options.plain) results = [results];
1795
1796 if (!results.length) return original;
1797
1798 await Promise.all(options.include.map(async include => {
1799 if (!include.separate) {
1800 return await Model._findSeparate(
1801 results.reduce((memo, result) => {
1802 let associations = result.get(include.association.as);
1803
1804 // Might be an empty belongsTo relation
1805 if (!associations) return memo;
1806
1807 // Force array so we can concat no matter if it's 1:1 or :M
1808 if (!Array.isArray(associations)) associations = [associations];
1809
1810 for (let i = 0, len = associations.length; i !== len; ++i) {
1811 memo.push(associations[i]);
1812 }
1813 return memo;
1814 }, []),
1815 {
1816
1817 ..._.omit(options, 'include', 'attributes', 'order', 'where', 'limit', 'offset', 'plain', 'scope'),
1818 include: include.include || []
1819 }
1820 );
1821 }
1822
1823 const map = await include.association.get(results, {
1824
1825 ..._.omit(options, nonCascadingOptions),
1826 ..._.omit(include, ['parent', 'association', 'as', 'originalAttributes'])
1827 });
1828
1829 for (const result of results) {
1830 result.set(
1831 include.association.as,
1832 map[result.get(include.association.sourceKey)],
1833 { raw: true }
1834 );
1835 }
1836 }));
1837
1838 return original;
1839 }
1840
1841 /**
1842 * Search for a single instance by its primary key._
1843 *
1844 * @param {number|string|Buffer} param The value of the desired instance's primary key.
1845 * @param {object} [options] find options
1846 * @param {Transaction} [options.transaction] Transaction to run query under
1847 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1848 *
1849 * @see
1850 * {@link Model.findAll} for a full explanation of options, Note that options.where is not supported.
1851 *
1852 * @returns {Promise<Model>}
1853 */
1854 static async findByPk(param, options) {
1855 // return Promise resolved with null if no arguments are passed
1856 if ([null, undefined].includes(param)) {
1857 return null;
1858 }
1859
1860 options = Utils.cloneDeep(options) || {};
1861
1862 if (typeof param === 'number' || typeof param === 'string' || Buffer.isBuffer(param)) {
1863 options.where = {
1864 [this.primaryKeyAttribute]: param
1865 };
1866 } else {
1867 throw new Error(`Argument passed to findByPk is invalid: ${param}`);
1868 }
1869
1870 // Bypass a possible overloaded findOne
1871 return await this.findOne(options);
1872 }
1873
1874 /**
1875 * Search for a single instance. Returns the first instance found, or null if none can be found.
1876 *
1877 * @param {object} [options] A hash of options to describe the scope of the search
1878 * @param {Transaction} [options.transaction] Transaction to run query under
1879 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1880 *
1881 * @see
1882 * {@link Model.findAll} for an explanation of options
1883 *
1884 * @returns {Promise<Model|null>}
1885 */
1886 static async findOne(options) {
1887 if (options !== undefined && !_.isPlainObject(options)) {
1888 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');
1889 }
1890 options = Utils.cloneDeep(options);
1891
1892 if (options.limit === undefined) {
1893 const uniqueSingleColumns = _.chain(this.uniqueKeys).values().filter(c => c.fields.length === 1).map('column').value();
1894
1895 // Don't add limit if querying directly on the pk or a unique column
1896 if (!options.where || !_.some(options.where, (value, key) =>
1897 (key === this.primaryKeyAttribute || uniqueSingleColumns.includes(key)) &&
1898 (Utils.isPrimitive(value) || Buffer.isBuffer(value))
1899 )) {
1900 options.limit = 1;
1901 }
1902 }
1903
1904 // Bypass a possible overloaded findAll.
1905 return await this.findAll(_.defaults(options, {
1906 plain: true
1907 }));
1908 }
1909
1910 /**
1911 * Run an aggregation method on the specified field
1912 *
1913 * @param {string} attribute The attribute to aggregate over. Can be a field name or *
1914 * @param {string} aggregateFunction The function to use for aggregation, e.g. sum, max etc.
1915 * @param {object} [options] Query options. See sequelize.query for full options
1916 * @param {object} [options.where] A hash of search attributes.
1917 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1918 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1919 * @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.
1920 * @param {boolean} [options.distinct] Applies DISTINCT to the field being aggregated over
1921 * @param {Transaction} [options.transaction] Transaction to run query under
1922 * @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`
1923 *
1924 * @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.
1925 */
1926 static async aggregate(attribute, aggregateFunction, options) {
1927 options = Utils.cloneDeep(options);
1928
1929 // We need to preserve attributes here as the `injectScope` call would inject non aggregate columns.
1930 const prevAttributes = options.attributes;
1931 this._injectScope(options);
1932 options.attributes = prevAttributes;
1933 this._conformIncludes(options, this);
1934
1935 if (options.include) {
1936 this._expandIncludeAll(options);
1937 this._validateIncludedElements(options);
1938 }
1939
1940 const attrOptions = this.rawAttributes[attribute];
1941 const field = attrOptions && attrOptions.field || attribute;
1942 let aggregateColumn = this.sequelize.col(field);
1943
1944 if (options.distinct) {
1945 aggregateColumn = this.sequelize.fn('DISTINCT', aggregateColumn);
1946 }
1947
1948 let { group } = options;
1949 if (Array.isArray(group) && Array.isArray(group[0])) {
1950 noDoubleNestedGroup();
1951 group = _.flatten(group);
1952 }
1953 options.attributes = _.unionBy(
1954 options.attributes,
1955 group,
1956 [[this.sequelize.fn(aggregateFunction, aggregateColumn), aggregateFunction]],
1957 a => Array.isArray(a) ? a[1] : a
1958 );
1959
1960 if (!options.dataType) {
1961 if (attrOptions) {
1962 options.dataType = attrOptions.type;
1963 } else {
1964 // Use FLOAT as fallback
1965 options.dataType = new DataTypes.FLOAT();
1966 }
1967 } else {
1968 options.dataType = this.sequelize.normalizeDataType(options.dataType);
1969 }
1970
1971 Utils.mapOptionFieldNames(options, this);
1972 options = this._paranoidClause(this, options);
1973
1974 const value = await this.queryInterface.rawSelect(this.getTableName(options), options, aggregateFunction, this);
1975 if (value === null) {
1976 return 0;
1977 }
1978 return value;
1979 }
1980
1981 /**
1982 * Count the number of records matching the provided where clause.
1983 *
1984 * If you provide an `include` option, the number of matching associations will be counted instead.
1985 *
1986 * @param {object} [options] options
1987 * @param {object} [options.where] A hash of search attributes.
1988 * @param {object} [options.include] Include options. See `find` for details
1989 * @param {boolean} [options.paranoid=true] Set `true` to count only non-deleted records. Can be used on models with `paranoid` enabled
1990 * @param {boolean} [options.distinct] Apply COUNT(DISTINCT(col)) on primary key or on options.col.
1991 * @param {string} [options.col] Column on which COUNT() should be applied
1992 * @param {Array} [options.attributes] Used in conjunction with `group`
1993 * @param {Array} [options.group] For creating complex counts. Will return multiple rows as needed.
1994 * @param {Transaction} [options.transaction] Transaction to run query under
1995 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
1996 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
1997 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
1998 *
1999 * @returns {Promise<number>}
2000 */
2001 static async count(options) {
2002 options = Utils.cloneDeep(options);
2003 options = _.defaults(options, { hooks: true });
2004 options.raw = true;
2005 if (options.hooks) {
2006 await this.runHooks('beforeCount', options);
2007 }
2008 let col = options.col || '*';
2009 if (options.include) {
2010 col = `${this.name}.${options.col || this.primaryKeyField}`;
2011 }
2012 if (options.distinct && col === '*') {
2013 col = this.primaryKeyField;
2014 }
2015 options.plain = !options.group;
2016 options.dataType = new DataTypes.INTEGER();
2017 options.includeIgnoreAttributes = false;
2018
2019 // No limit, offset or order for the options max be given to count()
2020 // Set them to null to prevent scopes setting those values
2021 options.limit = null;
2022 options.offset = null;
2023 options.order = null;
2024
2025 return await this.aggregate(col, 'count', options);
2026 }
2027
2028 /**
2029 * 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
2030 *
2031 * @example
2032 * const result = await Model.findAndCountAll({
2033 * where: ...,
2034 * limit: 12,
2035 * offset: 12
2036 * });
2037 *
2038 * # 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.
2039 *
2040 * # 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.
2041 *
2042 * # Suppose you want to find all users who have a profile attached:
2043 *
2044 * User.findAndCountAll({
2045 * include: [
2046 * { model: Profile, required: true}
2047 * ],
2048 * limit: 3
2049 * });
2050 *
2051 * # 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
2052 *
2053 * @param {object} [options] See findAll options
2054 *
2055 * @see
2056 * {@link Model.findAll} for a specification of find and query options
2057 * @see
2058 * {@link Model.count} for a specification of count options
2059 *
2060 * @returns {Promise<{count: number, rows: Model[]}>}
2061 */
2062 static async findAndCountAll(options) {
2063 if (options !== undefined && !_.isPlainObject(options)) {
2064 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');
2065 }
2066
2067 const countOptions = Utils.cloneDeep(options);
2068
2069 if (countOptions.attributes) {
2070 countOptions.attributes = undefined;
2071 }
2072
2073 const [count, rows] = await Promise.all([
2074 this.count(countOptions),
2075 this.findAll(options)
2076 ]);
2077
2078 return {
2079 count,
2080 rows: count === 0 ? [] : rows
2081 };
2082 }
2083
2084 /**
2085 * Find the maximum value of field
2086 *
2087 * @param {string} field attribute / field name
2088 * @param {object} [options] See aggregate
2089 *
2090 * @see
2091 * {@link Model.aggregate} for options
2092 *
2093 * @returns {Promise<*>}
2094 */
2095 static async max(field, options) {
2096 return await this.aggregate(field, 'max', options);
2097 }
2098
2099 /**
2100 * Find the minimum value of field
2101 *
2102 * @param {string} field attribute / field name
2103 * @param {object} [options] See aggregate
2104 *
2105 * @see
2106 * {@link Model.aggregate} for options
2107 *
2108 * @returns {Promise<*>}
2109 */
2110 static async min(field, options) {
2111 return await this.aggregate(field, 'min', options);
2112 }
2113
2114 /**
2115 * Find the sum of field
2116 *
2117 * @param {string} field attribute / field name
2118 * @param {object} [options] See aggregate
2119 *
2120 * @see
2121 * {@link Model.aggregate} for options
2122 *
2123 * @returns {Promise<number>}
2124 */
2125 static async sum(field, options) {
2126 return await this.aggregate(field, 'sum', options);
2127 }
2128
2129 /**
2130 * Builds a new model instance.
2131 *
2132 * @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.
2133 * @param {object} [options] Instance build options
2134 * @param {boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
2135 * @param {boolean} [options.isNewRecord=true] Is this new record
2136 * @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances. See `set`
2137 *
2138 * @returns {Model|Array<Model>}
2139 */
2140 static build(values, options) {
2141 if (Array.isArray(values)) {
2142 return this.bulkBuild(values, options);
2143 }
2144
2145 return new this(values, options);
2146 }
2147
2148 static bulkBuild(valueSets, options) {
2149 options = { isNewRecord: true, ...options };
2150
2151 if (!options.includeValidated) {
2152 this._conformIncludes(options, this);
2153 if (options.include) {
2154 this._expandIncludeAll(options);
2155 this._validateIncludedElements(options);
2156 }
2157 }
2158
2159 if (options.attributes) {
2160 options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
2161 }
2162
2163 return valueSets.map(values => this.build(values, options));
2164 }
2165
2166 /**
2167 * Builds a new model instance and calls save on it.
2168 *
2169 * @see
2170 * {@link Model.build}
2171 * @see
2172 * {@link Model.save}
2173 *
2174 * @param {object} values Hash of data values to create new record with
2175 * @param {object} [options] Build and query options
2176 * @param {boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
2177 * @param {boolean} [options.isNewRecord=true] Is this new record
2178 * @param {Array} [options.include] An array of include options - Used to build prefetched/included model instances. See `set`
2179 * @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
2180 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
2181 * @param {boolean} [options.validate=true] If false, validations won't be run.
2182 * @param {boolean} [options.hooks=true] Run before and after create / update + validate hooks
2183 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2184 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2185 * @param {Transaction} [options.transaction] Transaction to run query under
2186 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2187 * @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)
2188 *
2189 * @returns {Promise<Model>}
2190 *
2191 */
2192 static async create(values, options) {
2193 options = Utils.cloneDeep(options || {});
2194
2195 return await this.build(values, {
2196 isNewRecord: true,
2197 attributes: options.fields,
2198 include: options.include,
2199 raw: options.raw,
2200 silent: options.silent
2201 }).save(options);
2202 }
2203
2204 /**
2205 * Find a row that matches the query, or build (but don't save) the row if none is found.
2206 * The successful result of the promise will be (instance, built)
2207 *
2208 * @param {object} options find options
2209 * @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.
2210 * @param {object} [options.defaults] Default values to use if building a new instance
2211 * @param {object} [options.transaction] Transaction to run query under
2212 *
2213 * @returns {Promise<Model,boolean>}
2214 */
2215 static async findOrBuild(options) {
2216 if (!options || !options.where || arguments.length > 1) {
2217 throw new Error(
2218 'Missing where attribute in the options parameter passed to findOrBuild. ' +
2219 'Please note that the API has changed, and is now options only (an object with where, defaults keys, transaction etc.)'
2220 );
2221 }
2222
2223 let values;
2224
2225 let instance = await this.findOne(options);
2226 if (instance === null) {
2227 values = { ...options.defaults };
2228 if (_.isPlainObject(options.where)) {
2229 values = Utils.defaults(values, options.where);
2230 }
2231
2232 instance = this.build(values, options);
2233
2234 return [instance, true];
2235 }
2236
2237 return [instance, false];
2238 }
2239
2240 /**
2241 * Find a row that matches the query, or build and save the row if none is found
2242 * The successful result of the promise will be (instance, created)
2243 *
2244 * 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.
2245 * 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.
2246 * If a transaction is created, a savepoint will be created instead, and any unique constraint violation will be handled internally.
2247 *
2248 * @see
2249 * {@link Model.findAll} for a full specification of find and options
2250 *
2251 * @param {object} options find and create options
2252 * @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.
2253 * @param {object} [options.defaults] Default values to use if creating a new instance
2254 * @param {Transaction} [options.transaction] Transaction to run query under
2255 *
2256 * @returns {Promise<Model,boolean>}
2257 */
2258 static async findOrCreate(options) {
2259 if (!options || !options.where || arguments.length > 1) {
2260 throw new Error(
2261 'Missing where attribute in the options parameter passed to findOrCreate. ' +
2262 'Please note that the API has changed, and is now options only (an object with where, defaults keys, transaction etc.)'
2263 );
2264 }
2265
2266 options = { ...options };
2267
2268 if (options.defaults) {
2269 const defaults = Object.keys(options.defaults);
2270 const unknownDefaults = defaults.filter(name => !this.rawAttributes[name]);
2271
2272 if (unknownDefaults.length) {
2273 logger.warn(`Unknown attributes (${unknownDefaults}) passed to defaults option of findOrCreate`);
2274 }
2275 }
2276
2277 if (options.transaction === undefined && this.sequelize.constructor._cls) {
2278 const t = this.sequelize.constructor._cls.get('transaction');
2279 if (t) {
2280 options.transaction = t;
2281 }
2282 }
2283
2284 const internalTransaction = !options.transaction;
2285 let values;
2286 let transaction;
2287
2288 try {
2289 const t = await this.sequelize.transaction(options);
2290 transaction = t;
2291 options.transaction = t;
2292
2293 const found = await this.findOne(Utils.defaults({ transaction }, options));
2294 if (found !== null) {
2295 return [found, false];
2296 }
2297
2298 values = { ...options.defaults };
2299 if (_.isPlainObject(options.where)) {
2300 values = Utils.defaults(values, options.where);
2301 }
2302
2303 options.exception = true;
2304 options.returning = true;
2305
2306 try {
2307 const created = await this.create(values, options);
2308 if (created.get(this.primaryKeyAttribute, { raw: true }) === null) {
2309 // If the query returned an empty result for the primary key, we know that this was actually a unique constraint violation
2310 throw new sequelizeErrors.UniqueConstraintError();
2311 }
2312
2313 return [created, true];
2314 } catch (err) {
2315 if (!(err instanceof sequelizeErrors.UniqueConstraintError)) throw err;
2316 const flattenedWhere = Utils.flattenObjectDeep(options.where);
2317 const flattenedWhereKeys = Object.keys(flattenedWhere).map(name => _.last(name.split('.')));
2318 const whereFields = flattenedWhereKeys.map(name => _.get(this.rawAttributes, `${name}.field`, name));
2319 const defaultFields = options.defaults && Object.keys(options.defaults)
2320 .filter(name => this.rawAttributes[name])
2321 .map(name => this.rawAttributes[name].field || name);
2322
2323 const errFieldKeys = Object.keys(err.fields);
2324 const errFieldsWhereIntersects = Utils.intersects(errFieldKeys, whereFields);
2325 if (defaultFields && !errFieldsWhereIntersects && Utils.intersects(errFieldKeys, defaultFields)) {
2326 throw err;
2327 }
2328
2329 if (errFieldsWhereIntersects) {
2330 _.each(err.fields, (value, key) => {
2331 const name = this.fieldRawAttributesMap[key].fieldName;
2332 if (value.toString() !== options.where[name].toString()) {
2333 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}'`);
2334 }
2335 });
2336 }
2337
2338 // Someone must have created a matching instance inside the same transaction since we last did a find. Let's find it!
2339 const otherCreated = await this.findOne(Utils.defaults({
2340 transaction: internalTransaction ? null : transaction
2341 }, options));
2342
2343 // Sanity check, ideally we caught this at the defaultFeilds/err.fields check
2344 // But if we didn't and instance is null, we will throw
2345 if (otherCreated === null) throw err;
2346
2347 return [otherCreated, false];
2348 }
2349 } finally {
2350 if (internalTransaction && transaction) {
2351 await transaction.commit();
2352 }
2353 }
2354 }
2355
2356 /**
2357 * A more performant findOrCreate that will not work under a transaction (at least not in postgres)
2358 * Will execute a find call, if empty then attempt to create, if unique constraint then attempt to find again
2359 *
2360 * @see
2361 * {@link Model.findAll} for a full specification of find and options
2362 *
2363 * @param {object} options find options
2364 * @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.
2365 * @param {object} [options.defaults] Default values to use if creating a new instance
2366 *
2367 * @returns {Promise<Model,boolean>}
2368 */
2369 static async findCreateFind(options) {
2370 if (!options || !options.where) {
2371 throw new Error(
2372 'Missing where attribute in the options parameter passed to findCreateFind.'
2373 );
2374 }
2375
2376 let values = { ...options.defaults };
2377 if (_.isPlainObject(options.where)) {
2378 values = Utils.defaults(values, options.where);
2379 }
2380
2381
2382 const found = await this.findOne(options);
2383 if (found) return [found, false];
2384
2385 try {
2386 const created = await this.create(values, options);
2387 return [created, true];
2388 } catch (err) {
2389 if (!(err instanceof sequelizeErrors.UniqueConstraintError)) throw err;
2390 const foundAgain = await this.findOne(options);
2391 return [foundAgain, false];
2392 }
2393 }
2394
2395 /**
2396 * 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.
2397 *
2398 * **Implementation details:**
2399 *
2400 * * MySQL - Implemented with ON DUPLICATE KEY UPDATE`
2401 * * 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.
2402 * * SQLite - Implemented with ON CONFLICT DO UPDATE
2403 * * MSSQL - Implemented as a single query using `MERGE` and `WHEN (NOT) MATCHED THEN`
2404 *
2405 * **Note** that Postgres/SQLite returns null for created, no matter if the row was created or updated
2406 *
2407 * @param {object} values hash of values to upsert
2408 * @param {object} [options] upsert options
2409 * @param {boolean} [options.validate=true] Run validations before the row is inserted
2410 * @param {Array} [options.fields=Object.keys(this.attributes)] The fields to insert / update. Defaults to all changed fields
2411 * @param {boolean} [options.hooks=true] Run before / after upsert hooks?
2412 * @param {boolean} [options.returning=true] If true, fetches back auto generated values
2413 * @param {Transaction} [options.transaction] Transaction to run query under
2414 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2415 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2416 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2417 *
2418 * @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`.
2419 */
2420 static async upsert(values, options) {
2421 options = {
2422 hooks: true,
2423 returning: true,
2424 validate: true,
2425 ...Utils.cloneDeep(options)
2426 };
2427
2428 const createdAtAttr = this._timestampAttributes.createdAt;
2429 const updatedAtAttr = this._timestampAttributes.updatedAt;
2430 const hasPrimary = this.primaryKeyField in values || this.primaryKeyAttribute in values;
2431 const instance = this.build(values);
2432
2433 options.model = this;
2434 options.instance = instance;
2435
2436 const changed = Array.from(instance._changed);
2437 if (!options.fields) {
2438 options.fields = changed;
2439 }
2440
2441 if (options.validate) {
2442 await instance.validate(options);
2443 }
2444 // Map field names
2445 const updatedDataValues = _.pick(instance.dataValues, changed);
2446 const insertValues = Utils.mapValueFieldNames(instance.dataValues, Object.keys(instance.rawAttributes), this);
2447 const updateValues = Utils.mapValueFieldNames(updatedDataValues, options.fields, this);
2448 const now = Utils.now(this.sequelize.options.dialect);
2449
2450 // Attach createdAt
2451 if (createdAtAttr && !updateValues[createdAtAttr]) {
2452 const field = this.rawAttributes[createdAtAttr].field || createdAtAttr;
2453 insertValues[field] = this._getDefaultTimestamp(createdAtAttr) || now;
2454 }
2455 if (updatedAtAttr && !insertValues[updatedAtAttr]) {
2456 const field = this.rawAttributes[updatedAtAttr].field || updatedAtAttr;
2457 insertValues[field] = updateValues[field] = this._getDefaultTimestamp(updatedAtAttr) || now;
2458 }
2459
2460 // Build adds a null value for the primary key, if none was given by the user.
2461 // We need to remove that because of some Postgres technicalities.
2462 if (!hasPrimary && this.primaryKeyAttribute && !this.rawAttributes[this.primaryKeyAttribute].defaultValue) {
2463 delete insertValues[this.primaryKeyField];
2464 delete updateValues[this.primaryKeyField];
2465 }
2466
2467 if (options.hooks) {
2468 await this.runHooks('beforeUpsert', values, options);
2469 }
2470 const result = await this.queryInterface.upsert(this.getTableName(options), insertValues, updateValues, instance.where(), options);
2471
2472 if (options.hooks) {
2473 await this.runHooks('afterUpsert', result, options);
2474 return result;
2475 }
2476 return result;
2477 }
2478
2479 /**
2480 * Create and insert multiple instances in bulk.
2481 *
2482 * 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
2483 * 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.
2484 * To obtain Instances for the newly created values, you will need to query for them again.
2485 *
2486 * If validation fails, the promise is rejected with an array-like AggregateError
2487 *
2488 * @param {Array} records List of objects (key/value pairs) to create instances from
2489 * @param {object} [options] Bulk create options
2490 * @param {Array} [options.fields] Fields to insert (defaults to all fields)
2491 * @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
2492 * @param {boolean} [options.hooks=true] Run before / after bulk create hooks?
2493 * @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.
2494 * @param {boolean} [options.ignoreDuplicates=false] Ignore duplicate values for primary keys? (not supported by MSSQL or Postgres < 9.5)
2495 * @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.
2496 * @param {Transaction} [options.transaction] Transaction to run query under
2497 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2498 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2499 * @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)
2500 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2501 *
2502 * @returns {Promise<Array<Model>>}
2503 */
2504 static async bulkCreate(records, options = {}) {
2505 if (!records.length) {
2506 return [];
2507 }
2508
2509 const dialect = this.sequelize.options.dialect;
2510 const now = Utils.now(this.sequelize.options.dialect);
2511
2512 options.model = this;
2513
2514 if (!options.includeValidated) {
2515 this._conformIncludes(options, this);
2516 if (options.include) {
2517 this._expandIncludeAll(options);
2518 this._validateIncludedElements(options);
2519 }
2520 }
2521
2522 const instances = records.map(values => this.build(values, { isNewRecord: true, include: options.include }));
2523
2524 const recursiveBulkCreate = async (instances, options) => {
2525 options = {
2526 validate: false,
2527 hooks: true,
2528 individualHooks: false,
2529 ignoreDuplicates: false,
2530 ...options
2531 };
2532
2533 if (options.returning === undefined) {
2534 if (options.association) {
2535 options.returning = false;
2536 } else {
2537 options.returning = true;
2538 }
2539 }
2540
2541 if (options.ignoreDuplicates && ['mssql'].includes(dialect)) {
2542 throw new Error(`${dialect} does not support the ignoreDuplicates option.`);
2543 }
2544 if (options.updateOnDuplicate && (dialect !== 'mysql' && dialect !== 'mariadb' && dialect !== 'sqlite' && dialect !== 'postgres')) {
2545 throw new Error(`${dialect} does not support the updateOnDuplicate option.`);
2546 }
2547
2548 const model = options.model;
2549
2550 options.fields = options.fields || Object.keys(model.rawAttributes);
2551 const createdAtAttr = model._timestampAttributes.createdAt;
2552 const updatedAtAttr = model._timestampAttributes.updatedAt;
2553
2554 if (options.updateOnDuplicate !== undefined) {
2555 if (Array.isArray(options.updateOnDuplicate) && options.updateOnDuplicate.length) {
2556 options.updateOnDuplicate = _.intersection(
2557 _.without(Object.keys(model.tableAttributes), createdAtAttr),
2558 options.updateOnDuplicate
2559 );
2560 } else {
2561 throw new Error('updateOnDuplicate option only supports non-empty array.');
2562 }
2563 }
2564
2565 // Run before hook
2566 if (options.hooks) {
2567 await model.runHooks('beforeBulkCreate', instances, options);
2568 }
2569 // Validate
2570 if (options.validate) {
2571 const errors = [];
2572 const validateOptions = { ...options };
2573 validateOptions.hooks = options.individualHooks;
2574
2575 await Promise.all(instances.map(async instance => {
2576 try {
2577 await instance.validate(validateOptions);
2578 } catch (err) {
2579 errors.push(new sequelizeErrors.BulkRecordError(err, instance));
2580 }
2581 }));
2582
2583 delete options.skip;
2584 if (errors.length) {
2585 throw new sequelizeErrors.AggregateError(errors);
2586 }
2587 }
2588 if (options.individualHooks) {
2589 await Promise.all(instances.map(async instance => {
2590 const individualOptions = {
2591 ...options,
2592 validate: false,
2593 hooks: true
2594 };
2595 delete individualOptions.fields;
2596 delete individualOptions.individualHooks;
2597 delete individualOptions.ignoreDuplicates;
2598
2599 await instance.save(individualOptions);
2600 }));
2601 } else {
2602 if (options.include && options.include.length) {
2603 await Promise.all(options.include.filter(include => include.association instanceof BelongsTo).map(async include => {
2604 const associationInstances = [];
2605 const associationInstanceIndexToInstanceMap = [];
2606
2607 for (const instance of instances) {
2608 const associationInstance = instance.get(include.as);
2609 if (associationInstance) {
2610 associationInstances.push(associationInstance);
2611 associationInstanceIndexToInstanceMap.push(instance);
2612 }
2613 }
2614
2615 if (!associationInstances.length) {
2616 return;
2617 }
2618
2619 const includeOptions = _(Utils.cloneDeep(include))
2620 .omit(['association'])
2621 .defaults({
2622 transaction: options.transaction,
2623 logging: options.logging
2624 }).value();
2625
2626 const createdAssociationInstances = await recursiveBulkCreate(associationInstances, includeOptions);
2627 for (const idx in createdAssociationInstances) {
2628 const associationInstance = createdAssociationInstances[idx];
2629 const instance = associationInstanceIndexToInstanceMap[idx];
2630
2631 await include.association.set(instance, associationInstance, { save: false, logging: options.logging });
2632 }
2633 }));
2634 }
2635
2636 // Create all in one query
2637 // Recreate records from instances to represent any changes made in hooks or validation
2638 records = instances.map(instance => {
2639 const values = instance.dataValues;
2640
2641 // set createdAt/updatedAt attributes
2642 if (createdAtAttr && !values[createdAtAttr]) {
2643 values[createdAtAttr] = now;
2644 if (!options.fields.includes(createdAtAttr)) {
2645 options.fields.push(createdAtAttr);
2646 }
2647 }
2648 if (updatedAtAttr && !values[updatedAtAttr]) {
2649 values[updatedAtAttr] = now;
2650 if (!options.fields.includes(updatedAtAttr)) {
2651 options.fields.push(updatedAtAttr);
2652 }
2653 }
2654
2655 const out = Utils.mapValueFieldNames(values, options.fields, model);
2656 for (const key of model._virtualAttributes) {
2657 delete out[key];
2658 }
2659 return out;
2660 });
2661
2662 // Map attributes to fields for serial identification
2663 const fieldMappedAttributes = {};
2664 for (const attr in model.tableAttributes) {
2665 fieldMappedAttributes[model.rawAttributes[attr].field || attr] = model.rawAttributes[attr];
2666 }
2667
2668 // Map updateOnDuplicate attributes to fields
2669 if (options.updateOnDuplicate) {
2670 options.updateOnDuplicate = options.updateOnDuplicate.map(attr => model.rawAttributes[attr].field || attr);
2671 // Get primary keys for postgres to enable updateOnDuplicate
2672 options.upsertKeys = _.chain(model.primaryKeys).values().map('field').value();
2673 if (Object.keys(model.uniqueKeys).length > 0) {
2674 options.upsertKeys = _.chain(model.uniqueKeys).values().filter(c => c.fields.length >= 1).map(c => c.fields).reduce(c => c[0]).value();
2675 }
2676 }
2677
2678 // Map returning attributes to fields
2679 if (options.returning && Array.isArray(options.returning)) {
2680 options.returning = options.returning.map(attr => _.get(model.rawAttributes[attr], 'field', attr));
2681 }
2682
2683 const results = await model.queryInterface.bulkInsert(model.getTableName(options), records, options, fieldMappedAttributes);
2684 if (Array.isArray(results)) {
2685 results.forEach((result, i) => {
2686 const instance = instances[i];
2687
2688 for (const key in result) {
2689 if (!instance || key === model.primaryKeyAttribute &&
2690 instance.get(model.primaryKeyAttribute) &&
2691 ['mysql', 'mariadb', 'sqlite'].includes(dialect)) {
2692 // The query.js for these DBs is blind, it autoincrements the
2693 // primarykey value, even if it was set manually. Also, it can
2694 // return more results than instances, bug?.
2695 continue;
2696 }
2697 if (Object.prototype.hasOwnProperty.call(result, key)) {
2698 const record = result[key];
2699
2700 const attr = _.find(model.rawAttributes, attribute => attribute.fieldName === key || attribute.field === key);
2701
2702 instance.dataValues[attr && attr.fieldName || key] = record;
2703 }
2704 }
2705 });
2706 }
2707 }
2708
2709 if (options.include && options.include.length) {
2710 await Promise.all(options.include.filter(include => !(include.association instanceof BelongsTo ||
2711 include.parent && include.parent.association instanceof BelongsToMany)).map(async include => {
2712 const associationInstances = [];
2713 const associationInstanceIndexToInstanceMap = [];
2714
2715 for (const instance of instances) {
2716 let associated = instance.get(include.as);
2717 if (!Array.isArray(associated)) associated = [associated];
2718
2719 for (const associationInstance of associated) {
2720 if (associationInstance) {
2721 if (!(include.association instanceof BelongsToMany)) {
2722 associationInstance.set(include.association.foreignKey, instance.get(include.association.sourceKey || instance.constructor.primaryKeyAttribute, { raw: true }), { raw: true });
2723 Object.assign(associationInstance, include.association.scope);
2724 }
2725 associationInstances.push(associationInstance);
2726 associationInstanceIndexToInstanceMap.push(instance);
2727 }
2728 }
2729 }
2730
2731 if (!associationInstances.length) {
2732 return;
2733 }
2734
2735 const includeOptions = _(Utils.cloneDeep(include))
2736 .omit(['association'])
2737 .defaults({
2738 transaction: options.transaction,
2739 logging: options.logging
2740 }).value();
2741
2742 const createdAssociationInstances = await recursiveBulkCreate(associationInstances, includeOptions);
2743 if (include.association instanceof BelongsToMany) {
2744 const valueSets = [];
2745
2746 for (const idx in createdAssociationInstances) {
2747 const associationInstance = createdAssociationInstances[idx];
2748 const instance = associationInstanceIndexToInstanceMap[idx];
2749
2750 const values = {
2751 [include.association.foreignKey]: instance.get(instance.constructor.primaryKeyAttribute, { raw: true }),
2752 [include.association.otherKey]: associationInstance.get(associationInstance.constructor.primaryKeyAttribute, { raw: true }),
2753 // Include values defined in the association
2754 ...include.association.through.scope
2755 };
2756 if (associationInstance[include.association.through.model.name]) {
2757 for (const attr of Object.keys(include.association.through.model.rawAttributes)) {
2758 if (include.association.through.model.rawAttributes[attr]._autoGenerated ||
2759 attr === include.association.foreignKey ||
2760 attr === include.association.otherKey ||
2761 typeof associationInstance[include.association.through.model.name][attr] === undefined) {
2762 continue;
2763 }
2764 values[attr] = associationInstance[include.association.through.model.name][attr];
2765 }
2766 }
2767
2768 valueSets.push(values);
2769 }
2770
2771 const throughOptions = _(Utils.cloneDeep(include))
2772 .omit(['association', 'attributes'])
2773 .defaults({
2774 transaction: options.transaction,
2775 logging: options.logging
2776 }).value();
2777 throughOptions.model = include.association.throughModel;
2778 const throughInstances = include.association.throughModel.bulkBuild(valueSets, throughOptions);
2779
2780 await recursiveBulkCreate(throughInstances, throughOptions);
2781 }
2782 }));
2783 }
2784
2785 // map fields back to attributes
2786 instances.forEach(instance => {
2787 for (const attr in model.rawAttributes) {
2788 if (model.rawAttributes[attr].field &&
2789 instance.dataValues[model.rawAttributes[attr].field] !== undefined &&
2790 model.rawAttributes[attr].field !== attr
2791 ) {
2792 instance.dataValues[attr] = instance.dataValues[model.rawAttributes[attr].field];
2793 delete instance.dataValues[model.rawAttributes[attr].field];
2794 }
2795 instance._previousDataValues[attr] = instance.dataValues[attr];
2796 instance.changed(attr, false);
2797 }
2798 instance.isNewRecord = false;
2799 });
2800
2801 // Run after hook
2802 if (options.hooks) {
2803 await model.runHooks('afterBulkCreate', instances, options);
2804 }
2805
2806 return instances;
2807 };
2808
2809 return await recursiveBulkCreate(instances, options);
2810 }
2811
2812 /**
2813 * Truncate all instances of the model. This is a convenient method for Model.destroy({ truncate: true }).
2814 *
2815 * @param {object} [options] The options passed to Model.destroy in addition to truncate
2816 * @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.
2817 * @param {boolean} [options.restartIdentity=false] Automatically restart sequences owned by columns of the truncated table.
2818 * @param {Transaction} [options.transaction] Transaction to run query under
2819 * @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging
2820 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2821 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
2822 *
2823 * @returns {Promise}
2824 *
2825 * @see
2826 * {@link Model.destroy} for more information
2827 */
2828 static async truncate(options) {
2829 options = Utils.cloneDeep(options) || {};
2830 options.truncate = true;
2831 return await this.destroy(options);
2832 }
2833
2834 /**
2835 * Delete multiple instances, or set their deletedAt timestamp to the current time if `paranoid` is enabled.
2836 *
2837 * @param {object} options destroy options
2838 * @param {object} [options.where] Filter the destroy
2839 * @param {boolean} [options.hooks=true] Run before / after bulk destroy hooks?
2840 * @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
2841 * @param {number} [options.limit] How many rows to delete
2842 * @param {boolean} [options.force=false] Delete instead of setting deletedAt to current timestamp (only applicable if `paranoid` is enabled)
2843 * @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
2844 * @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.
2845 * @param {boolean} [options.restartIdentity=false] Only used in conjunction with TRUNCATE. Automatically restart sequences owned by columns of the truncated table.
2846 * @param {Transaction} [options.transaction] Transaction to run query under
2847 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2848 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2849 *
2850 * @returns {Promise<number>} The number of destroyed rows
2851 */
2852 static async destroy(options) {
2853 options = Utils.cloneDeep(options);
2854
2855 this._injectScope(options);
2856
2857 if (!options || !(options.where || options.truncate)) {
2858 throw new Error('Missing where or truncate attribute in the options parameter of model.destroy.');
2859 }
2860
2861 if (!options.truncate && !_.isPlainObject(options.where) && !Array.isArray(options.where) && !(options.where instanceof Utils.SequelizeMethod)) {
2862 throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.destroy.');
2863 }
2864
2865 options = _.defaults(options, {
2866 hooks: true,
2867 individualHooks: false,
2868 force: false,
2869 cascade: false,
2870 restartIdentity: false
2871 });
2872
2873 options.type = QueryTypes.BULKDELETE;
2874
2875 Utils.mapOptionFieldNames(options, this);
2876 options.model = this;
2877
2878
2879 // Run before hook
2880 if (options.hooks) {
2881 await this.runHooks('beforeBulkDestroy', options);
2882 }
2883 let instances;
2884 // Get daos and run beforeDestroy hook on each record individually
2885 if (options.individualHooks) {
2886 instances = await this.findAll({ where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark });
2887
2888 await Promise.all(instances.map(instance => this.runHooks('beforeDestroy', instance, options)));
2889 }
2890 let result;
2891 // Run delete query (or update if paranoid)
2892 if (this._timestampAttributes.deletedAt && !options.force) {
2893 // Set query type appropriately when running soft delete
2894 options.type = QueryTypes.BULKUPDATE;
2895
2896 const attrValueHash = {};
2897 const deletedAtAttribute = this.rawAttributes[this._timestampAttributes.deletedAt];
2898 const field = this.rawAttributes[this._timestampAttributes.deletedAt].field;
2899 const where = {
2900 [field]: Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null
2901 };
2902
2903
2904 attrValueHash[field] = Utils.now(this.sequelize.options.dialect);
2905 result = await this.queryInterface.bulkUpdate(this.getTableName(options), attrValueHash, Object.assign(where, options.where), options, this.rawAttributes);
2906 } else {
2907 result = await this.queryInterface.bulkDelete(this.getTableName(options), options.where, options, this);
2908 }
2909 // Run afterDestroy hook on each record individually
2910 if (options.individualHooks) {
2911 await Promise.all(
2912 instances.map(instance => this.runHooks('afterDestroy', instance, options))
2913 );
2914 }
2915 // Run after hook
2916 if (options.hooks) {
2917 await this.runHooks('afterBulkDestroy', options);
2918 }
2919 return result;
2920 }
2921
2922 /**
2923 * Restore multiple instances if `paranoid` is enabled.
2924 *
2925 * @param {object} options restore options
2926 * @param {object} [options.where] Filter the restore
2927 * @param {boolean} [options.hooks=true] Run before / after bulk restore hooks?
2928 * @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
2929 * @param {number} [options.limit] How many rows to undelete (only for mysql)
2930 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2931 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
2932 * @param {Transaction} [options.transaction] Transaction to run query under
2933 *
2934 * @returns {Promise}
2935 */
2936 static async restore(options) {
2937 if (!this._timestampAttributes.deletedAt) throw new Error('Model is not paranoid');
2938
2939 options = {
2940 hooks: true,
2941 individualHooks: false,
2942 ...options
2943 };
2944
2945 options.type = QueryTypes.RAW;
2946 options.model = this;
2947
2948 Utils.mapOptionFieldNames(options, this);
2949
2950 // Run before hook
2951 if (options.hooks) {
2952 await this.runHooks('beforeBulkRestore', options);
2953 }
2954
2955 let instances;
2956 // Get daos and run beforeRestore hook on each record individually
2957 if (options.individualHooks) {
2958 instances = await this.findAll({ where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark, paranoid: false });
2959
2960 await Promise.all(instances.map(instance => this.runHooks('beforeRestore', instance, options)));
2961 }
2962 // Run undelete query
2963 const attrValueHash = {};
2964 const deletedAtCol = this._timestampAttributes.deletedAt;
2965 const deletedAtAttribute = this.rawAttributes[deletedAtCol];
2966 const deletedAtDefaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
2967
2968 attrValueHash[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
2969 options.omitNull = false;
2970 const result = await this.queryInterface.bulkUpdate(this.getTableName(options), attrValueHash, options.where, options, this.rawAttributes);
2971 // Run afterDestroy hook on each record individually
2972 if (options.individualHooks) {
2973 await Promise.all(
2974 instances.map(instance => this.runHooks('afterRestore', instance, options))
2975 );
2976 }
2977 // Run after hook
2978 if (options.hooks) {
2979 await this.runHooks('afterBulkRestore', options);
2980 }
2981 return result;
2982 }
2983
2984 /**
2985 * Update multiple instances that match the where options.
2986 *
2987 * @param {object} values hash of values to update
2988 * @param {object} options update options
2989 * @param {object} options.where Options to describe the scope of the search.
2990 * @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.
2991 * @param {Array} [options.fields] Fields to update (defaults to all fields)
2992 * @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
2993 * @param {boolean} [options.hooks=true] Run before / after bulk update hooks?
2994 * @param {boolean} [options.sideEffects=true] Whether or not to update the side effects of any virtual setters.
2995 * @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
2996 * @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)
2997 * @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)
2998 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
2999 * @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging).
3000 * @param {Transaction} [options.transaction] Transaction to run query under
3001 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
3002 *
3003 * @returns {Promise<Array<number,number>>} The promise returns an array with one or two elements. The first element is always the number
3004 * of affected rows, while the second element is the actual affected rows (only supported in postgres with `options.returning` true).
3005 *
3006 */
3007 static async update(values, options) {
3008 options = Utils.cloneDeep(options);
3009
3010 this._injectScope(options);
3011 this._optionsMustContainWhere(options);
3012
3013 options = this._paranoidClause(this, _.defaults(options, {
3014 validate: true,
3015 hooks: true,
3016 individualHooks: false,
3017 returning: false,
3018 force: false,
3019 sideEffects: true
3020 }));
3021
3022 options.type = QueryTypes.BULKUPDATE;
3023
3024 // Clone values so it doesn't get modified for caller scope and ignore undefined values
3025 values = _.omitBy(values, value => value === undefined);
3026
3027 // Remove values that are not in the options.fields
3028 if (options.fields && options.fields instanceof Array) {
3029 for (const key of Object.keys(values)) {
3030 if (!options.fields.includes(key)) {
3031 delete values[key];
3032 }
3033 }
3034 } else {
3035 const updatedAtAttr = this._timestampAttributes.updatedAt;
3036 options.fields = _.intersection(Object.keys(values), Object.keys(this.tableAttributes));
3037 if (updatedAtAttr && !options.fields.includes(updatedAtAttr)) {
3038 options.fields.push(updatedAtAttr);
3039 }
3040 }
3041
3042 if (this._timestampAttributes.updatedAt && !options.silent) {
3043 values[this._timestampAttributes.updatedAt] = this._getDefaultTimestamp(this._timestampAttributes.updatedAt) || Utils.now(this.sequelize.options.dialect);
3044 }
3045
3046 options.model = this;
3047
3048 let valuesUse;
3049 // Validate
3050 if (options.validate) {
3051 const build = this.build(values);
3052 build.set(this._timestampAttributes.updatedAt, values[this._timestampAttributes.updatedAt], { raw: true });
3053
3054 if (options.sideEffects) {
3055 Object.assign(values, _.pick(build.get(), build.changed()));
3056 options.fields = _.union(options.fields, Object.keys(values));
3057 }
3058
3059 // We want to skip validations for all other fields
3060 options.skip = _.difference(Object.keys(this.rawAttributes), Object.keys(values));
3061 const attributes = await build.validate(options);
3062 options.skip = undefined;
3063 if (attributes && attributes.dataValues) {
3064 values = _.pick(attributes.dataValues, Object.keys(values));
3065 }
3066 }
3067 // Run before hook
3068 if (options.hooks) {
3069 options.attributes = values;
3070 await this.runHooks('beforeBulkUpdate', options);
3071 values = options.attributes;
3072 delete options.attributes;
3073 }
3074
3075 valuesUse = values;
3076
3077 // Get instances and run beforeUpdate hook on each record individually
3078 let instances;
3079 let updateDoneRowByRow = false;
3080 if (options.individualHooks) {
3081 instances = await this.findAll({
3082 where: options.where,
3083 transaction: options.transaction,
3084 logging: options.logging,
3085 benchmark: options.benchmark,
3086 paranoid: options.paranoid
3087 });
3088
3089 if (instances.length) {
3090 // Run beforeUpdate hooks on each record and check whether beforeUpdate hook changes values uniformly
3091 // i.e. whether they change values for each record in the same way
3092 let changedValues;
3093 let different = false;
3094
3095 instances = await Promise.all(instances.map(async instance => {
3096 // Record updates in instances dataValues
3097 Object.assign(instance.dataValues, values);
3098 // Set the changed fields on the instance
3099 _.forIn(valuesUse, (newValue, attr) => {
3100 if (newValue !== instance._previousDataValues[attr]) {
3101 instance.setDataValue(attr, newValue);
3102 }
3103 });
3104
3105 // Run beforeUpdate hook
3106 await this.runHooks('beforeUpdate', instance, options);
3107 if (!different) {
3108 const thisChangedValues = {};
3109 _.forIn(instance.dataValues, (newValue, attr) => {
3110 if (newValue !== instance._previousDataValues[attr]) {
3111 thisChangedValues[attr] = newValue;
3112 }
3113 });
3114
3115 if (!changedValues) {
3116 changedValues = thisChangedValues;
3117 } else {
3118 different = !_.isEqual(changedValues, thisChangedValues);
3119 }
3120 }
3121
3122 return instance;
3123 }));
3124
3125 if (!different) {
3126 const keys = Object.keys(changedValues);
3127 // Hooks do not change values or change them uniformly
3128 if (keys.length) {
3129 // Hooks change values - record changes in valuesUse so they are executed
3130 valuesUse = changedValues;
3131 options.fields = _.union(options.fields, keys);
3132 }
3133 } else {
3134 instances = await Promise.all(instances.map(async instance => {
3135 const individualOptions = {
3136 ...options,
3137 hooks: false,
3138 validate: false
3139 };
3140 delete individualOptions.individualHooks;
3141
3142 return instance.save(individualOptions);
3143 }));
3144 updateDoneRowByRow = true;
3145 }
3146 }
3147 }
3148 let result;
3149 if (updateDoneRowByRow) {
3150 result = [instances.length, instances];
3151 } else if (_.isEmpty(valuesUse)
3152 || Object.keys(valuesUse).length === 1 && valuesUse[this._timestampAttributes.updatedAt]) {
3153 // only updatedAt is being passed, then skip update
3154 result = [0];
3155 } else {
3156 valuesUse = Utils.mapValueFieldNames(valuesUse, options.fields, this);
3157 options = Utils.mapOptionFieldNames(options, this);
3158 options.hasTrigger = this.options ? this.options.hasTrigger : false;
3159
3160 const affectedRows = await this.queryInterface.bulkUpdate(this.getTableName(options), valuesUse, options.where, options, this.tableAttributes);
3161 if (options.returning) {
3162 result = [affectedRows.length, affectedRows];
3163 instances = affectedRows;
3164 } else {
3165 result = [affectedRows];
3166 }
3167 }
3168
3169 if (options.individualHooks) {
3170 await Promise.all(instances.map(instance => this.runHooks('afterUpdate', instance, options)));
3171 result[1] = instances;
3172 }
3173 // Run after hook
3174 if (options.hooks) {
3175 options.attributes = values;
3176 await this.runHooks('afterBulkUpdate', options);
3177 delete options.attributes;
3178 }
3179 return result;
3180 }
3181
3182 /**
3183 * Run a describe query on the table.
3184 *
3185 * @param {string} [schema] schema name to search table in
3186 * @param {object} [options] query options
3187 *
3188 * @returns {Promise} hash of attributes and their types
3189 */
3190 static async describe(schema, options) {
3191 return await this.queryInterface.describeTable(this.tableName, { schema: schema || this._schema || undefined, ...options });
3192 }
3193
3194 static _getDefaultTimestamp(attr) {
3195 if (!!this.rawAttributes[attr] && !!this.rawAttributes[attr].defaultValue) {
3196 return Utils.toDefaultValue(this.rawAttributes[attr].defaultValue, this.sequelize.options.dialect);
3197 }
3198 return undefined;
3199 }
3200
3201 static _expandAttributes(options) {
3202 if (!_.isPlainObject(options.attributes)) {
3203 return;
3204 }
3205 let attributes = Object.keys(this.rawAttributes);
3206
3207 if (options.attributes.exclude) {
3208 attributes = attributes.filter(elem => !options.attributes.exclude.includes(elem));
3209 }
3210
3211 if (options.attributes.include) {
3212 attributes = attributes.concat(options.attributes.include);
3213 }
3214
3215 options.attributes = attributes;
3216 }
3217
3218 // Inject _scope into options.
3219 static _injectScope(options) {
3220 const scope = Utils.cloneDeep(this._scope);
3221 this._defaultsOptions(options, scope);
3222 }
3223
3224 static [Symbol.for('nodejs.util.inspect.custom')]() {
3225 return this.name;
3226 }
3227
3228 static hasAlias(alias) {
3229 return Object.prototype.hasOwnProperty.call(this.associations, alias);
3230 }
3231
3232 /**
3233 * 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
3234 * ``` SET column = column + X WHERE foo = 'bar' ``` query. To get the correct value after an increment into the Instance you should do a reload.
3235 *
3236 * @example <caption>increment number by 1</caption>
3237 * Model.increment('number', { where: { foo: 'bar' });
3238 *
3239 * @example <caption>increment number and count by 2</caption>
3240 * Model.increment(['number', 'count'], { by: 2, where: { foo: 'bar' } });
3241 *
3242 * @example <caption>increment answer by 42, and decrement tries by 1</caption>
3243 * // `by` is ignored, as each column has its own value
3244 * Model.increment({ answer: 42, tries: -1}, { by: 2, where: { foo: 'bar' } });
3245 *
3246 * @see
3247 * {@link Model#reload}
3248 *
3249 * @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.
3250 * @param {object} options increment options
3251 * @param {object} options.where conditions hash
3252 * @param {number} [options.by=1] The number to increment by
3253 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
3254 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
3255 * @param {Transaction} [options.transaction] Transaction to run query under
3256 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
3257 *
3258 * @returns {Promise<Model[],?number>} returns an array of affected rows and affected count with `options.returning` true, whenever supported by dialect
3259 */
3260 static async increment(fields, options) {
3261 options = options || {};
3262 if (typeof fields === 'string') fields = [fields];
3263 if (Array.isArray(fields)) {
3264 fields = fields.map(f => {
3265 if (this.rawAttributes[f] && this.rawAttributes[f].field && this.rawAttributes[f].field !== f) {
3266 return this.rawAttributes[f].field;
3267 }
3268 return f;
3269 });
3270 }
3271
3272 this._injectScope(options);
3273 this._optionsMustContainWhere(options);
3274
3275 options = Utils.defaults({}, options, {
3276 by: 1,
3277 where: {},
3278 increment: true
3279 });
3280 const isSubtraction = !options.increment;
3281
3282 Utils.mapOptionFieldNames(options, this);
3283
3284 const where = { ...options.where };
3285
3286 // A plain object whose keys are the fields to be incremented and whose values are
3287 // the amounts to be incremented by.
3288 let incrementAmountsByField = {};
3289 if (Array.isArray(fields)) {
3290 incrementAmountsByField = {};
3291 for (const field of fields) {
3292 incrementAmountsByField[field] = options.by;
3293 }
3294 } else {
3295 // If the `fields` argument is not an array, then we assume it already has the
3296 // form necessary to be placed directly in the `incrementAmountsByField` variable.
3297 incrementAmountsByField = fields;
3298 }
3299
3300 // If optimistic locking is enabled, we can take advantage that this is an
3301 // increment/decrement operation and send it here as well. We put `-1` for
3302 // decrementing because it will be subtracted, getting `-(-1)` which is `+1`
3303 if (this._versionAttribute) {
3304 incrementAmountsByField[this._versionAttribute] = isSubtraction ? -1 : 1;
3305 }
3306
3307 const extraAttributesToBeUpdated = {};
3308
3309 const updatedAtAttr = this._timestampAttributes.updatedAt;
3310 if (!options.silent && updatedAtAttr && !incrementAmountsByField[updatedAtAttr]) {
3311 const attrName = this.rawAttributes[updatedAtAttr].field || updatedAtAttr;
3312 extraAttributesToBeUpdated[attrName] = this._getDefaultTimestamp(updatedAtAttr) || Utils.now(this.sequelize.options.dialect);
3313 }
3314
3315 const tableName = this.getTableName(options);
3316 let affectedRows;
3317 if (isSubtraction) {
3318 affectedRows = await this.queryInterface.decrement(
3319 this, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options
3320 );
3321 } else {
3322 affectedRows = await this.queryInterface.increment(
3323 this, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options
3324 );
3325 }
3326
3327 if (options.returning) {
3328 return [affectedRows, affectedRows.length];
3329 }
3330
3331 return [affectedRows];
3332 }
3333
3334 /**
3335 * 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
3336 * ```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.
3337 *
3338 * @example <caption>decrement number by 1</caption>
3339 * Model.decrement('number', { where: { foo: 'bar' });
3340 *
3341 * @example <caption>decrement number and count by 2</caption>
3342 * Model.decrement(['number', 'count'], { by: 2, where: { foo: 'bar' } });
3343 *
3344 * @example <caption>decrement answer by 42, and decrement tries by -1</caption>
3345 * // `by` is ignored, since each column has its own value
3346 * Model.decrement({ answer: 42, tries: -1}, { by: 2, where: { foo: 'bar' } });
3347 *
3348 * @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.
3349 * @param {object} options decrement options, similar to increment
3350 *
3351 * @see
3352 * {@link Model.increment}
3353 * @see
3354 * {@link Model#reload}
3355 * @since 4.36.0
3356 *
3357 * @returns {Promise<Model[],?number>} returns an array of affected rows and affected count with `options.returning` true, whenever supported by dialect
3358 */
3359 static async decrement(fields, options) {
3360 return this.increment(fields, {
3361 by: 1,
3362 ...options,
3363 increment: false
3364 });
3365 }
3366
3367 static _optionsMustContainWhere(options) {
3368 assert(options && options.where, 'Missing where attribute in the options parameter');
3369 assert(_.isPlainObject(options.where) || Array.isArray(options.where) || options.where instanceof Utils.SequelizeMethod,
3370 'Expected plain object, array or sequelize method in the options.where parameter');
3371 }
3372
3373 /**
3374 * Get an object representing the query for this instance, use with `options.where`
3375 *
3376 * @param {boolean} [checkVersion=false] include version attribute in where hash
3377 *
3378 * @returns {object}
3379 */
3380 where(checkVersion) {
3381 const where = this.constructor.primaryKeyAttributes.reduce((result, attribute) => {
3382 result[attribute] = this.get(attribute, { raw: true });
3383 return result;
3384 }, {});
3385
3386 if (_.size(where) === 0) {
3387 return this.constructor.options.whereCollection;
3388 }
3389 const versionAttr = this.constructor._versionAttribute;
3390 if (checkVersion && versionAttr) {
3391 where[versionAttr] = this.get(versionAttr, { raw: true });
3392 }
3393 return Utils.mapWhereFieldNames(where, this.constructor);
3394 }
3395
3396 toString() {
3397 return `[object SequelizeInstance:${this.constructor.name}]`;
3398 }
3399
3400 /**
3401 * Get the value of the underlying data value
3402 *
3403 * @param {string} key key to look in instance data store
3404 *
3405 * @returns {any}
3406 */
3407 getDataValue(key) {
3408 return this.dataValues[key];
3409 }
3410
3411 /**
3412 * Update the underlying data value
3413 *
3414 * @param {string} key key to set in instance data store
3415 * @param {any} value new value for given key
3416 *
3417 */
3418 setDataValue(key, value) {
3419 const originalValue = this._previousDataValues[key];
3420
3421 if (!_.isEqual(value, originalValue)) {
3422 this.changed(key, true);
3423 }
3424
3425 this.dataValues[key] = value;
3426 }
3427
3428 /**
3429 * If no key is given, returns all values of the instance, also invoking virtual getters.
3430 *
3431 * 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.
3432 *
3433 * @param {string} [key] key to get value of
3434 * @param {object} [options] get options
3435 * @param {boolean} [options.plain=false] If set to true, included instances will be returned as plain objects
3436 * @param {boolean} [options.raw=false] If set to true, field and virtual setters will be ignored
3437 *
3438 * @returns {object|any}
3439 */
3440 get(key, options) {
3441 if (options === undefined && typeof key === 'object') {
3442 options = key;
3443 key = undefined;
3444 }
3445
3446 options = options || {};
3447
3448 if (key) {
3449 if (Object.prototype.hasOwnProperty.call(this._customGetters, key) && !options.raw) {
3450 return this._customGetters[key].call(this, key, options);
3451 }
3452
3453 if (options.plain && this._options.include && this._options.includeNames.includes(key)) {
3454 if (Array.isArray(this.dataValues[key])) {
3455 return this.dataValues[key].map(instance => instance.get(options));
3456 }
3457 if (this.dataValues[key] instanceof Model) {
3458 return this.dataValues[key].get(options);
3459 }
3460 return this.dataValues[key];
3461 }
3462
3463 return this.dataValues[key];
3464 }
3465
3466 if (
3467 this._hasCustomGetters
3468 || options.plain && this._options.include
3469 || options.clone
3470 ) {
3471 const values = {};
3472 let _key;
3473
3474 if (this._hasCustomGetters) {
3475 for (_key in this._customGetters) {
3476 if (
3477 this._options.attributes
3478 && !this._options.attributes.includes(_key)
3479 ) {
3480 continue;
3481 }
3482
3483 if (Object.prototype.hasOwnProperty.call(this._customGetters, _key)) {
3484 values[_key] = this.get(_key, options);
3485 }
3486 }
3487 }
3488
3489 for (_key in this.dataValues) {
3490 if (
3491 !Object.prototype.hasOwnProperty.call(values, _key)
3492 && Object.prototype.hasOwnProperty.call(this.dataValues, _key)
3493 ) {
3494 values[_key] = this.get(_key, options);
3495 }
3496 }
3497
3498 return values;
3499 }
3500
3501 return this.dataValues;
3502 }
3503
3504 /**
3505 * 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`).
3506 * 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
3507 * will be called instead. To bypass the setter, you can pass `raw: true` in the options object.
3508 *
3509 * 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
3510 * set directly to the object passed, or used to extend dataValues, if dataValues already contain values.
3511 *
3512 * When set is called, the previous value of the field is stored and sets a changed flag(see `changed`).
3513 *
3514 * Set can also be used to build instances for associations, if you have values for those.
3515 * When using set with associations you need to make sure the property key matches the alias of the association
3516 * while also making sure that the proper include options have been set (from .build() or .findOne())
3517 *
3518 * 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.
3519 *
3520 * @see
3521 * {@link Model.findAll} for more information about includes
3522 *
3523 * @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.
3524 * @param {any} value value to set
3525 * @param {object} [options] set options
3526 * @param {boolean} [options.raw=false] If set to true, field and virtual setters will be ignored
3527 * @param {boolean} [options.reset=false] Clear all previously set data values
3528 *
3529 * @returns {Model}
3530 */
3531 set(key, value, options) {
3532 let values;
3533 let originalValue;
3534
3535 if (typeof key === 'object' && key !== null) {
3536 values = key;
3537 options = value || {};
3538
3539 if (options.reset) {
3540 this.dataValues = {};
3541 for (const key in values) {
3542 this.changed(key, false);
3543 }
3544 }
3545
3546 // If raw, and we're not dealing with includes or special attributes, just set it straight on the dataValues object
3547 if (options.raw && !(this._options && this._options.include) && !(options && options.attributes) && !this.constructor._hasDateAttributes && !this.constructor._hasBooleanAttributes) {
3548 if (Object.keys(this.dataValues).length) {
3549 Object.assign(this.dataValues, values);
3550 } else {
3551 this.dataValues = values;
3552 }
3553 // If raw, .changed() shouldn't be true
3554 this._previousDataValues = { ...this.dataValues };
3555 } else {
3556 // Loop and call set
3557 if (options.attributes) {
3558 const setKeys = data => {
3559 for (const k of data) {
3560 if (values[k] === undefined) {
3561 continue;
3562 }
3563 this.set(k, values[k], options);
3564 }
3565 };
3566 setKeys(options.attributes);
3567 if (this.constructor._hasVirtualAttributes) {
3568 setKeys(this.constructor._virtualAttributes);
3569 }
3570 if (this._options.includeNames) {
3571 setKeys(this._options.includeNames);
3572 }
3573 } else {
3574 for (const key in values) {
3575 this.set(key, values[key], options);
3576 }
3577 }
3578
3579 if (options.raw) {
3580 // If raw, .changed() shouldn't be true
3581 this._previousDataValues = { ...this.dataValues };
3582 }
3583 }
3584 return this;
3585 }
3586 if (!options)
3587 options = {};
3588 if (!options.raw) {
3589 originalValue = this.dataValues[key];
3590 }
3591
3592 // If not raw, and there's a custom setter
3593 if (!options.raw && this._customSetters[key]) {
3594 this._customSetters[key].call(this, value, key);
3595 // custom setter should have changed value, get that changed value
3596 // TODO: v5 make setters return new value instead of changing internal store
3597 const newValue = this.dataValues[key];
3598 if (!_.isEqual(newValue, originalValue)) {
3599 this._previousDataValues[key] = originalValue;
3600 this.changed(key, true);
3601 }
3602 } else {
3603 // Check if we have included models, and if this key matches the include model names/aliases
3604 if (this._options && this._options.include && this._options.includeNames.includes(key)) {
3605 // Pass it on to the include handler
3606 this._setInclude(key, value, options);
3607 return this;
3608 }
3609 // Bunch of stuff we won't do when it's raw
3610 if (!options.raw) {
3611 // If attribute is not in model definition, return
3612 if (!this._isAttribute(key)) {
3613 if (key.includes('.') && this.constructor._jsonAttributes.has(key.split('.')[0])) {
3614 const previousNestedValue = Dottie.get(this.dataValues, key);
3615 if (!_.isEqual(previousNestedValue, value)) {
3616 Dottie.set(this.dataValues, key, value);
3617 this.changed(key.split('.')[0], true);
3618 }
3619 }
3620 return this;
3621 }
3622
3623 // If attempting to set primary key and primary key is already defined, return
3624 if (this.constructor._hasPrimaryKeys && originalValue && this.constructor._isPrimaryKey(key)) {
3625 return this;
3626 }
3627
3628 // If attempting to set read only attributes, return
3629 if (!this.isNewRecord && this.constructor._hasReadOnlyAttributes && this.constructor._readOnlyAttributes.has(key)) {
3630 return this;
3631 }
3632 }
3633
3634 // If there's a data type sanitizer
3635 if (
3636 !(value instanceof Utils.SequelizeMethod)
3637 && Object.prototype.hasOwnProperty.call(this.constructor._dataTypeSanitizers, key)
3638 ) {
3639 value = this.constructor._dataTypeSanitizers[key].call(this, value, options);
3640 }
3641
3642 // Set when the value has changed and not raw
3643 if (
3644 !options.raw &&
3645 (
3646 // True when sequelize method
3647 (value instanceof Utils.SequelizeMethod ||
3648 // Check for data type type comparators
3649 !(value instanceof Utils.SequelizeMethod) && this.constructor._dataTypeChanges[key] && this.constructor._dataTypeChanges[key].call(this, value, originalValue, options) || // Check default
3650 !this.constructor._dataTypeChanges[key] && !_.isEqual(value, originalValue))
3651 )
3652 ) {
3653 this._previousDataValues[key] = originalValue;
3654 this.changed(key, true);
3655 }
3656
3657 // set data value
3658 this.dataValues[key] = value;
3659 }
3660 return this;
3661 }
3662
3663 setAttributes(updates) {
3664 return this.set(updates);
3665 }
3666
3667 /**
3668 * 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`.
3669 *
3670 * If changed is called without an argument, it will return an array of keys that have changed.
3671 *
3672 * If changed is called without an argument and no keys have changed, it will return `false`.
3673 *
3674 * Please note that this function will return `false` when a property from a nested (for example JSON) property
3675 * was edited manually, you must call `changed('key', true)` manually in these cases.
3676 * Writing an entirely new object (eg. deep cloned) will be detected.
3677 *
3678 * @example
3679 * ```
3680 * const mdl = await MyModel.findOne();
3681 * mdl.myJsonField.a = 1;
3682 * console.log(mdl.changed()) => false
3683 * mdl.save(); // this will not save anything
3684 * mdl.changed('myJsonField', true);
3685 * console.log(mdl.changed()) => ['myJsonField']
3686 * mdl.save(); // will save
3687 * ```
3688 *
3689 * @param {string} [key] key to check or change status of
3690 * @param {any} [value] value to set
3691 *
3692 * @returns {boolean|Array}
3693 */
3694 changed(key, value) {
3695 if (key === undefined) {
3696 if (this._changed.size > 0) {
3697 return Array.from(this._changed);
3698 }
3699 return false;
3700 }
3701 if (value === true) {
3702 this._changed.add(key);
3703 return this;
3704 }
3705 if (value === false) {
3706 this._changed.delete(key);
3707 return this;
3708 }
3709 return this._changed.has(key);
3710 }
3711
3712 /**
3713 * Returns the previous value for key from `_previousDataValues`.
3714 *
3715 * If called without a key, returns the previous values for all values which have changed
3716 *
3717 * @param {string} [key] key to get previous value of
3718 *
3719 * @returns {any|Array<any>}
3720 */
3721 previous(key) {
3722 if (key) {
3723 return this._previousDataValues[key];
3724 }
3725
3726 return _.pickBy(this._previousDataValues, (value, key) => this.changed(key));
3727 }
3728
3729 _setInclude(key, value, options) {
3730 if (!Array.isArray(value)) value = [value];
3731 if (value[0] instanceof Model) {
3732 value = value.map(instance => instance.dataValues);
3733 }
3734
3735 const include = this._options.includeMap[key];
3736 const association = include.association;
3737 const accessor = key;
3738 const primaryKeyAttribute = include.model.primaryKeyAttribute;
3739 const childOptions = {
3740 isNewRecord: this.isNewRecord,
3741 include: include.include,
3742 includeNames: include.includeNames,
3743 includeMap: include.includeMap,
3744 includeValidated: true,
3745 raw: options.raw,
3746 attributes: include.originalAttributes
3747 };
3748 let isEmpty;
3749
3750 if (include.originalAttributes === undefined || include.originalAttributes.length) {
3751 if (association.isSingleAssociation) {
3752 if (Array.isArray(value)) {
3753 value = value[0];
3754 }
3755 isEmpty = value && value[primaryKeyAttribute] === null || value === null;
3756 this[accessor] = this.dataValues[accessor] = isEmpty ? null : include.model.build(value, childOptions);
3757 } else {
3758 isEmpty = value[0] && value[0][primaryKeyAttribute] === null;
3759 this[accessor] = this.dataValues[accessor] = isEmpty ? [] : include.model.bulkBuild(value, childOptions);
3760 }
3761 }
3762 }
3763
3764 /**
3765 * Validates this instance, and if the validation passes, persists it to the database.
3766 *
3767 * 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).
3768 *
3769 * This method is optimized to perform an UPDATE only into the fields that changed. If nothing has changed, no SQL query will be performed.
3770 *
3771 * 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.
3772 *
3773 * @param {object} [options] save options
3774 * @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
3775 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
3776 * @param {boolean} [options.validate=true] If false, validations won't be run.
3777 * @param {boolean} [options.hooks=true] Run before and after create / update + validate hooks
3778 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
3779 * @param {Transaction} [options.transaction] Transaction to run query under
3780 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
3781 * @param {boolean} [options.returning] Append RETURNING * to get back auto generated values (Postgres only)
3782 *
3783 * @returns {Promise<Model>}
3784 */
3785 async save(options) {
3786 if (arguments.length > 1) {
3787 throw new Error('The second argument was removed in favor of the options object.');
3788 }
3789
3790 options = Utils.cloneDeep(options);
3791 options = _.defaults(options, {
3792 hooks: true,
3793 validate: true
3794 });
3795
3796 if (!options.fields) {
3797 if (this.isNewRecord) {
3798 options.fields = Object.keys(this.constructor.rawAttributes);
3799 } else {
3800 options.fields = _.intersection(this.changed(), Object.keys(this.constructor.rawAttributes));
3801 }
3802
3803 options.defaultFields = options.fields;
3804 }
3805
3806 if (options.returning === undefined) {
3807 if (options.association) {
3808 options.returning = false;
3809 } else if (this.isNewRecord) {
3810 options.returning = true;
3811 }
3812 }
3813
3814 const primaryKeyName = this.constructor.primaryKeyAttribute;
3815 const primaryKeyAttribute = primaryKeyName && this.constructor.rawAttributes[primaryKeyName];
3816 const createdAtAttr = this.constructor._timestampAttributes.createdAt;
3817 const versionAttr = this.constructor._versionAttribute;
3818 const hook = this.isNewRecord ? 'Create' : 'Update';
3819 const wasNewRecord = this.isNewRecord;
3820 const now = Utils.now(this.sequelize.options.dialect);
3821 let updatedAtAttr = this.constructor._timestampAttributes.updatedAt;
3822
3823 if (updatedAtAttr && options.fields.length >= 1 && !options.fields.includes(updatedAtAttr)) {
3824 options.fields.push(updatedAtAttr);
3825 }
3826 if (versionAttr && options.fields.length >= 1 && !options.fields.includes(versionAttr)) {
3827 options.fields.push(versionAttr);
3828 }
3829
3830 if (options.silent === true && !(this.isNewRecord && this.get(updatedAtAttr, { raw: true }))) {
3831 // UpdateAtAttr might have been added as a result of Object.keys(Model.rawAttributes). In that case we have to remove it again
3832 _.remove(options.fields, val => val === updatedAtAttr);
3833 updatedAtAttr = false;
3834 }
3835
3836 if (this.isNewRecord === true) {
3837 if (createdAtAttr && !options.fields.includes(createdAtAttr)) {
3838 options.fields.push(createdAtAttr);
3839 }
3840
3841 if (primaryKeyAttribute && primaryKeyAttribute.defaultValue && !options.fields.includes(primaryKeyName)) {
3842 options.fields.unshift(primaryKeyName);
3843 }
3844 }
3845
3846 if (this.isNewRecord === false) {
3847 if (primaryKeyName && this.get(primaryKeyName, { raw: true }) === undefined) {
3848 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');
3849 }
3850 }
3851
3852 if (updatedAtAttr && !options.silent && options.fields.includes(updatedAtAttr)) {
3853 this.dataValues[updatedAtAttr] = this.constructor._getDefaultTimestamp(updatedAtAttr) || now;
3854 }
3855
3856 if (this.isNewRecord && createdAtAttr && !this.dataValues[createdAtAttr]) {
3857 this.dataValues[createdAtAttr] = this.constructor._getDefaultTimestamp(createdAtAttr) || now;
3858 }
3859
3860 // Validate
3861 if (options.validate) {
3862 await this.validate(options);
3863 }
3864 // Run before hook
3865 if (options.hooks) {
3866 const beforeHookValues = _.pick(this.dataValues, options.fields);
3867 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
3868 let hookChanged;
3869 let afterHookValues;
3870
3871 if (updatedAtAttr && options.fields.includes(updatedAtAttr)) {
3872 ignoreChanged = _.without(ignoreChanged, updatedAtAttr);
3873 }
3874
3875 await this.constructor.runHooks(`before${hook}`, this, options);
3876 if (options.defaultFields && !this.isNewRecord) {
3877 afterHookValues = _.pick(this.dataValues, _.difference(this.changed(), ignoreChanged));
3878
3879 hookChanged = [];
3880 for (const key of Object.keys(afterHookValues)) {
3881 if (afterHookValues[key] !== beforeHookValues[key]) {
3882 hookChanged.push(key);
3883 }
3884 }
3885
3886 options.fields = _.uniq(options.fields.concat(hookChanged));
3887 }
3888
3889 if (hookChanged) {
3890 if (options.validate) {
3891 // Validate again
3892
3893 options.skip = _.difference(Object.keys(this.constructor.rawAttributes), hookChanged);
3894 await this.validate(options);
3895 delete options.skip;
3896 }
3897 }
3898 }
3899 if (options.fields.length && this.isNewRecord && this._options.include && this._options.include.length) {
3900 await Promise.all(this._options.include.filter(include => include.association instanceof BelongsTo).map(async include => {
3901 const instance = this.get(include.as);
3902 if (!instance) return;
3903
3904 const includeOptions = _(Utils.cloneDeep(include))
3905 .omit(['association'])
3906 .defaults({
3907 transaction: options.transaction,
3908 logging: options.logging,
3909 parentRecord: this
3910 }).value();
3911
3912 await instance.save(includeOptions);
3913
3914 await this[include.association.accessors.set](instance, { save: false, logging: options.logging });
3915 }));
3916 }
3917 const realFields = options.fields.filter(field => !this.constructor._virtualAttributes.has(field));
3918 if (!realFields.length) return this;
3919 if (!this.changed() && !this.isNewRecord) return this;
3920
3921 const versionFieldName = _.get(this.constructor.rawAttributes[versionAttr], 'field') || versionAttr;
3922 const values = Utils.mapValueFieldNames(this.dataValues, options.fields, this.constructor);
3923 let query = null;
3924 let args = [];
3925 let where;
3926
3927 if (this.isNewRecord) {
3928 query = 'insert';
3929 args = [this, this.constructor.getTableName(options), values, options];
3930 } else {
3931 where = this.where(true);
3932 if (versionAttr) {
3933 values[versionFieldName] = parseInt(values[versionFieldName], 10) + 1;
3934 }
3935 query = 'update';
3936 args = [this, this.constructor.getTableName(options), values, where, options];
3937 }
3938
3939 const [result, rowsUpdated] = await this.constructor.queryInterface[query](...args);
3940 if (versionAttr) {
3941 // Check to see that a row was updated, otherwise it's an optimistic locking error.
3942 if (rowsUpdated < 1) {
3943 throw new sequelizeErrors.OptimisticLockError({
3944 modelName: this.constructor.name,
3945 values,
3946 where
3947 });
3948 } else {
3949 result.dataValues[versionAttr] = values[versionFieldName];
3950 }
3951 }
3952
3953 // Transfer database generated values (defaults, autoincrement, etc)
3954 for (const attr of Object.keys(this.constructor.rawAttributes)) {
3955 if (this.constructor.rawAttributes[attr].field &&
3956 values[this.constructor.rawAttributes[attr].field] !== undefined &&
3957 this.constructor.rawAttributes[attr].field !== attr
3958 ) {
3959 values[attr] = values[this.constructor.rawAttributes[attr].field];
3960 delete values[this.constructor.rawAttributes[attr].field];
3961 }
3962 }
3963 Object.assign(values, result.dataValues);
3964
3965 Object.assign(result.dataValues, values);
3966 if (wasNewRecord && this._options.include && this._options.include.length) {
3967 await Promise.all(
3968 this._options.include.filter(include => !(include.association instanceof BelongsTo ||
3969 include.parent && include.parent.association instanceof BelongsToMany)).map(async include => {
3970 let instances = this.get(include.as);
3971
3972 if (!instances) return;
3973 if (!Array.isArray(instances)) instances = [instances];
3974
3975 const includeOptions = _(Utils.cloneDeep(include))
3976 .omit(['association'])
3977 .defaults({
3978 transaction: options.transaction,
3979 logging: options.logging,
3980 parentRecord: this
3981 }).value();
3982
3983 // Instances will be updated in place so we can safely treat HasOne like a HasMany
3984 await Promise.all(instances.map(async instance => {
3985 if (include.association instanceof BelongsToMany) {
3986 await instance.save(includeOptions);
3987 const values0 = {
3988 [include.association.foreignKey]: this.get(this.constructor.primaryKeyAttribute, { raw: true }),
3989 [include.association.otherKey]: instance.get(instance.constructor.primaryKeyAttribute, { raw: true }),
3990 // Include values defined in the association
3991 ...include.association.through.scope
3992 };
3993
3994 if (instance[include.association.through.model.name]) {
3995 for (const attr of Object.keys(include.association.through.model.rawAttributes)) {
3996 if (include.association.through.model.rawAttributes[attr]._autoGenerated ||
3997 attr === include.association.foreignKey ||
3998 attr === include.association.otherKey ||
3999 typeof instance[include.association.through.model.name][attr] === undefined) {
4000 continue;
4001 }
4002 values0[attr] = instance[include.association.through.model.name][attr];
4003 }
4004 }
4005
4006 await include.association.throughModel.create(values0, includeOptions);
4007 } else {
4008 instance.set(include.association.foreignKey, this.get(include.association.sourceKey || this.constructor.primaryKeyAttribute, { raw: true }), { raw: true });
4009 Object.assign(instance, include.association.scope);
4010 await instance.save(includeOptions);
4011 }
4012 }));
4013 })
4014 );
4015 }
4016 // Run after hook
4017 if (options.hooks) {
4018 await this.constructor.runHooks(`after${hook}`, result, options);
4019 }
4020 for (const field of options.fields) {
4021 result._previousDataValues[field] = result.dataValues[field];
4022 this.changed(field, false);
4023 }
4024 this.isNewRecord = false;
4025
4026 return result;
4027 }
4028
4029 /**
4030 * Refresh the current instance in-place, i.e. update the object with current data from the DB and return the same object.
4031 * This is different from doing a `find(Instance.id)`, because that would create and return a new instance. With this method,
4032 * all references to the Instance are updated with the new data and no new objects are created.
4033 *
4034 * @see
4035 * {@link Model.findAll}
4036 *
4037 * @param {object} [options] Options that are passed on to `Model.find`
4038 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4039 *
4040 * @returns {Promise<Model>}
4041 */
4042 async reload(options) {
4043 options = Utils.defaults({
4044 where: this.where()
4045 }, options, {
4046 include: this._options.include || undefined
4047 });
4048
4049 const reloaded = await this.constructor.findOne(options);
4050 if (!reloaded) {
4051 throw new sequelizeErrors.InstanceError(
4052 'Instance could not be reloaded because it does not exist anymore (find call returned null)'
4053 );
4054 }
4055 // update the internal options of the instance
4056 this._options = reloaded._options;
4057 // re-set instance values
4058 this.set(reloaded.dataValues, {
4059 raw: true,
4060 reset: true && !options.attributes
4061 });
4062
4063 return this;
4064 }
4065
4066 /**
4067 * Validate the attributes of this instance according to validation rules set in the model definition.
4068 *
4069 * The promise fulfills if and only if validation successful; otherwise it rejects an Error instance containing { field name : [error msgs] } entries.
4070 *
4071 * @param {object} [options] Options that are passed to the validator
4072 * @param {Array} [options.skip] An array of strings. All properties that are in this array will not be validated
4073 * @param {Array} [options.fields] An array of strings. Only the properties that are in this array will be validated
4074 * @param {boolean} [options.hooks=true] Run before and after validate hooks
4075 *
4076 * @returns {Promise}
4077 */
4078 async validate(options) {
4079 return new InstanceValidator(this, options).validate();
4080 }
4081
4082 /**
4083 * This is the same as calling `set` and then calling `save` but it only saves the
4084 * exact values passed to it, making it more atomic and safer.
4085 *
4086 * @see
4087 * {@link Model#set}
4088 * @see
4089 * {@link Model#save}
4090 *
4091 * @param {object} values See `set`
4092 * @param {object} options See `save`
4093 *
4094 * @returns {Promise<Model>}
4095 */
4096 async update(values, options) {
4097 // Clone values so it doesn't get modified for caller scope and ignore undefined values
4098 values = _.omitBy(values, value => value === undefined);
4099
4100 const changedBefore = this.changed() || [];
4101
4102 options = options || {};
4103 if (Array.isArray(options)) options = { fields: options };
4104
4105 options = Utils.cloneDeep(options);
4106 const setOptions = Utils.cloneDeep(options);
4107 setOptions.attributes = options.fields;
4108 this.set(values, setOptions);
4109
4110 // Now we need to figure out which fields were actually affected by the setter.
4111 const sideEffects = _.without(this.changed(), ...changedBefore);
4112 const fields = _.union(Object.keys(values), sideEffects);
4113
4114 if (!options.fields) {
4115 options.fields = _.intersection(fields, this.changed());
4116 options.defaultFields = options.fields;
4117 }
4118
4119 return await this.save(options);
4120 }
4121
4122 /**
4123 * 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.
4124 *
4125 * @param {object} [options={}] destroy options
4126 * @param {boolean} [options.force=false] If set to true, paranoid models will actually be deleted
4127 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4128 * @param {Transaction} [options.transaction] Transaction to run query under
4129 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
4130 *
4131 * @returns {Promise}
4132 */
4133 async destroy(options) {
4134 options = {
4135 hooks: true,
4136 force: false,
4137 ...options
4138 };
4139
4140 // Run before hook
4141 if (options.hooks) {
4142 await this.constructor.runHooks('beforeDestroy', this, options);
4143 }
4144 const where = this.where(true);
4145
4146 let result;
4147 if (this.constructor._timestampAttributes.deletedAt && options.force === false) {
4148 const attributeName = this.constructor._timestampAttributes.deletedAt;
4149 const attribute = this.constructor.rawAttributes[attributeName];
4150 const defaultValue = Object.prototype.hasOwnProperty.call(attribute, 'defaultValue')
4151 ? attribute.defaultValue
4152 : null;
4153 const currentValue = this.getDataValue(attributeName);
4154 const undefinedOrNull = currentValue == null && defaultValue == null;
4155 if (undefinedOrNull || _.isEqual(currentValue, defaultValue)) {
4156 // only update timestamp if it wasn't already set
4157 this.setDataValue(attributeName, new Date());
4158 }
4159
4160 result = await this.save({ ...options, hooks: false });
4161 } else {
4162 result = await this.constructor.queryInterface.delete(this, this.constructor.getTableName(options), where, { type: QueryTypes.DELETE, limit: null, ...options });
4163 }
4164 // Run after hook
4165 if (options.hooks) {
4166 await this.constructor.runHooks('afterDestroy', this, options);
4167 }
4168 return result;
4169 }
4170
4171 /**
4172 * Helper method to determine if a instance is "soft deleted". This is
4173 * particularly useful if the implementer renamed the `deletedAt` attribute
4174 * to something different. This method requires `paranoid` to be enabled.
4175 *
4176 * @returns {boolean}
4177 */
4178 isSoftDeleted() {
4179 if (!this.constructor._timestampAttributes.deletedAt) {
4180 throw new Error('Model is not paranoid');
4181 }
4182
4183 const deletedAtAttribute = this.constructor.rawAttributes[this.constructor._timestampAttributes.deletedAt];
4184 const defaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
4185 const deletedAt = this.get(this.constructor._timestampAttributes.deletedAt) || null;
4186 const isSet = deletedAt !== defaultValue;
4187
4188 return isSet;
4189 }
4190
4191 /**
4192 * Restore the row corresponding to this instance. Only available for paranoid models.
4193 *
4194 * @param {object} [options={}] restore options
4195 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4196 * @param {Transaction} [options.transaction] Transaction to run query under
4197 *
4198 * @returns {Promise}
4199 */
4200 async restore(options) {
4201 if (!this.constructor._timestampAttributes.deletedAt) throw new Error('Model is not paranoid');
4202
4203 options = {
4204 hooks: true,
4205 force: false,
4206 ...options
4207 };
4208
4209 // Run before hook
4210 if (options.hooks) {
4211 await this.constructor.runHooks('beforeRestore', this, options);
4212 }
4213 const deletedAtCol = this.constructor._timestampAttributes.deletedAt;
4214 const deletedAtAttribute = this.constructor.rawAttributes[deletedAtCol];
4215 const deletedAtDefaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null;
4216
4217 this.setDataValue(deletedAtCol, deletedAtDefaultValue);
4218 const result = await this.save({ ...options, hooks: false, omitNull: false });
4219 // Run after hook
4220 if (options.hooks) {
4221 await this.constructor.runHooks('afterRestore', this, options);
4222 return result;
4223 }
4224 return result;
4225 }
4226
4227 /**
4228 * 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
4229 * ```sql
4230 * SET column = column + X
4231 * ```
4232 * 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.
4233 *
4234 * @example
4235 * instance.increment('number') // increment number by 1
4236 *
4237 * instance.increment(['number', 'count'], { by: 2 }) // increment number and count by 2
4238 *
4239 * // increment answer by 42, and tries by 1.
4240 * // `by` is ignored, since each column has its own value
4241 * instance.increment({ answer: 42, tries: 1}, { by: 2 })
4242 *
4243 * @see
4244 * {@link Model#reload}
4245 *
4246 * @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.
4247 * @param {object} [options] options
4248 * @param {number} [options.by=1] The number to increment by
4249 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
4250 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4251 * @param {Transaction} [options.transaction] Transaction to run query under
4252 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
4253 * @param {boolean} [options.returning=true] Append RETURNING * to get back auto generated values (Postgres only)
4254 *
4255 * @returns {Promise<Model>}
4256 * @since 4.0.0
4257 */
4258 async increment(fields, options) {
4259 const identifier = this.where();
4260
4261 options = Utils.cloneDeep(options);
4262 options.where = { ...options.where, ...identifier };
4263 options.instance = this;
4264
4265 await this.constructor.increment(fields, options);
4266
4267 return this;
4268 }
4269
4270 /**
4271 * 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
4272 * ```sql
4273 * SET column = column - X
4274 * ```
4275 * 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.
4276 *
4277 * @example
4278 * instance.decrement('number') // decrement number by 1
4279 *
4280 * instance.decrement(['number', 'count'], { by: 2 }) // decrement number and count by 2
4281 *
4282 * // decrement answer by 42, and tries by 1.
4283 * // `by` is ignored, since each column has its own value
4284 * instance.decrement({ answer: 42, tries: 1}, { by: 2 })
4285 *
4286 * @see
4287 * {@link Model#reload}
4288 * @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
4289 * @param {object} [options] decrement options
4290 * @param {number} [options.by=1] The number to decrement by
4291 * @param {boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
4292 * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
4293 * @param {Transaction} [options.transaction] Transaction to run query under
4294 * @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
4295 * @param {boolean} [options.returning=true] Append RETURNING * to get back auto generated values (Postgres only)
4296 *
4297 * @returns {Promise}
4298 */
4299 async decrement(fields, options) {
4300 return this.increment(fields, {
4301 by: 1,
4302 ...options,
4303 increment: false
4304 });
4305 }
4306
4307 /**
4308 * Check whether this and `other` Instance refer to the same row
4309 *
4310 * @param {Model} other Other instance to compare against
4311 *
4312 * @returns {boolean}
4313 */
4314 equals(other) {
4315 if (!other || !other.constructor) {
4316 return false;
4317 }
4318
4319 if (!(other instanceof this.constructor)) {
4320 return false;
4321 }
4322
4323 return this.constructor.primaryKeyAttributes.every(attribute => this.get(attribute, { raw: true }) === other.get(attribute, { raw: true }));
4324 }
4325
4326 /**
4327 * Check if this is equal to one of `others` by calling equals
4328 *
4329 * @param {Array<Model>} others An array of instances to check against
4330 *
4331 * @returns {boolean}
4332 */
4333 equalsOneOf(others) {
4334 return others.some(other => this.equals(other));
4335 }
4336
4337 setValidators(attribute, validators) {
4338 this.validators[attribute] = validators;
4339 }
4340
4341 /**
4342 * Convert the instance to a JSON representation.
4343 * Proxies to calling `get` with no keys.
4344 * This means get all values gotten from the DB, and apply all custom getters.
4345 *
4346 * @see
4347 * {@link Model#get}
4348 *
4349 * @returns {object}
4350 */
4351 toJSON() {
4352 return _.cloneDeep(
4353 this.get({
4354 plain: true
4355 })
4356 );
4357 }
4358
4359 /**
4360 * Creates a 1:m association between this (the source) and the provided target.
4361 * The foreign key is added on the target.
4362 *
4363 * @param {Model} target Target model
4364 * @param {object} [options] hasMany association options
4365 * @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
4366 * @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
4367 * @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
4368 * @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
4369 * @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)
4370 * @param {string} [options.onDelete='SET&nbsp;NULL|CASCADE'] SET NULL if foreignKey allows nulls, CASCADE if otherwise
4371 * @param {string} [options.onUpdate='CASCADE'] Set `ON UPDATE`
4372 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4373 *
4374 * @returns {HasMany}
4375 *
4376 * @example
4377 * User.hasMany(Profile) // This will add userId to the profile table
4378 */
4379 static hasMany(target, options) {} // eslint-disable-line
4380
4381 /**
4382 * Create an N:M association with a join table. Defining `through` is required.
4383 *
4384 * @param {Model} target Target model
4385 * @param {object} options belongsToMany association options
4386 * @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
4387 * @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.
4388 * @param {Model} [options.through.model] The model used to join both sides of the N:M association.
4389 * @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)
4390 * @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)
4391 * @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
4392 * @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
4393 * @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
4394 * @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)
4395 * @param {boolean} [options.timestamps=sequelize.options.timestamps] Should the join model have timestamps
4396 * @param {string} [options.onDelete='SET&nbsp;NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m
4397 * @param {string} [options.onUpdate='CASCADE'] Sets `ON UPDATE`
4398 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4399 *
4400 * @returns {BelongsToMany}
4401 *
4402 * @example
4403 * // Automagically generated join model
4404 * User.belongsToMany(Project, { through: 'UserProjects' })
4405 * Project.belongsToMany(User, { through: 'UserProjects' })
4406 *
4407 * // Join model with additional attributes
4408 * const UserProjects = sequelize.define('UserProjects', {
4409 * started: Sequelize.BOOLEAN
4410 * })
4411 * User.belongsToMany(Project, { through: UserProjects })
4412 * Project.belongsToMany(User, { through: UserProjects })
4413 */
4414 static belongsToMany(target, options) {} // eslint-disable-line
4415
4416 /**
4417 * Creates an association between this (the source) and the provided target. The foreign key is added on the target.
4418 *
4419 * @param {Model} target Target model
4420 * @param {object} [options] hasOne association options
4421 * @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
4422 * @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
4423 * @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
4424 * @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
4425 * @param {string} [options.onDelete='SET&nbsp;NULL|CASCADE'] SET NULL if foreignKey allows nulls, CASCADE if otherwise
4426 * @param {string} [options.onUpdate='CASCADE'] Sets 'ON UPDATE'
4427 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4428 * @param {string} [options.uniqueKey] The custom name for unique constraint.
4429 *
4430 * @returns {HasOne}
4431 *
4432 * @example
4433 * User.hasOne(Profile) // This will add userId to the profile table
4434 */
4435 static hasOne(target, options) {} // eslint-disable-line
4436
4437 /**
4438 * Creates an association between this (the source) and the provided target. The foreign key is added on the source.
4439 *
4440 * @param {Model} target The target model
4441 * @param {object} [options] belongsTo association options
4442 * @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
4443 * @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
4444 * @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
4445 * @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
4446 * @param {string} [options.onDelete='SET&nbsp;NULL|NO&nbsp;ACTION'] SET NULL if foreignKey allows nulls, NO ACTION if otherwise
4447 * @param {string} [options.onUpdate='CASCADE'] Sets 'ON UPDATE'
4448 * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
4449 *
4450 * @returns {BelongsTo}
4451 *
4452 * @example
4453 * Profile.belongsTo(User) // This will add userId to the profile table
4454 */
4455 static belongsTo(target, options) {} // eslint-disable-line
4456}
4457
4458Object.assign(Model, associationsMixin);
4459Hooks.applyTo(Model, true);
4460
4461module.exports = Model;