不要怂,就是干,撸起袖子干!

Commit 8fe367e9 by Pedro Augusto de Paula Barbosa Committed by Sushant

docs(manuals): extensive rewrite (#11825)

1 parent f3110e32
Showing with 2793 additions and 822 deletions
...@@ -38,6 +38,10 @@ div.sequelize { ...@@ -38,6 +38,10 @@ div.sequelize {
font-size: 17px; font-size: 17px;
} }
.no-mouse {
pointer-events: none;
}
.api-reference-link { .api-reference-link {
font-weight: bold; font-weight: bold;
padding: 0 20px; padding: 0 20px;
......
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
[![License](https://badgen.net/github/license/sequelize/sequelize)](https://github.com/sequelize/sequelize/blob/master/LICENSE) [![License](https://badgen.net/github/license/sequelize/sequelize)](https://github.com/sequelize/sequelize/blob/master/LICENSE)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
Sequelize is a promise-based Node.js ORM for Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. It features solid transaction support, relations, eager and lazy loading, read replication and more. Sequelize is a promise-based Node.js [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Postgres](https://en.wikipedia.org/wiki/PostgreSQL), [MySQL](https://en.wikipedia.org/wiki/MySQL), [MariaDB](https://en.wikipedia.org/wiki/MariaDB), [SQLite](https://en.wikipedia.org/wiki/SQLite) and [Microsoft SQL Server](https://en.wikipedia.org/wiki/Microsoft_SQL_Server). It features solid transaction support, relations, eager and lazy loading, read replication and more.
Sequelize follows [SEMVER](http://semver.org). Supports Node v10 and above to use ES6 features. Sequelize follows [Semantic Versioning](http://semver.org) and supports Node v10 and above.
You are currently looking at the **Tutorials and Guides** for Sequelize. You might also be interested in the [API Reference](identifiers). You are currently looking at the **Tutorials and Guides** for Sequelize. You might also be interested in the [API Reference](identifiers.html).
## Quick example ## Quick example
...@@ -36,14 +36,14 @@ User.init({ ...@@ -36,14 +36,14 @@ User.init({
birthday: DataTypes.DATE birthday: DataTypes.DATE
}, { sequelize, modelName: 'user' }); }, { sequelize, modelName: 'user' });
sequelize.sync() (async () => {
.then(() => User.create({ await sequelize.sync();
const jane = await User.create({
username: 'janedoe', username: 'janedoe',
birthday: new Date(1980, 6, 20) birthday: new Date(1980, 6, 20)
}))
.then(jane => {
console.log(jane.toJSON());
}); });
console.log(jane.toJSON());
})();
``` ```
To learn more about how to use Sequelize, read the tutorials available in the left menu. Begin with [Getting Started](manual/getting-started). To learn more about how to use Sequelize, read the tutorials available in the left menu. Begin with [Getting Started](manual/getting-started.html).
{ {
"Core Concepts": [ "Core Concepts": [
"getting-started.md", "core-concepts/getting-started.md",
"dialects.md", "core-concepts/model-basics.md",
"data-types.md", "core-concepts/model-instances.md",
"models-definition.md", "core-concepts/model-querying-basics.md",
"models-usage.md", "core-concepts/model-querying-finders.md",
"hooks.md", "core-concepts/getters-setters-virtuals.md",
"querying.md", "core-concepts/validations-and-constraints.md",
"instances.md", "core-concepts/raw-queries.md",
"associations.md", "core-concepts/assocs.md",
"raw-queries.md" "core-concepts/paranoid.md"
],
"Advanced Association Concepts": [
"advanced-association-concepts/eager-loading.md",
"advanced-association-concepts/creating-with-associations.md",
"advanced-association-concepts/advanced-many-to-many.md",
"advanced-association-concepts/association-scopes.md",
"advanced-association-concepts/polymorphic-associations.md"
], ],
"Other Topics": [ "Other Topics": [
"transactions.md", "other-topics/dialect-specific-things.md",
"scopes.md", "other-topics/transactions.md",
"read-replication.md", "other-topics/hooks.md",
"migrations.md", "other-topics/query-interface.md",
"resources.md", "other-topics/naming-strategies.md",
"typescript.md", "other-topics/scopes.md",
"upgrade-to-v6.md", "other-topics/sub-queries.md",
"legacy.md", "other-topics/other-data-types.md",
"whos-using.md", "other-topics/constraints-and-circularities.md",
"legal.md" "other-topics/extending-data-types.md",
"other-topics/indexes.md",
"other-topics/optimistic-locking.md",
"other-topics/read-replication.md",
"other-topics/connection-pool.md",
"other-topics/legacy.md",
"other-topics/migrations.md",
"other-topics/typescript.md",
"other-topics/resources.md",
"other-topics/upgrade-to-v6.md",
"other-topics/whos-using.md",
"other-topics/legal.md"
],
"__hidden__": [
"moved/associations.md",
"moved/data-types.md",
"moved/models-usage.md",
"moved/querying.md"
] ]
} }
\ No newline at end of file
# Association Scopes
This section concerns association scopes, which are similar but not the same as [model scopes](scopes.html).
Association scopes can be placed both on the associated model (the target of the association) and on the through table for Many-to-Many relationships.
## Concept
Similarly to how a [model scope](scopes.html) is automatically applied on the model static calls, such as `Model.scope('foo').findAll()`, an association scope is a rule (more precisely, a set of default attributes and options) that is automatically applied on instance calls from the model. Here, *instance calls* mean method calls that are called from an instance (rather than from the Model itself). Mixins are the main example of instance methods (`instance.getSomething`, `instance.setSomething`, `instance.addSomething` and `instance.createSomething`).
Association scopes behave just like model scopes, in the sense that both cause an automatic application of things like `where` clauses to finder calls; the difference being that instead of applying to static finder calls (which is the case for model scopes), the association scopes automatically apply to instance finder calls (such as mixins).
## Example
A basic example of an association scope for the One-to-Many association between models `Foo` and `Bar` is shown below.
* Setup:
```js
const Foo = sequelize.define('foo', { name: DataTypes.STRING });
const Bar = sequelize.define('bar', { status: DataTypes.STRING });
Foo.hasMany(Bar, {
scope: {
status: 'open'
},
as: 'openBars'
});
await sequelize.sync();
const myFoo = await Foo.create({ name: "My Foo" });
```
* After this setup, calling `myFoo.getOpenBars()` generates the following SQL:
```sql
SELECT
`id`, `status`, `createdAt`, `updatedAt`, `fooId`
FROM `bars` AS `bar`
WHERE `bar`.`status` = 'open' AND `bar`.`fooId` = 1;
```
With this we can see that upon calling the `.getOpenBars()` mixin, the association scope `{ status: 'open' }` was automatically applied into the `WHERE` clause of the generated SQL.
## Achieving the same behavior with standard scopes
We could have achieved the same behavior with standard scopes:
```js
// Foo.hasMany(Bar, {
// scope: {
// status: 'open'
// },
// as: 'openBars'
// });
Bar.addScope('open', {
where: {
status: 'open'
}
});
Foo.hasMany(Bar);
Foo.hasMany(Bar.scope('testTitleScope'), { as: 'openBars' });
```
With the above code, `myFoo.getOpenBars()` yields the same SQL shown above.
\ No newline at end of file
# Creating with Associations
An instance can be created with nested association in one step, provided all elements are new.
In contrast, performing updates and deletions involving nested objects is currently not possible. For that, you will have to perform each separate action explicitly.
## BelongsTo / HasMany / HasOne association
Consider the following models:
```js
class Product extends Model {}
Product.init({
title: Sequelize.STRING
}, { sequelize, modelName: 'product' });
class User extends Model {}
User.init({
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
}, { sequelize, modelName: 'user' });
class Address extends Model {}
Address.init({
type: DataTypes.STRING,
line1: Sequelize.STRING,
line2: Sequelize.STRING,
city: Sequelize.STRING,
state: Sequelize.STRING,
zip: Sequelize.STRING,
}, { sequelize, modelName: 'address' });
// We save the return values of the association setup calls to use them later
Product.User = Product.belongsTo(User);
User.Addresses = User.hasMany(Address);
// Also works for `hasOne`
```
A new `Product`, `User`, and one or more `Address` can be created in one step in the following way:
```js
return Product.create({
title: 'Chair',
user: {
firstName: 'Mick',
lastName: 'Broadstone',
addresses: [{
type: 'home',
line1: '100 Main St.',
city: 'Austin',
state: 'TX',
zip: '78704'
}]
}
}, {
include: [{
association: Product.User,
include: [ User.Addresses ]
}]
});
```
Observe the usage of the `include` option in the `Product.create` call. That is necessary for Sequelize to understand what you are trying to create along with the association.
Note: here, our user model is called `user`, with a lowercase `u` - This means that the property in the object should also be `user`. If the name given to `sequelize.define` was `User`, the key in the object should also be `User`. Likewise for `addresses`, except it's pluralized being a `hasMany` association.
## BelongsTo association with an alias
The previous example can be extended to support an association alias.
```js
const Creator = Product.belongsTo(User, { as: 'creator' });
return Product.create({
title: 'Chair',
creator: {
firstName: 'Matt',
lastName: 'Hansen'
}
}, {
include: [ Creator ]
});
```
## HasMany / BelongsToMany association
Let's introduce the ability to associate a product with many tags. Setting up the models could look like:
```js
class Tag extends Model {}
Tag.init({
name: Sequelize.STRING
}, { sequelize, modelName: 'tag' });
Product.hasMany(Tag);
// Also works for `belongsToMany`.
```
Now we can create a product with multiple tags in the following way:
```js
Product.create({
id: 1,
title: 'Chair',
tags: [
{ name: 'Alpha'},
{ name: 'Beta'}
]
}, {
include: [ Tag ]
})
```
And, we can modify this example to support an alias as well:
```js
const Categories = Product.hasMany(Tag, { as: 'categories' });
Product.create({
id: 1,
title: 'Chair',
categories: [
{ id: 1, name: 'Alpha' },
{ id: 2, name: 'Beta' }
]
}, {
include: [{
association: Categories,
as: 'categories'
}]
})
```
\ No newline at end of file
# Getters, Setters & Virtuals
Sequelize allows you to define custom getters and setters for the attributes of your models.
Sequelize also allows you to specify the so-called *virtual attributes*, which are attributes on the Sequelize Model that doesn't really exist in the underlying SQL table, but instead are populated automatically by Sequelize. They are very useful for simplifying code, for example.
## Getters
A getter is a `get()` function defined for one column in the model definition:
```js
const User = sequelize.define('user', {
// Let's say we wanted to see every username in uppercase, even
// though they are not necessarily uppercase in the database itself
username: {
type: DataTypes.STRING,
get() {
const rawValue = this.getDataValue(username);
return rawValue ? rawValue.toUpperCase() : null;
}
}
});
```
This getter, just like a standard JavaScript getter, is called automatically when the field value is read:
```js
const user = User.build({ username: 'SuperUser123' });
console.log(user.username); // 'SUPERUSER123'
console.log(user.getDataValue(username)); // 'SuperUser123'
```
Note that, although `SUPERUSER123` was logged above, the value truly stored in the database is still `SuperUser123`. We used `this.getDataValue(username)` to obtain this value, and converted it to uppercase.
Had we tried to use `this.username` in the getter instead, we would have gotten an infinite loop! This is why Sequelize provides the `getDataValue` method.
## Setters
A setter is a `set()` function defined for one column in the model definition. It receives the value being set:
```js
const User = sequelize.define('user', {
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value) {
// Storing passwords in plaintext in the database is terrible.
// Hashing the value with an appropriate cryptographic hash function is better.
this.setDataValue('password', hash(value));
}
}
});
```
```js
const user = User.build({ username: 'someone', password: 'NotSo§tr0ngP4$SW0RD!' });
console.log(user.password); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
console.log(user.getDataValue(password)); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
```
Observe that Sequelize called the setter automatically, before even sending data to the database. The only data the database ever saw was the already hashed value.
If we wanted to involve another field from our model instance in the computation, that is possible and very easy!
```js
const User = sequelize.define('user', {
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value) {
// Storing passwords in plaintext in the database is terrible.
// Hashing the value with an appropriate cryptographic hash function is better.
// Using the username as a salt is better.
this.setDataValue('password', hash(this.username + value));
}
}
});
```
**Note:** The above examples involving password handling, although much better than simply storing the password in plaintext, are far from perfect security. Handling passwords properly is hard, everything here is just for the sake of an example to show Sequelize functionality. We suggest involving a cybersecurity expert and/or reading [OWASP](https://www.owasp.org/) documents and/or visiting the [InfoSec StackExchange](https://security.stackexchange.com/).
## Combining getters and setters
Getters and setters can be both defined in the same field.
For the sake of an example, let's say we are modeling a `Post`, whose `content` is a text of unlimited length. To improve memory usage, let's say we want to store a gzipped version of the content.
*Note: modern databases should do some compression automatically in these cases. Please note that this is just for the sake of an example.*
```js
const { gzipSync, gunzipSync } = require('zlib');
const Post = sequelize.define('post', {
content: {
type: DataTypes.TEXT,
get() {
const storedValue = this.getDataValue('content');
const gzippedBuffer = Buffer.from(storedValue, 'base64');
const unzippedBuffer = gunzipSync(gzippedBuffer);
return unzippedBuffer.toString();
},
set(value) {
const gzippedBuffer = gzipSync(value);
this.setDataValue('content', gzippedBuffer.toString('base64'));
}
}
});
```
With the above setup, whenever we try to interact with the `content` field of our `Post` model, Sequelize will automatically handle the custom getter and setter. For example:
```js
const post = await Post.create({ content: 'Hello everyone!' });
console.log(post.content); // 'Hello everyone!'
// Everything is happening under the hood, so we can even forget that the
// content is actually being stored as a gzipped base64 string!
// However, if we are really curious, we can get the 'raw' data...
console.log(post.getDataValue('content'));
// Output: 'H4sIAAAAAAAACvNIzcnJV0gtSy2qzM9LVQQAUuk9jQ8AAAA='
```
## Virtual fields
Virtual fields are fields that Sequelize populates under the hood, but in reality they don't even exist in the database.
For example, let's say we have the `firstName` and `lastName` attributes for a User.
*Again, this is [only for the sake of an example](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/).*
It would be nice to have a simple way to obtain the *full name* directly! We can combine the idea of `getters` with the special data type Sequelize provides for this kind of situation: `DataTypes.VIRTUAL`:
```js
const { DataTypes } = require("sequelize");
const User = sequelize.define('user', {
firstName: DataTypes.TEXT,
lastName: DataTypes.TEXT,
fullName: {
type: DataTypes.VIRTUAL,
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
throw new Error('Do not try to set the `fullName` value!');
}
}
});
```
The `VIRTUAL` field does not cause a column in the table to exist. In other words, the model above will not have a `fullName` column. However, it will appear to have it!
```js
const user = await User.create({ firstName: 'John', lastName: 'Doe' });
console.log(user.fullName); // 'John Doe'
```
## `getterMethods` and `setterMethods`
Sequelize also provides the `getterMethods` and `setterMethods` options in the model definition to specify things that look like, but aren't exactly the same as, virtual attributes. This usage is discouraged and likely to be deprecated in the future (in favor of using virtual attributes directly).
Example:
```js
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
const User = sequelize.define('user', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING
}, {
getterMethods: {
fullName() {
return this.firstName + ' ' + this.lastName;
}
},
setterMethods: {
fullName(value) {
// Note: this is just for demonstration.
// See: https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
const names = value.split(' ');
const firstName = names[0];
const lastName = names.slice(1).join(' ');
this.setDataValue('firstName', firstName);
this.setDataValue('lastName', lastName);
}
}
});
(async () => {
await sequelize.sync();
let user = await User.create({ firstName: 'John', lastName: 'Doe' });
console.log(user.fullName); // 'John Doe'
user.fullName = 'Someone Else';
await user.save();
user = await User.findOne();
console.log(user.firstName); // 'Someone'
console.log(user.lastName); // 'Else'
})();
```
\ No newline at end of file
# Getting Started
In this tutorial you will learn to make a simple setup of Sequelize.
## Installing
Sequelize is available via [npm](https://www.npmjs.com/package/sequelize) (or [yarn](https://yarnpkg.com/package/sequelize)).
```sh
npm install --save sequelize
```
You'll also have to manually install the driver for your database of choice:
```sh
# One of the following:
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server
```
## Connecting to a database
To connect to the database, you must create a Sequelize instance. This can be done by either passing the connection parameters separately to the Sequelize constructor or by passing a single connection URI:
```js
const { Sequelize } = require('sequelize');
// Option 1: Passing a connection URI
const sequelize = new Sequelize('sqlite::memory:') // Example for sqlite
const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname') // Example for postgres
// Option 2: Passing parameters separately (sqlite)
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'path/to/database.sqlite'
});
// Option 2: Passing parameters separately (other dialects)
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
});
```
The Sequelize constructor accepts a lot of options. They are documented in the [API Reference](../class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor).
### Testing the connection
You can use the `.authenticate()` function to test if the connection is OK:
```js
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
```
### Closing the connection
Sequelize will keep the connection open by default, and use the same connection for all queries. If you need to close the connection, call `sequelize.close()` (which is asynchronous and returns a Promise).
## Terminology convention
Observe that, in the examples above, `Sequelize` refers to the library itself while `sequelize` refers to an instance of Sequelize, which represents a connection to one database. This is the recommended convention and it will be followed throughout the documentation.
## Tip for reading the docs
You are encouraged to run code examples locally while reading the Sequelize docs. This will help you learn faster. The easiest way to do this is using the SQLite dialect:
```js
const { Sequelize, Op, Model, DataTypes } = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");
// Code here! It works!
```
To experiment with the other dialects, which are harder to setup locally, you can use the [Sequelize SSCCE](https://github.com/papb/sequelize-sscce) GitHub repository, which allows you to run code on all supported dialects directly from GitHub, for free, without any setup!
## New databases versus existing databases
If you are starting a project from scratch, and your database does not exist yet, Sequelize can be used since the beginning in order to automate the creation of every table in your database.
Also, if you want to use Sequelize to connect to a database that is already filled with tables and data, that works as well! Sequelize has got you covered in both cases.
## Logging
By default, Sequelize will log to console every SQL query it performs. The `options.logging` option can be used to customize this behavior, by defining the function that gets executed every time Sequelize would log something. The default value is `console.log` and when using that only the first log parameter of log function call is displayed. For example, for query logging the first parameter is the raw query and the second (hidden by default) is the Sequelize object.
Common useful values for `options.logging`:
```js
const sequelize = new Sequelize('sqlite::memory:', {
// Choose one of the logging options
logging: console.log, // Default, displays the first parameter of the log function call
logging: (...msg) => console.log(msg), // Displays all log function call parameters
logging: false, // Disables logging
logging: msg => logger.debug(msg), // Use custom logger (e.g. Winston or Bunyan), displays the first parameter
logging: logger.debug.bind(logger) // Alternative way to use custom logger, displays all messages
});
```
## Bluebird Promises and async/await
Most of the methods provided by Sequelize are asynchronous and therefore return Promises. They are all [Bluebird](http://bluebirdjs.com) Promises, so you can use the rich Bluebird API (for example, using `finally`, `tap`, `tapCatch`, `map`, `mapSeries`, etc) out of the box. You can access the Bluebird constructor used internally by Sequelize with `Sequelize.Promise`, if you want to set any Bluebird specific options.
Of course, using `async` and `await` works normally as well.
\ No newline at end of file
# Model Instances
As you already know, a model is an [ES6 class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). An instance of the class represents one object from that model (which maps to one row of the table in the database). This way, model instances are [DAOs](https://en.wikipedia.org/wiki/Data_access_object).
For this guide, the following setup will be assumed:
```js
const { Sequelize, Model, DataTypes } = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");
const User = sequelize.define("user", {
name: DataTypes.TEXT,
favoriteColor: {
type: DataTypes.TEXT,
defaultValue: 'green'
},
age: DataTypes.INTEGER,
cash: DataTypes.INTEGER
});
(async () => {
await sequelize.sync({ force: true });
// Code here
})();
```
## Creating an instance
Although a model is a class, you should not create instances by using the `new` operator directly. Instead, the [`build`](../class/lib/model.js~Model.html#static-method-build) method should be used:
```js
const jane = User.build({ name: "Jane" });
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
```
However, the code above does not communicate with the database at all (note that it is not even asynchronous)! This is because the [`build`](../class/lib/model.js~Model.html#static-method-build) method only creates an object that *represents* data that *can* be mapped to a database. In order to really save (i.e. persist) this instance in the database, the [`save`](../class/lib/model.js~Model.html#instance-method-save) method should be used:
```js
await jane.save();
console.log('Jane was saved to the database!');
```
Note, from the usage of `await` in the snippet above, that `save` is an asynchronous method. In fact, almost every Sequelize method is asynchronous; `build` is one of the very few exceptions.
### A very useful shortcut: the `create` method
Sequelize provides the [`create`](../class/lib/model.js~Model.html#static-method-create) method, which combines the `build` and `save` methods shown above into a single method:
```js
const jane = await User.create({ name: "Jane" });
// Jane exists in the database now!
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
```
## Note: logging instances
Trying to log a model instance directly to `console.log` will produce a lot of clutter, since Sequelize instances have a lot of things attached to them. Instead, you can use the `.toJSON()` method (which, by the way, automatically guarantees the instances to be `JSON.stringify`-ed well).
```js
const jane = await User.create({ name: "Jane" });
// console.log(jane); // Don't do this
console.log(jane.toJSON()); // This is good!
console.log(JSON.stringify(jane, null, 4)); // This is also good!
```
## Default values
Built instances will automatically get default values:
```js
const jane = User.build({ name: "Jane" });
console.log(jane.favoriteColor); // "green"
```
## Updating an instance
If you change the value of some field of an instance, calling `save` again will update it accordingly:
```js
const jane = await User.create({ name: "Jane" });
console.log(jane.name); // "Jane"
jane.name = "Ada";
// the name is still "Jane" in the database
await jane.save();
// Now the name was updated to "Ada" in the database!
```
## Deleting an instance
You can delete an instance by calling [`destroy`](../class/lib/model.js~Model.html#instance-method-destroy):
```js
const jane = await User.create({ name: "Jane" });
console.log(jane.name); // "Jane"
await jane.destroy();
// Now this entry was removed from the database
```
## Reloading an instance
You can reload an instance from the database by calling [`reload`](../class/lib/model.js~Model.html#instance-method-reload):
```js
const jane = await User.create({ name: "Jane" });
console.log(jane.name); // "Jane"
jane.name = "Ada";
// the name is still "Jane" in the database
await jane.reload();
console.log(jane.name); // "Jane"
```
The reload call generates a `SELECT` query to get the up-to-date data from the database.
## Saving only some fields
It is possible to define which attributes should be saved when calling `save`, by passing an array of column names.
This is useful when you set attributes based on a previously defined object, for example, when you get the values of an object via a form of a web app. Furthermore, this is used internally in the `update` implementation. This is how it looks like:
```js
const jane = await User.create({ name: "Jane" });
console.log(jane.name); // "Jane"
console.log(jane.favoriteColor); // "green"
jane.name = "Jane II";
jane.favoriteColor = "blue";
await jane.save({ fields: ['name'] });
console.log(jane.name); // "Jane II"
console.log(jane.favoriteColor); // "blue"
// The above printed blue because the local object has it set to blue, but
// in the database it is still "green":
await jane.reload();
console.log(jane.name); // "Jane II"
console.log(jane.favoriteColor); // "green"
```
## Change-awareness of save
The `save` method is optimized internally to only update fields that really changed. This means that if you don't change anything and call `save`, Sequelize will know that the save is superfluous and do nothing, i.e., no query will be generated (it will still return a Promise, but it will resolve immediately).
Also, if only a few attributes have changed when you call `save`, only those fields will be sent in the `UPDATE` query, to improve performance.
## Incrementing and decrementing integer values
In order to increment/decrement values of an instance without running into concurrency issues, Sequelize provides the [`increment`](../class/lib/model.js~Model.html#instance-method-increment) and [`decrement`](../class/lib/model.js~Model.html#instance-method-decrement) instance methods.
```js
const jane = await User.create({ name: "Jane", age: 100 });
const incrementResult = await user.increment('age', { by: 2 });
// Note: to increment by 1 you can omit the `by` option and just do `user.increment('age')`
// In PostgreSQL, `incrementResult` will be the updated user, unless the option
// `{ returning: false }` was set (and then it will be undefined).
// In other dialects, `incrementResult` will be undefined. If you need the updated instance, you will have to call `user.reload()`.
```
You can also increment multiple fields at once:
```js
const jane = await User.create({ name: "Jane", age: 100, cash: 5000 });
await jane.increment({
'age': 2,
'cash': 100
});
// If the values are incremented by the same amount, you can use this other syntax as well:
await jane.increment(['age', 'cash'], { by: 2 });
```
Decrementing works in the exact same way.
\ No newline at end of file
# Model Querying - Finders
Finder methods are the ones that generate `SELECT` queries.
By default, the results of all finder methods are instances of the model class (as opposed to being just plain JavaScript objects). This means that after the database returns the results, Sequelize automatically wraps everything in proper instance objects. In a few cases, when there are too many results, this wrapping can be inefficient. To disable this wrapping and receive a plain response instead, pass `{ raw: true }` as an option to the finder method.
## `findAll`
The `findAll` method is already known from the previous tutorial. It generates a standard `SELECT` query which will retrieve all entries from the table (unless restricted by something like a `where` clause, for example).
## `findByPk`
The `findByPk` method obtains only a single entry from the table, using the provided primary key.
```js
const project = await Project.findByPk(123);
if (project === null) {
console.log('Not found!');
} else {
console.log(project instanceof Project); // true
// Its primary key is 123
}
```
## `findOne`
The `findOne` method obtains the first entry it finds (that fulfills the optional query options, if provided).
```js
const project = await Project.findOne({ where: { title: 'My Title' } });
if (project === null) {
console.log('Not found!');
} else {
console.log(project instanceof Project); // true
console.log(project.title); // 'My Title'
}
```
## `findOrCreate`
The method `findOrCreate` will create an entry in the table unless it can find one fulfilling the query options. In both cases, it will return an instance (either the found instance or the created instance) and a boolean indicating whether that instance was created or already existed.
The `where` option is considered for finding the entry, and the `defaults` option is used to define what must be created in case nothing was found. If the `defaults` do not contain values for every column, Sequelize will take the values given to `where` (if present).
Let's assume we have an empty database with a `User` model which has a `username` and a `job`.
```js
const [user, created] = await User.findOrCreate({
where: { username: 'sdepold' },
defaults: {
job: 'Technical Lead JavaScript'
}
});
console.log(user.username); // 'sdepold'
console.log(user.job); // This may or may not be 'Technical Lead JavaScript'
console.log(created); // The boolean indicating whether this instance was just created
if (created) {
console.log(user.job); // This will certainly be 'Technical Lead JavaScript'
}
```
## `findAndCountAll`
The `findAndCountAll` method is a convenience method that combines `findAll` and `count`. This is useful when dealing with queries related to pagination where you want to retrieve data with a `limit` and `offset` but also need to know the total number of records that match the query.
The `findAndCountAll` method returns an object with two properties:
* `count` - an integer - the total number records matching the query
* `rows` - an array of objects - the obtained records
```js
const { count, rows } = await Project.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
});
console.log(count);
console.log(rows);
```
\ No newline at end of file
# Paranoid
Sequelize supports the concept of *paranoid* tables. A *paranoid* table is one that, when told to delete a record, it will not truly delete it. Instead, a special column called `deletedAt` will have its value set to the timestamp of that deletion request.
This means that paranoid tables perform a *soft-deletion* of records, instead of a *hard-deletion*.
## Defining a model as paranoid
To make a model paranoid, you must pass the `paranoid: true` option to the model definition. Paranoid requires timestamps to work (i.e. it won't work if you also pass `timestamps: false`).
You can also change the default column name (which is `deletedAt`) to something else.
```js
class Post extends Model {}
Post.init({ /* attributes here */ }, {
sequelize,
paranoid: true,
// If you want to give a custom name to the deletedAt column
deletedAt: 'destroyTime'
});
```
## Deleting
When you call the `destroy` method, a soft-deletion will happen:
```js
await Post.destroy({
where: {
id: 1
}
});
// UPDATE "posts" SET "deletedAt"=[timestamp] WHERE "deletedAt" IS NULL AND "id" = 1
```
If you really want a hard-deletion and your model is paranoid, you can force it using the `force: true` option:
```js
await Post.destroy({
where: {
id: 1
},
force: true
});
// DELETE FROM "posts" WHERE "id" = 1
```
The above examples used the static `destroy` method as an example (`Post.destroy`), but everything works in the same way with the instance method:
```js
const post = await Post.create({ title: 'test' });
console.log(post instanceof Post); // true
await post.destroy(); // Would just set the `deletedAt` flag
await post.destroy({ force: true }); // Would really delete the record
```
## Restoring
To restore soft-deleted records, you can use the `restore` method, which comes both in the static version as well as in the instance version:
```js
// Example showing the instance `restore` method
// We create a post, soft-delete it and then restore it back
const post = await Post.create({ title: 'test' });
console.log(post instanceof Post); // true
await post.destroy();
console.log('soft-deleted!');
await post.restore();
console.log('restored!');
// Example showing the static `restore` method.
// Restoring every soft-deleted post with more than 100 likes
await Post.restore({
where: {
likes: {
[Op.gt]: 100
}
}
});
```
## Behavior with other queries
Every query performed by Sequelize will automatically ignore soft-deleted records (except raw queries, of course).
This means that, for example, the `findAll` method will not see the soft-deleted records, fetching only the ones that were not deleted.
Even if you simply call `findByPk` providing the primary key of a soft-deleted record, the result will be `null` as if that record didn't exist.
If you really want to let the query see the soft-deleted records, you can pass the `paranoid: false` option to the query method. For example:
```js
await Post.findByPk(123); // This will return `null` if the record of id 123 is soft-deleted
await Post.findByPk(123, { paranoid: false }); // This will retrieve the record
await Post.findAll({
where: { foo: 'bar' }
}); // This will not retrieve soft-deleted records
await Post.findAll({
where: { foo: 'bar' },
paranoid: false
}); // This will also retrieve soft-deleted records
```
\ No newline at end of file
# Raw Queries
As there are often use cases in which it is just easier to execute raw / already prepared SQL queries, you can use the [`sequelize.query`](../class/lib/sequelize.js~Sequelize.html#instance-method-query) method.
By default the function will return two arguments - a results array, and an object containing metadata (such as amount of affected rows, etc). Note that since this is a raw query, the metadata are dialect specific. Some dialects return the metadata "within" the results object (as properties on an array). However, two arguments will always be returned, but for MSSQL and MySQL it will be two references to the same object.
```js
const [results, metadata] = await sequelize.query("UPDATE users SET y = 42 WHERE x = 12");
// Results will be an empty array and metadata will contain the number of affected rows.
```
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
```js
const { QueryTypes } = require('sequelize');
const users = await sequelize.query("SELECT * FROM `users`", { type: QueryTypes.SELECT });
// We didn't need to destructure the result here - the results were returned directly
```
Several other query types are available. [Peek into the source for details](https://github.com/sequelize/sequelize/blob/master/lib/query-types.js).
A second option is the model. If you pass a model the returned data will be instances of that model.
```js
// Callee is the model definition. This allows you to easily map a query to a predefined model
const projects = await sequelize.query('SELECT * FROM projects', {
model: Projects,
mapToModel: true // pass true here if you have any mapped fields
});
// Each element of `projects` is now an instance of Project
```
See more options in the [query API reference](../class/lib/sequelize.js~Sequelize.html#instance-method-query). Some examples:
```js
const { QueryTypes } = require('sequelize');
await sequelize.query('SELECT 1', {
// A function (or false) for logging your queries
// Will get called for every SQL query that gets sent
// to the server.
logging: console.log,
// If plain is true, then sequelize will only return the first
// record of the result set. In case of false it will return all records.
plain: false,
// Set this to true if you don't have a model definition for your query.
raw: false,
// The type of query you are executing. The query type affects how results are formatted before they are passed back.
type: QueryTypes.SELECT
});
// Note the second argument being null!
// Even if we declared a callee here, the raw: true would
// supersede and return a raw object.
console.log(await sequelize.query('SELECT * FROM projects', { raw: true }));
```
## "Dotted" attributes
If an attribute name of the table contains dots, the resulting objects will be nested. This is due to the usage of [dottie.js](https://github.com/mickhansen/dottie.js/) under the hood. See below:
```js
const rows = await sequelize.query('select 1 as `foo.bar.baz`');
console.log(JSON.stringify(rows, null, 2));
```
```json
[
{
"foo": {
"bar": {
"baz": 1
}
}
}
]
```
## Replacements
Replacements in a query can be done in two different ways, either using named parameters (starting with `:`), or unnamed, represented by a `?`. Replacements are passed in the options object.
* If an array is passed, `?` will be replaced in the order that they appear in the array
* If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice versa, an exception will be thrown.
```js
const { QueryTypes } = require('sequelize');
await sequelize.query(
'SELECT * FROM projects WHERE status = ?',
{
replacements: ['active'],
type: QueryTypes.SELECT
}
);
await sequelize.query(
'SELECT * FROM projects WHERE status = :status',
{
replacements: { status: 'active' },
type: QueryTypes.SELECT
}
);
```
Array replacements will automatically be handled, the following query searches for projects where the status matches an array of values.
```js
const { QueryTypes } = require('sequelize');
await sequelize.query(
'SELECT * FROM projects WHERE status IN(:status)',
{
replacements: { status: ['active', 'inactive'] },
type: QueryTypes.SELECT
}
);
```
To use the wildcard operator `%`, append it to your replacement. The following query matches users with names that start with 'ben'.
```js
const { QueryTypes } = require('sequelize');
await sequelize.query(
'SELECT * FROM users WHERE name LIKE :search_name',
{
replacements: { search_name: 'ben%' },
type: QueryTypes.SELECT
}
);
```
## Bind Parameter
Bind parameters are like replacements. Except replacements are escaped and inserted into the query by sequelize before the query is sent to the database, while bind parameters are sent to the database outside the SQL query text. A query can have either bind parameters or replacements. Bind parameters are referred to by either $1, $2, ... (numeric) or $key (alpha-numeric). This is independent of the dialect.
* If an array is passed, `$1` is bound to the 1st element in the array (`bind[0]`)
* If an object is passed, `$key` is bound to `object['key']`. Each key must begin with a non-numeric char. `$1` is not a valid key, even if `object['1']` exists.
* In either case `$$` can be used to escape a literal `$` sign.
The array or object must contain all bound values or Sequelize will throw an exception. This applies even to cases in which the database may ignore the bound parameter.
The database may add further restrictions to this. Bind parameters cannot be SQL keywords, nor table or column names. They are also ignored in quoted text or data. In PostgreSQL it may also be needed to typecast them, if the type cannot be inferred from the context `$1::varchar`.
```js
const { QueryTypes } = require('sequelize');
await sequelize.query(
'SELECT *, "text with literal $$1 and literal $$status" as t FROM projects WHERE status = $1',
{
bind: ['active'],
type: QueryTypes.SELECT
}
);
await sequelize.query(
'SELECT *, "text with literal $$1 and literal $$status" as t FROM projects WHERE status = $status',
{
bind: { status: 'active' },
type: QueryTypes.SELECT
}
);
```
# Dialects
Sequelize is independent from specific dialects. This means that you'll have to install the respective connector library to your project yourself.
## MySQL
In order to get Sequelize working nicely together with MySQL, you'll need to install`mysql2@^1.5.2`or higher. Once that's done you can use it like this:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mysql'
})
```
**Note:** You can pass options directly to dialect library by setting the
`dialectOptions` parameter.
## MariaDB
Library for MariaDB is `mariadb`.
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mariadb',
dialectOptions: {connectTimeout: 1000} // mariadb connector option
})
```
or using connection String:
```js
const sequelize = new Sequelize('mariadb://user:password@example.com:9821/database')
```
## SQLite
For SQLite compatibility you'll need`sqlite3@^4.0.0`. Configure Sequelize like this:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
// sqlite! now!
dialect: 'sqlite',
// the storage engine for sqlite
// - default ':memory:'
storage: 'path/to/database.sqlite'
})
```
Or you can use a connection string as well with a path:
```js
const sequelize = new Sequelize('sqlite:/home/abs/path/dbname.db')
const sequelize = new Sequelize('sqlite:relativePath/dbname.db')
```
## PostgreSQL
For PostgreSQL, two libraries are needed, `pg@^7.0.0` and `pg-hstore`. You'll just need to define the dialect:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
// gimme postgres, please!
dialect: 'postgres'
})
```
To connect over a unix domain socket, specify the path to the socket directory
in the `host` option.
The socket path must start with `/`.
```js
const sequelize = new Sequelize('database', 'username', 'password', {
// gimme postgres, please!
dialect: 'postgres',
host: '/path/to/socket_directory'
})
```
## MSSQL
The library for MSSQL is`tedious@^6.0.0` You'll just need to define the dialect.
Please note: `tedious@^6.0.0` requires you to nest MSSQL specific options inside an additional `options`-object inside the `dialectOptions`-object.
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mssql',
dialectOptions: {
options: {
useUTC: false,
dateFirst: 1,
}
}
})
```
# Getting started
In this tutorial you will learn to make a simple setup of Sequelize to learn the basics.
## Installing
Sequelize is available via [npm](https://www.npmjs.com/package/sequelize) (or [yarn](https://yarnpkg.com/package/sequelize)).
```sh
npm install --save sequelize
```
You'll also have to manually install the driver for your database of choice:
```sh
# One of the following:
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server
```
## Setting up a connection
To connect to the database, you must create a Sequelize instance. This can be done by either passing the connection parameters separately to the Sequelize constructor or by passing a single connection URI:
```js
const Sequelize = require('sequelize');
// Option 1: Passing parameters separately
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
});
// Option 2: Passing a connection URI
const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname');
```
The Sequelize constructor takes a whole slew of options that are documented in the [API Reference for the Sequelize constructor](../class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor).
### Note: setting up SQLite
If you're using SQLite, you should use the following instead:
```js
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'path/to/database.sqlite'
});
```
### Note: connection pool (production)
If you're connecting to the database from a single process, you should create only one Sequelize instance. Sequelize will set up a connection pool on initialization. This connection pool can be configured through the constructor's `options` parameter (using `options.pool`), as is shown in the following example:
```js
const sequelize = new Sequelize(/* ... */, {
// ...
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
```
Learn more in the [API Reference for the Sequelize constructor](../class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor). If you're connecting to the database from multiple processes, you'll have to create one instance per process, but each instance should have a maximum connection pool size of such that the total maximum size is respected. For example, if you want a max connection pool size of 90 and you have three processes, the Sequelize instance of each process should have a max connection pool size of 30.
### Note: setting up logging
Using `options.logging` can be used to define the function that gets executed every time Sequelize would log something. The default value is `console.log` and when using that only the first log parameter of log function call is displayed. For example, for query logging the first parameter is the raw query and the second (hidden by default) is the Sequelize object.
Common useful values for `options.logging`:
```js
const sequelize = new Sequelize(/* ... */, {
// Choose one of the logging options
logging: console.log, // Default, displays the first parameter of the log function call
logging: (...msg) => console.log(msg), // Displays all log function call parameters
logging: false, // Disables logging
logging: msg => logger.debug(msg), // Use custom logger (e.g. Winston or Bunyan), displays the first parameter
logging: logger.debug.bind(logger) // Alternative way to use custom logger, displays all messages
});
```
### Testing the connection
You can use the `.authenticate()` function to test if the connection is OK:
```js
sequelize
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
```
### Closing the connection
Sequelize will keep the connection open by default, and use the same connection for all queries. If you need to close the connection, call `sequelize.close()` (which is asynchronous and returns a Promise).
## Modeling a table
A model is a class that extends `Sequelize.Model`. Models can be defined in two equivalent ways. The first, with `Sequelize.Model.init(attributes, options)`:
```js
const Model = Sequelize.Model;
class User extends Model {}
User.init({
// attributes
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING
// allowNull defaults to true
}
}, {
sequelize,
modelName: 'user'
// options
});
```
Alternatively, using `sequelize.define`:
```js
const User = sequelize.define('user', {
// attributes
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING
// allowNull defaults to true
}
}, {
// options
});
```
Internally, `sequelize.define` calls `Model.init`.
The above code tells Sequelize to expect a table named `users` in the database with the fields `firstName` and `lastName`. The table name is automatically pluralized by default (a library called [inflection](https://www.npmjs.com/package/inflection) is used under the hood to do this). This behavior can be stopped for a specific model by using the `freezeTableName: true` option, or for all models by using the `define` option from the [Sequelize constructor](../class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor).
Sequelize also defines by default the fields `id` (primary key), `createdAt` and `updatedAt` to every model. This behavior can also be changed, of course (check the API Reference to learn more about the available options).
### Changing the default model options
The Sequelize constructor takes a `define` option which will change the default options for all defined models.
```js
const sequelize = new Sequelize(connectionURI, {
define: {
// The `timestamps` field specify whether or not the `createdAt` and `updatedAt` fields will be created.
// This was true by default, but now is false by default
timestamps: false
}
});
// Here `timestamps` will be false, so the `createdAt` and `updatedAt` fields will not be created.
class Foo extends Model {}
Foo.init({ /* ... */ }, { sequelize });
// Here `timestamps` is directly set to true, so the `createdAt` and `updatedAt` fields will be created.
class Bar extends Model {}
Bar.init({ /* ... */ }, { sequelize, timestamps: true });
```
You can read more about creating models in the [Model.init API Reference](../class/lib/model.js~Model.html#static-method-init), or in the [sequelize.define API reference](../class/lib/sequelize.js~Sequelize.html#instance-method-define).
## Synchronizing the model with the database
If you want Sequelize to automatically create the table (or modify it as needed) according to your model definition, you can use the `sync` method, as follows:
```js
// Note: using `force: true` will drop the table if it already exists
User.sync({ force: true }).then(() => {
// Now the `users` table in the database corresponds to the model definition
return User.create({
firstName: 'John',
lastName: 'Hancock'
});
});
```
### Synchronizing all models at once
Instead of calling `sync()` for every model, you can call `sequelize.sync()` which will automatically sync all models.
### Note for production
In production, you might want to consider using Migrations instead of calling `sync()` in your code. Learn more in the [Migrations](migrations.html) guide.
## Querying
A few simple queries are shown below:
```js
// Find all users
User.findAll().then(users => {
console.log("All users:", JSON.stringify(users, null, 4));
});
// Create a new user
User.create({ firstName: "Jane", lastName: "Doe" }).then(jane => {
console.log("Jane's auto-generated ID:", jane.id);
});
// Delete everyone named "Jane"
User.destroy({
where: {
firstName: "Jane"
}
}).then(() => {
console.log("Done");
});
// Change everyone without a last name to "Doe"
User.update({ lastName: "Doe" }, {
where: {
lastName: null
}
}).then(() => {
console.log("Done");
});
```
Sequelize has a lot of options for querying. You will learn more about those in the next tutorials. It is also possible to make raw SQL queries, if you really need them.
## Promises and async/await
As shown above by the extensive usage of `.then` calls, Sequelize uses Promises extensively. This means that, if your Node version supports it, you can use ES2017 `async/await` syntax for all asynchronous calls made with Sequelize.
Also, all Sequelize promises are in fact [Bluebird](http://bluebirdjs.com) promises, so you have the rich Bluebird API to use as well (for example, using `finally`, `tap`, `tapCatch`, `map`, `mapSeries`, etc). You can access the Bluebird constructor used internally by Sequelize with `Sequelize.Promise`, if you want to set any Bluebird specific options.
# \[MOVED\] Associations
The contents of this page were moved to other specialized guides.
If you're here, you might be looking for these topics:
* **Core Concepts**
* [Associations](assocs.html)
* **Advanced Association Concepts**
* [Eager Loading](eager-loading.html)
* [Creating with Associations](creating-with-associations.html)
* [Advanced M:N Associations](advanced-many-to-many.html)
* [Polymorphism & Scopes](polymorphism-and-scopes.html)
* **Other Topics**
* [Naming Strategies](naming-strategies.html)
* [Constraints & Circularities](constraints-and-circularities.html)
\ No newline at end of file
# \[MOVED\] Data Types
The contents of this page were moved to other specialized guides.
If you're here, you might be looking for these topics:
* **Core Concepts**
* [Model Basics: Data Types](model-basics.html#data-types)
* **Other Topics**
* [Other Data Types](other-data-types.html)
* [Extending Data Types](extending-data-types.html)
* [Dialect-Specific Things](dialect-specific-things.html)
\ No newline at end of file
# \[MOVED\] Models Definition
The contents of this page were moved to [Model Basics](model-basics.html).
The only exception is the guide on `sequelize.import`, which is deprecated and was removed from the docs. However, if you really need it, it was kept here.
----
## Deprecated: `sequelize.import`
> _**Note:** You should not use `sequelize.import`. Please just use `require` instead._
>
> _This documentation has been kept just in case you really need to maintain old code that uses it._
You can store your model definitions in a single file using the `sequelize.import` method. The returned object is exactly the same as defined in the imported file's function. The import is cached, just like `require`, so you won't run into trouble if importing a file more than once.
```js
// in your server file - e.g. app.js
const Project = sequelize.import(__dirname + "/path/to/models/project");
// The model definition is done in /path/to/models/project.js
module.exports = (sequelize, DataTypes) => {
return sequelize.define('project', {
name: DataTypes.STRING,
description: DataTypes.TEXT
});
};
```
The `import` method can also accept a callback as an argument.
```js
sequelize.import('project', (sequelize, DataTypes) => {
return sequelize.define('project', {
name: DataTypes.STRING,
description: DataTypes.TEXT
});
});
```
This extra capability is useful when, for example, `Error: Cannot find module` is thrown even though `/path/to/models/project` seems to be correct. Some frameworks, such as Meteor, overload `require`, and might raise an error such as:
```text
Error: Cannot find module '/home/you/meteorApp/.meteor/local/build/programs/server/app/path/to/models/project.js'
```
This can be worked around by passing in Meteor's version of `require`:
```js
// If this fails...
const AuthorModel = db.import('./path/to/models/project');
// Try this instead!
const AuthorModel = db.import('project', require('./path/to/models/project'));
```
\ No newline at end of file
# \[MOVED\] Models Usage
The contents of this page were moved to other specialized guides.
If you're here, you might be looking for these topics:
* **Core Concepts**
* [Model Querying - Basics](model-querying-basics.html)
* [Model Querying - Finders](model-querying-finders.html)
* [Raw Queries](raw-queries.html)
* **Advanced Association Concepts**
* [Eager Loading](eager-loading.html)
\ No newline at end of file
# \[MOVED\] Querying
The contents of this page were moved to other specialized guides.
If you're here, you might be looking for these topics:
* **Core Concepts**
* [Model Querying - Basics](model-querying-basics.html)
* [Model Querying - Finders](model-querying-finders.html)
* [Raw Queries](raw-queries.html)
* [Associations](assocs.html)
* **Other Topics**
* [Dialect-Specific Things](dialect-specific-things.html)
\ No newline at end of file
# Connection Pool
If you're connecting to the database from a single process, you should create only one Sequelize instance. Sequelize will set up a connection pool on initialization. This connection pool can be configured through the constructor's `options` parameter (using `options.pool`), as is shown in the following example:
```js
const sequelize = new Sequelize(/* ... */, {
// ...
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
```
Learn more in the [API Reference for the Sequelize constructor](../class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor). If you're connecting to the database from multiple processes, you'll have to create one instance per process, but each instance should have a maximum connection pool size of such that the total maximum size is respected. For example, if you want a max connection pool size of 90 and you have three processes, the Sequelize instance of each process should have a max connection pool size of 30.
# Constraints & Circularities
Adding constraints between tables means that tables must be created in the database in a certain order, when using `sequelize.sync`. If `Task` has a reference to `User`, the `User` table must be created before the `Task` table can be created. This can sometimes lead to circular references, where Sequelize cannot find an order in which to sync. Imagine a scenario of documents and versions. A document can have multiple versions, and for convenience, a document has a reference to its current version.
```js
const { Sequelize, Model, DataTypes } = require("sequelize");
class Document extends Model {}
Document.init({
author: DataTypes.STRING
}, { sequelize, modelName: 'document' });
class Version extends Model {}
Version.init({
timestamp: DataTypes.DATE
}, { sequelize, modelName: 'version' });
Document.hasMany(Version); // This adds documentId attribute to version
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId'
}); // This adds currentVersionId attribute to document
```
However, unfortunately the code above will result in the following error:
```text
Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
```
In order to alleviate that, we can pass `constraints: false` to one of the associations:
```js
Document.hasMany(Version);
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId',
constraints: false
});
```
Which will allow us to sync the tables correctly:
```sql
CREATE TABLE IF NOT EXISTS "documents" (
"id" SERIAL,
"author" VARCHAR(255),
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"currentVersionId" INTEGER,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "versions" (
"id" SERIAL,
"timestamp" TIMESTAMP WITH TIME ZONE,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"documentId" INTEGER REFERENCES "documents" ("id") ON DELETE
SET
NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
```
## Enforcing a foreign key reference without constraints
Sometimes you may want to reference another table, without adding any constraints, or associations. In that case you can manually add the reference attributes to your schema definition, and mark the relations between them.
```js
class Trainer extends Model {}
Trainer.init({
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
}, { sequelize, modelName: 'trainer' });
// Series will have a trainerId = Trainer.id foreign reference key
// after we call Trainer.hasMany(series)
class Series extends Model {}
Series.init({
title: Sequelize.STRING,
subTitle: Sequelize.STRING,
description: Sequelize.TEXT,
// Set FK relationship (hasMany) with `Trainer`
trainerId: {
type: DataTypes.INTEGER,
references: {
model: Trainer,
key: 'id'
}
}
}, { sequelize, modelName: 'series' });
// Video will have seriesId = Series.id foreign reference key
// after we call Series.hasOne(Video)
class Video extends Model {}
Video.init({
title: Sequelize.STRING,
sequence: Sequelize.INTEGER,
description: Sequelize.TEXT,
// set relationship (hasOne) with `Series`
seriesId: {
type: DataTypes.INTEGER,
references: {
model: Series, // Can be both a string representing the table name or a Sequelize model
key: 'id'
}
}
}, { sequelize, modelName: 'video' });
Series.hasOne(Video);
Trainer.hasMany(Series);
```
\ No newline at end of file
# Dialect-Specific Things
## Underlying Connector Libraries
### MySQL
The underlying connector library used by Sequelize for MySQL is the [mysql2](https://www.npmjs.com/package/mysql2) npm package (version 1.5.2 or higher).
You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mysql',
dialectOptions: {
// Your mysql2 options here
}
})
```
### MariaDB
The underlying connector library used by Sequelize for MariaDB is the [mariadb](https://www.npmjs.com/package/mariadb) npm package.
You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mariadb',
dialectOptions: {
// Your mariadb options here
// connectTimeout: 1000
}
});
```
### SQLite
The underlying connector library used by Sequelize for SQLite is the [sqlite3](https://www.npmjs.com/package/sqlite3) npm package (version 4.0.0 or above).
You specify the storage file in the Sequelize constructor with the `storage` option (use `:memory:` for an in-memory SQLite instance).
You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'sqlite',
storage: 'path/to/database.sqlite' // or ':memory:'
dialectOptions: {
// Your sqlite3 options here
}
});
```
### PostgreSQL
The underlying connector library used by Sequelize for PostgreSQL is the [pg](https://www.npmjs.com/package/pg) npm package (version 7.0.0 or above). The module [pg-hstore](https://www.npmjs.com/package/pg-hstore) is also necessary.
You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'postgres',
dialectOptions: {
// Your pg options here
}
});
```
To connect over a unix domain socket, specify the path to the socket directory in the `host` option. The socket path must start with `/`.
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'postgres',
host: '/path/to/socket_directory'
});
```
### MSSQL
The underlying connector library used by Sequelize for MSSQL is the [tedious](https://www.npmjs.com/package/tedious) npm package (version 6.0.0 or above).
You can provide custom options to it using `dialectOptions.options` in the Sequelize constructor:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'postgres',
dialectOptions: {
// Observe the need for this nested `options` field for MSSQL
options: {
// Your tedious options here
useUTC: false,
dateFirst: 1
}
}
});
```
## Data type: TIMESTAMP WITHOUT TIME ZONE - PostgreSQL only
If you are working with the PostgreSQL `TIMESTAMP WITHOUT TIME ZONE` and you need to parse it to a different timezone, please use the pg library's own parser:
```js
require('pg').types.setTypeParser(1114, stringValue => {
return new Date(stringValue + '+0000');
// e.g., UTC offset. Use any offset that you would like.
});
```
## Data type: ARRAY(ENUM) - PostgreSQL only
Array(Enum) type requireS special treatment. Whenever Sequelize will talk to the database, it has to typecast array values with ENUM name.
So this enum name must follow this pattern `enum_<table_name>_<col_name>`. If you are using `sync` then correct name will automatically be generated.
## Table Hints - MSSQL only
The `tableHint` option can be used to define a table hint. The hint must be a value from `TableHints` and should only be used when absolutely necessary. Only a single table hint is currently supported per query.
Table hints override the default behavior of MSSQL query optimizer by specifing certain options. They only affect the table or view referenced in that clause.
```js
const { TableHints } = require('sequelize');
Project.findAll({
// adding the table hint NOLOCK
tableHint: TableHints.NOLOCK
// this will generate the SQL 'WITH (NOLOCK)'
})
```
## Index Hints - MySQL/MariaDB only
The `indexHints` option can be used to define index hints. The hint type must be a value from `IndexHints` and the values should reference existing indexes.
Index hints [override the default behavior of the MySQL query optimizer](https://dev.mysql.com/doc/refman/5.7/en/index-hints.html).
```js
const { IndexHints } = require("sequelize");
Project.findAll({
indexHints: [
{ type: IndexHints.USE, values: ['index_project_on_name'] }
],
where: {
id: {
[Op.gt]: 623
},
name: {
[Op.like]: 'Foo %'
}
}
});
```
The above will generate a MySQL query that looks like this:
```sql
SELECT * FROM Project USE INDEX (index_project_on_name) WHERE name LIKE 'FOO %' AND id > 623;
```
`Sequelize.IndexHints` includes `USE`, `FORCE`, and `IGNORE`.
See [Issue #9421](https://github.com/sequelize/sequelize/issues/9421) for the original API proposal.
## Engines - MySQL/MariaDB only
The default engine for a model is InnoDB.
You can change the engine for a model with the `engine` option (e.g., to MyISAM):
```js
const Person = sequelize.define('person', { /* attributes */ }, {
engine: 'MYISAM'
});
```
Like every option for the definition of a model, this setting can also be changed globally with the `define` option of the Sequelize constructor:
```js
const sequelize = new Sequelize(db, user, pw, {
define: { engine: 'MYISAM' }
})
```
## Table comments - MySQL/MariaDB/PostgreSQL only
You can specify a comment for a table when defining the model:
```js
class Person extends Model {}
Person.init({ /* attributes */ }, {
comment: "I'm a table comment!",
sequelize
})
```
The comment will be set when calling `sync()`.
\ No newline at end of file
# Extending Data Types
Most likely the type you are trying to implement is already included in [DataTypes](data-types.html). If a new datatype is not included, this manual will show how to write it yourself.
Sequelize doesn't create new datatypes in the database. This tutorial explains how to make Sequelize recognize new datatypes and assumes that those new datatypes are already created in the database.
To extend Sequelize datatypes, do it before any Sequelize instance is created.
## Example
In this example, we will create a type called `SOMETYPE` that replicates the built-in datatype `DataTypes.INTEGER(11).ZEROFILL.UNSIGNED`.
```js
const { Sequelize, DataTypes, Utils } = require('Sequelize');
createTheNewDataType();
const sequelize = new Sequelize('sqlite::memory:');
function createTheNewDataType() {
class SOMETYPE extends DataTypes.ABSTRACT {
// Mandatory: complete definition of the new type in the database
toSql() {
return 'INTEGER(11) UNSIGNED ZEROFILL'
}
// Optional: validator function
validate(value, options) {
return (typeof value === 'number') && (!Number.isNaN(value));
}
// Optional: sanitizer
_sanitize(value) {
// Force all numbers to be positive
return value < 0 ? 0 : Math.round(value);
}
// Optional: value stringifier before sending to database
_stringify(value) {
return value.toString();
}
// Optional: parser for values received from the database
static parse(value) {
return Number.parseInt(value);
}
}
// Mandatory: set the type key
SOMETYPE.prototype.key = SOMETYPE.key = 'SOMETYPE';
// Mandatory: add the new type to DataTypes. Optionally wrap it on `Utils.classToInvokable` to
// be able to use this datatype directly without having to call `new` on it.
DataTypes.SOMETYPE = Utils.classToInvokable(SOMETYPE);
// Optional: disable escaping after stringifier. Do this at your own risk, since this opens opportunity for SQL injections.
// DataTypes.SOMETYPE.escape = false;
}
```
After creating this new datatype, you need to map this datatype in each database dialect and make some adjustments.
## PostgreSQL
Let's say the name of the new datatype is `pg_new_type` in the postgres database. That name has to be mapped to `DataTypes.SOMETYPE`. Additionally, it is required to create a child postgres-specific datatype.
```js
function createTheNewDataType() {
// [...]
const PgTypes = DataTypes.postgres;
// Mandatory: map postgres datatype name
DataTypes.SOMETYPE.types.postgres = ['pg_new_type'];
// Mandatory: create a postgres-specific child datatype with its own parse
// method. The parser will be dynamically mapped to the OID of pg_new_type.
PgTypes.SOMETYPE = function SOMETYPE() {
if (!(this instanceof PgTypes.SOMETYPE)) {
return new PgTypes.SOMETYPE();
}
DataTypes.SOMETYPE.apply(this, arguments);
}
const util = require('util'); // Built-in Node package
util.inherits(PgTypes.SOMETYPE, DataTypes.SOMETYPE);
// Mandatory: create, override or reassign a postgres-specific parser
// PgTypes.SOMETYPE.parse = value => value;
PgTypes.SOMETYPE.parse = DataTypes.SOMETYPE.parse || x => x;
// Optional: add or override methods of the postgres-specific datatype
// like toSql, escape, validate, _stringify, _sanitize...
}
```
### Ranges
After a new range type has been [defined in postgres](https://www.postgresql.org/docs/current/static/rangetypes.html#RANGETYPES-DEFINING), it is trivial to add it to Sequelize.
In this example the name of the postgres range type is `SOMETYPE_range` and the name of the underlying postgres datatype is `pg_new_type`. The key of `subtypes` and `castTypes` is the key of the Sequelize datatype `DataTypes.SOMETYPE.key`, in lower case.
```js
function createTheNewDataType() {
// [...]
// Add postgresql range, SOMETYPE comes from DataType.SOMETYPE.key in lower case
DataTypes.RANGE.types.postgres.subtypes.SOMETYPE = 'SOMETYPE_range';
DataTypes.RANGE.types.postgres.castTypes.SOMETYPE = 'pg_new_type';
}
```
The new range can be used in model definitions as `DataTypes.RANGE(DataTypes.SOMETYPE)` or `DataTypes.RANGE(DataTypes.SOMETYPE)`.
# Indexes
Sequelize supports adding indexes to the model definition which will be created on [`sequelize.sync()`](../class/lib/sequelize.js~Sequelize.html#instance-method-sync).
```js
const User = sequelize.define('User', { /* attributes */ }, {
indexes: [
// Create a unique index on email
{
unique: true,
fields: ['email']
},
// Creates a gin index on data with the jsonb_path_ops operator
{
fields: ['data'],
using: 'gin',
operator: 'jsonb_path_ops'
},
// By default index name will be [table]_[fields]
// Creates a multi column partial index
{
name: 'public_by_author',
fields: ['author', 'status'],
where: {
status: 'public'
}
},
// A BTREE index with an ordered field
{
name: 'title_index',
using: 'BTREE',
fields: [
'author',
{
attribute: 'title',
collate: 'en_US',
order: 'DESC',
length: 5
}
]
}
]
});
```
\ No newline at end of file
# Working with legacy tables # Working with Legacy Tables
While out of the box Sequelize will seem a bit opinionated it's easy to work legacy tables and forward proof your application by defining (otherwise generated) table and field names. While out of the box Sequelize will seem a bit opinionated it's easy to work legacy tables and forward proof your application by defining (otherwise generated) table and field names.
...@@ -21,7 +21,7 @@ User.init({ ...@@ -21,7 +21,7 @@ User.init({
class MyModel extends Model {} class MyModel extends Model {}
MyModel.init({ MyModel.init({
userId: { userId: {
type: Sequelize.INTEGER, type: DataTypes.INTEGER,
field: 'user_id' field: 'user_id'
} }
}, { sequelize }); }, { sequelize });
...@@ -37,7 +37,7 @@ To define your own primary key: ...@@ -37,7 +37,7 @@ To define your own primary key:
class Collection extends Model {} class Collection extends Model {}
Collection.init({ Collection.init({
uid: { uid: {
type: Sequelize.INTEGER, type: DataTypes.INTEGER,
primaryKey: true, primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres autoIncrement: true // Automatically gets converted to SERIAL for postgres
} }
...@@ -46,7 +46,7 @@ Collection.init({ ...@@ -46,7 +46,7 @@ Collection.init({
class Collection extends Model {} class Collection extends Model {}
Collection.init({ Collection.init({
uuid: { uuid: {
type: Sequelize.UUID, type: DataTypes.UUID,
primaryKey: true primaryKey: true
} }
}, { sequelize }); }, { sequelize });
......
# Naming Strategies
## The `underscored` option
Sequelize provides the `underscored` option for a model. When `true`, this option will set the `field` option on all attributes to the [snake_case](https://en.wikipedia.org/wiki/Snake_case) version of its name. This also applies to foreign keys automatically generated by associations and other automatically generated fields. Example:
```js
const User = sequelize.define('task', { username: Sequelize.STRING }, {
underscored: true
});
const Task = sequelize.define('task', { title: Sequelize.STRING }, {
underscored: true
});
User.hasMany(Task);
Task.belongsTo(User);
```
Above we have the models User and Task, both using the `underscored` option. We also have a One-to-Many relationship between them. Also, recall that since `timestamps` is true by default, we should expect the `createdAt` and `updatedAt` fields to be automatically created as well.
Without the `underscored` option, Sequelize would automatically define:
* A `createdAt` attribute for each model, pointing to a column named `createdAt` in each table
* An `updatedAt` attribute for each model, pointing to a column named `updatedAt` in each table
* A `userId` attribute in the `Task` model, pointing to a column named `userId` in the task table
With the `underscored` option enabled, Sequelize will instead define:
* A `createdAt` attribute for each model, pointing to a column named `created_at` in each table
* An `updatedAt` attribute for each model, pointing to a column named `updated_at` in each table
* A `userId` attribute in the `Task` model, pointing to a column named `user_id` in the task table
Note that in both cases the fields are still [camelCase](https://en.wikipedia.org/wiki/Camel_case) in the JavaScript side; this option only changes how these fields are mapped to the database itself. The `field` option of every attribute is set to their snake_case version, but the attribute itself remains camelCase.
This way, calling `sync()` on the above code will generate the following:
```sql
CREATE TABLE IF NOT EXISTS "users" (
"id" SERIAL,
"username" VARCHAR(255),
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "tasks" (
"id" SERIAL,
"title" VARCHAR(255),
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"user_id" INTEGER REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
```
## Singular vs. Plural
At a first glance, it can be confusing whether the singular form or plural form of a name shall be used around in Sequelize. This section aims at clarifying that a bit.
Recall that Sequelize uses a library called [inflection](https://www.npmjs.com/package/inflection) under the hood, so that irregular plurals (such as `person -> people`) are computed correctly. However, if you're working in another language, you may want to define the singular and plural forms of names directly; sequelize allows you to do this with some options.
### When defining models
Models should be defined with the singular form of a word. Example:
```js
sequelize.define('foo', { name: DataTypes.STRING });
```
Above, the model name is `foo` (singular), and the respective table name is `foos`, since Sequelize automatically gets the plural for the table name.
### When defining a reference key in a model
```js
sequelize.define('foo', {
name: DataTypes.STRING,
barId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: "bars",
key: "id"
},
onDelete: "CASCADE"
},
});
```
In the above example we are manually defining a key that references another model. It's not usual to do this, but if you have to, you should use the table name there. This is because the reference is created upon the referencced table name. In the example above, the plural form was used (`bars`), assuming that the `bar` model was created with the default settings (making its underlying table automatically pluralized).
### When retrieving data from eager loading
When you perform an `include` in a query, the included data will be added to an extra field in the returned objects, according to the following rules:
* When including something from a single association (`hasOne` or `belongsTo`) - the field name will be the singular version of the model name;
* When including something from a multiple association (`hasMany` or `belongsToMany`) - the field name will be the plural form of the model.
In short, the name of the field will take the most logical form in each situation.
Examples:
```js
// Assuming Foo.hasMany(Bar)
const foo = Foo.findOne({ include: Bar });
// foo.bars will be an array
// foo.bar will not exist since it doens't make sense
// Assuming Foo.hasOne(Bar)
const foo = Foo.findOne({ include: Bar });
// foo.bar will be an object (possibly null if there is no associated model)
// foo.bars will not exist since it doens't make sense
// And so on.
```
### Overriding singulars and plurals when defining aliases
When defining an alias for an association, instead of using simply `{ as: 'myAlias' }`, you can pass an object to specify the singular and plural forms:
```js
Project.belongsToMany(User, {
as: {
singular: 'líder',
plural: 'líderes'
}
});
```
If you know that a model will always use the same alias in associations, you can provide the singular and plural forms directly to the model itself:
```js
const User = sequelize.define('user', { /* ... */ }, {
name: {
singular: 'líder',
plural: 'líderes',
}
});
Project.belongsToMany(User);
```
The mixins added to the user instances will use the correct forms. For example, instead of `project.addUser()`, Sequelize will provide `project.getLíder()`. Also, instead of `project.setUsers()`, Sequelize will provide `project.setLíderes()`.
Note: recall that using `as` to change the name of the association will also change the name of the foreign key. Therefore it is recommended to also specify the foreign key(s) involved directly in this case.
```js
// Example of possible mistake
Invoice.belongsTo(Subscription, { as: 'TheSubscription' });
Subscription.hasMany(Invoice);
```
The first call above will establish a foreign key called `theSubscriptionId` on `Invoice`. However, the second call will also establish a foreign key on `Invoice` (since as we know, `hasMany` calls places foreign keys in the target model) - however, it will be named `subscriptionId`. This way you will have both `subscriptionId` and `theSubscriptionId` columns.
The best approach is to choose a name for the foreign key and place it explicitly in both calls. For example, if `subscription_id` was chosen:
```js
// Fixed example
Invoice.belongsTo(Subscription, { as: 'TheSubscription', foreignKey: 'subscription_id' });
Subscription.hasMany(Invoice, { foreignKey: 'subscription_id' });
```
\ No newline at end of file
## Optimistic Locking
Sequelize has built-in support for optimistic locking through a model instance version count.
Optimistic locking is disabled by default and can be enabled by setting the `version` property to true in a specific model definition or global model configuration. See [model configuration](models-definition.html#configuration) for more details.
Optimistic locking allows concurrent access to model records for edits and prevents conflicts from overwriting data. It does this by checking whether another process has made changes to a record since it was read and throws an OptimisticLockError when a conflict is detected.
\ No newline at end of file
# Other Data Types
Apart from the most common data types mentioned in the Model Basics guide, Sequelize provides several other data types.
## Ranges (PostgreSQL only)
```js
DataTypes.RANGE(DataTypes.INTEGER) // int4range
DataTypes.RANGE(DataTypes.BIGINT) // int8range
DataTypes.RANGE(DataTypes.DATE) // tstzrange
DataTypes.RANGE(DataTypes.DATEONLY) // daterange
DataTypes.RANGE(DataTypes.DECIMAL) // numrange
```
Since range types have extra information for their bound inclusion/exclusion it's not very straightforward to just use a tuple to represent them in javascript.
When supplying ranges as values you can choose from the following APIs:
```js
// defaults to inclusive lower bound, exclusive upper bound
const range = [
new Date(Date.UTC(2016, 0, 1)),
new Date(Date.UTC(2016, 1, 1))
];
// '["2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00")'
// control inclusion
const range = [
{ value: new Date(Date.UTC(2016, 0, 1)), inclusive: false },
{ value: new Date(Date.UTC(2016, 1, 1)), inclusive: true },
];
// '("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00"]'
// composite form
const range = [
{ value: new Date(Date.UTC(2016, 0, 1)), inclusive: false },
new Date(Date.UTC(2016, 1, 1)),
];
// '("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00")'
const Timeline = sequelize.define('Timeline', {
range: DataTypes.RANGE(DataTypes.DATE)
});
await Timeline.create({ range });
```
However, retrieved range values always come in the form of an array of objects. For example, if the stored value is `("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00"]`, after a finder query you will get:
```js
[
{ value: Date, inclusive: false },
{ value: Date, inclusive: true }
]
```
You will need to call `reload()` after updating an instance with a range type or use the `returning: true` option.
### Special Cases
```js
// empty range:
Timeline.create({ range: [] }); // range = 'empty'
// Unbounded range:
Timeline.create({ range: [null, null] }); // range = '[,)'
// range = '[,"2016-01-01 00:00:00+00:00")'
Timeline.create({ range: [null, new Date(Date.UTC(2016, 0, 1))] });
// Infinite range:
// range = '[-infinity,"2016-01-01 00:00:00+00:00")'
Timeline.create({ range: [-Infinity, new Date(Date.UTC(2016, 0, 1))] });
```
## BLOBs
```js
DataTypes.BLOB // BLOB (bytea for PostgreSQL)
DataTypes.BLOB('tiny') // TINYBLOB (bytea for PostgreSQL)
DataTypes.BLOB('medium') // MEDIUMBLOB (bytea for PostgreSQL)
DataTypes.BLOB('long') // LONGBLOB (bytea for PostgreSQL)
```
The blob datatype allows you to insert data both as strings and as buffers. However, when a blob is retrieved from database with Sequelize, it will always be retrieved as a buffer.
## ENUMs
The ENUM is a data type that accepts only a few values, specified as a list.
```js
DataTypes.ENUM('foo', 'bar') // An ENUM with allowed values 'foo' and 'bar'
```
ENUMs can also be specified with the `values` field of the column definition, as follows:
```js
sequelize.define('foo', {
states: {
type: DataTypes.ENUM,
values: ['active', 'pending', 'deleted']
}
});
```
## JSON (SQLite, MySQL, MariaDB and PostgreSQL only)
The `DataTypes.JSON` data type is only supported for SQLite, MySQL, MariaDB and PostgreSQL. However, there is a minimum support for MSSQL (see below).
### Note for PostgreSQL
The JSON data type in PostgreSQL stores the value as plain text, as opposed to binary representation. If you simply want to store and retrieve a JSON representation, using JSON will take less disk space and less time to build from its input representation. However, if you want to do any operations on the JSON value, you should prefer the JSONB data type described below.
### JSONB (PostgreSQL only)
PostgreSQL also supports a JSONB data type: `DataTypes.JSONB`. It can be queried in three different ways:
```js
// Nested object
await Foo.findOne({
where: {
meta: {
video: {
url: {
[Op.ne]: null
}
}
}
}
});
// Nested key
await Foo.findOne({
where: {
"meta.audio.length": {
[Op.gt]: 20
}
}
});
// Containment
await Foo.findOne({
where: {
meta: {
[Op.contains]: {
site: {
url: 'http://google.com'
}
}
}
}
});
```
### MSSQL
MSSQL does not have a JSON data type, however it does provide some support for JSON stored as strings through certain functions since SQL Server 2016. Using these functions, you will be able to query the JSON stored in the string, but any returned values will need to be parsed seperately.
```js
// ISJSON - to test if a string contains valid JSON
await User.findAll({
where: sequelize.where(sequelize.fn('ISJSON', sequelize.col('userDetails')), 1)
})
// JSON_VALUE - extract a scalar value from a JSON string
await User.findAll({
attributes: [[ sequelize.fn('JSON_VALUE', sequelize.col('userDetails'), '$.address.Line1'), 'address line 1']]
})
// JSON_VALUE - query a scalar value from a JSON string
await User.findAll({
where: sequelize.where(sequelize.fn('JSON_VALUE', sequelize.col('userDetails'), '$.address.Line1'), '14, Foo Street')
})
// JSON_QUERY - extract an object or array
await User.findAll({
attributes: [[ sequelize.fn('JSON_QUERY', sequelize.col('userDetails'), '$.address'), 'full address']]
})
```
## Others
```js
DataTypes.ARRAY(/* DataTypes.SOMETHING */) // Defines an array of DataTypes.SOMETHING. PostgreSQL only.
DataTypes.CIDR // CIDR PostgreSQL only
DataTypes.INET // INET PostgreSQL only
DataTypes.MACADDR // MACADDR PostgreSQL only
DataTypes.GEOMETRY // Spatial column. PostgreSQL (with PostGIS) or MySQL only.
DataTypes.GEOMETRY('POINT') // Spatial column with geometry type. PostgreSQL (with PostGIS) or MySQL only.
DataTypes.GEOMETRY('POINT', 4326) // Spatial column with geometry type and SRID. PostgreSQL (with PostGIS) or MySQL only.
```
\ No newline at end of file
# Query Interface
An instance of Sequelize uses something called **Query Interface** to communicate to the database in a dialect-agnostic way. Most of the methods you've learned in this manual are implemented with the help of several methods from the query interface.
The methods from the query interface are therefore lower-level methods; you should use them only if you do not find another way to do it with higher-level APIs from Sequelize. They are, of course, still higher-level than running raw queries directly (i.e., writing SQL by hand).
This guide shows a few examples, but for the full list of what it can do, and for detailed usage of each method, check the [QueryInterface API](../class/lib/query-interface.js~QueryInterface.html).
## Obtaining the query interface
From now on, we will call `queryInterface` the singleton instance of the [QueryInterface](../class/lib/query-interface.js~QueryInterface.html) class, which is available on your Sequelize instance:
```js
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize(/* ... */);
const queryInterface = sequelize.getQueryInterface();
```
## Creating a table
```js
queryInterface.createTable('Person', {
name: DataTypes.STRING,
isBetaMember: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
}
});
```
Generated SQL (using SQLite):
```SQL
CREATE TABLE IF NOT EXISTS `Person` (
`name` VARCHAR(255),
`isBetaMember` TINYINT(1) NOT NULL DEFAULT 0
);
```
**Note:** Consider defining a Model instead and calling `YourModel.sync()` instead, which is a higher-level approach.
## Adding a column to a table
```js
queryInterface.addColumn('Person', 'petName', { type: DataTypes.STRING });
```
Generated SQL (using SQLite):
```sql
ALTER TABLE `Person` ADD `petName` VARCHAR(255);
```
## Changing the datatype of a column
```js
queryInterface.changeColumn('Person', 'foo', {
type: DataTypes.FLOAT,
defaultValue: 3.14,
allowNull: false
});
```
Generated SQL (using MySQL):
```sql
ALTER TABLE `Person` CHANGE `foo` `foo` FLOAT NOT NULL DEFAULT 3.14;
```
## Removing a column
```js
queryInterface.removeColumn('Person', 'petName', { /* query options */ });
```
Generated SQL (using PostgreSQL):
```SQL
ALTER TABLE "public"."Person" DROP COLUMN "petName";
```
## Changing and removing columns in SQLite
SQLite does not support directly altering and removing columns. However, Sequelize will try to work around this by recreating the whole table with the help of a backup table, inspired by [these instructions](https://www.sqlite.org/lang_altertable.html#otheralter).
For example:
```js
// Assuming we have a table in SQLite created as follows:
queryInterface.createTable('Person', {
name: DataTypes.STRING,
isBetaMember: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
},
petName: DataTypes.STRING,
foo: DataTypes.INTEGER
});
// And we change a column:
queryInterface.changeColumn('Person', 'foo', {
type: DataTypes.FLOAT,
defaultValue: 3.14,
allowNull: false
});
```
The following SQL calls are generated for SQLite:
```sql
PRAGMA TABLE_INFO(`Person`);
CREATE TABLE IF NOT EXISTS `Person_backup` (
`name` VARCHAR(255),
`isBetaMember` TINYINT(1) NOT NULL DEFAULT 0,
`foo` FLOAT NOT NULL DEFAULT '3.14',
`petName` VARCHAR(255)
);
INSERT INTO `Person_backup`
SELECT
`name`,
`isBetaMember`,
`foo`,
`petName`
FROM `Person`;
DROP TABLE `Person`;
CREATE TABLE IF NOT EXISTS `Person` (
`name` VARCHAR(255),
`isBetaMember` TINYINT(1) NOT NULL DEFAULT 0,
`foo` FLOAT NOT NULL DEFAULT '3.14',
`petName` VARCHAR(255)
);
INSERT INTO `Person`
SELECT
`name`,
`isBetaMember`,
`foo`,
`petName`
FROM `Person_backup`;
DROP TABLE `Person_backup`;
```
## Other
As mentioned in the beginning of this guide, there is a lot more to the Query Interface available in Sequelize! Check the [QueryInterface API](../class/lib/query-interface.js~QueryInterface.html) for a full list of what can be done.
\ No newline at end of file
# Read replication # Read Replication
Sequelize supports read replication, i.e. having multiple servers that you can connect to when you want to do a SELECT query. When you do read replication, you specify one or more servers to act as read replicas, and one server to act as the write master, which handles all writes and updates and propagates them to the replicas (note that the actual replication process is **not** handled by Sequelize, but should be set up by database backend). Sequelize supports [read replication](https://en.wikipedia.org/wiki/Replication_%28computing%29#Database_replication), i.e. having multiple servers that you can connect to when you want to do a SELECT query. When you do read replication, you specify one or more servers to act as read replicas, and one server to act as the write master, which handles all writes and updates and propagates them to the replicas (note that the actual replication process is **not** handled by Sequelize, but should be set up by database backend).
```js ```js
const sequelize = new Sequelize('database', null, null, { const sequelize = new Sequelize('database', null, null, {
...@@ -8,10 +8,10 @@ const sequelize = new Sequelize('database', null, null, { ...@@ -8,10 +8,10 @@ const sequelize = new Sequelize('database', null, null, {
port: 3306 port: 3306
replication: { replication: {
read: [ read: [
{ host: '8.8.8.8', username: 'read-username', password: 'some-password' }, { host: '8.8.8.8', username: 'read-1-username', password: process.env.READ_DB_1_PW },
{ host: '9.9.9.9', username: 'another-username', password: null } { host: '9.9.9.9', username: 'read-2-username', password: process.env.READ_DB_2_PW }
], ],
write: { host: '1.1.1.1', username: 'write-username', password: 'any-password' } write: { host: '1.1.1.1', username: 'write-username', password: process.env.WRITE_DB_PW }
}, },
pool: { // If you want to override the options used for the read/write pool you can do so here pool: { // If you want to override the options used for the read/write pool you can do so here
max: 20, max: 20,
......
# Sub Queries
Consider you have two models, `Post` and `Reaction`, with a One-to-Many relationship set up, so that one post has many reactions:
```js
const Post = sequelize.define('post', {
content: DataTypes.STRING
}, { timestamps: false });
const Reaction = sequelize.define('reaction', {
type: DataTypes.STRING
}, { timestamps: false });
Post.hasMany(Reaction);
Reaction.belongsTo(Post);
```
*Note: we have disabled timestamps just to have shorter queries for the next examples.*
Let's fill our tables with some data:
```js
async function makePostWithReactions(content, reactionTypes) {
const post = await Post.create({ content });
await Reaction.bulkCreate(
reactionTypes.map(type => ({ type, postId: post.id }))
);
return post;
}
await makePostWithReactions('Hello World', [
'Like', 'Angry', 'Laugh', 'Like', 'Like', 'Angry', 'Sad', 'Like'
]);
await makePostWithReactions('My Second Post', [
'Laugh', 'Laugh', 'Like', 'Laugh'
]);
```
Now, we are ready for examples of the power of subqueries.
Let's say we wanted to compute via SQL a `laughReactionsCount` for each post. We can achieve that with a sub-query, such as the following:
```sql
SELECT
*,
(
SELECT COUNT(*)
FROM reactions AS reaction
WHERE
reaction.postId = post.id
AND
reaction.type = "Laugh"
) AS laughReactionsCount
FROM posts AS post
```
If we run the above raw SQL query through Sequelize, we get:
```json
[
{
"id": 1,
"content": "Hello World",
"laughReactionsCount": 1
},
{
"id": 2,
"content": "My Second Post",
"laughReactionsCount": 3
}
]
```
So how can we achieve that with more help from Sequelize, without having to write the whole raw query by hand?
The answer: by combining the `attributes` option of the finder methods (such as `findAll`) with the `sequelize.literal` utility function, that allows you to directly insert arbitrary content into the query without any automatic escaping.
This means that Sequelize will help you with the main, larger query, but you will still have to write that sub-query by yourself:
```js
Post.findAll({
attributes: {
include: [
[
// Note the wrapping parentheses in the call below!
sequelize.literal(`(
SELECT COUNT(*)
FROM reactions AS reaction
WHERE
reaction.postId = post.id
AND
reaction.type = "Laugh"
)`),
'laughReactionsCount'
]
]
}
});
```
*Important Note: Since `sequelize.literal` inserts arbitrary content without escaping to the query, it deserves very special attention since it may be a source of (major) security vulnerabilities. It should not be used on user-generated content.* However, here, we are using `sequelize.literal` with a fixed string, carefully written by us (the coders). This is ok, since we know what we are doing.
The above gives the following output:
```json
[
{
"id": 1,
"content": "Hello World",
"laughReactionsCount": 1
},
{
"id": 2,
"content": "My Second Post",
"laughReactionsCount": 3
}
]
```
Success!
## Using sub-queries for complex ordering
This idea can be used to enable complex ordering, such as ordering posts by the number of laugh reactions they have:
```js
Post.findAll({
attributes: {
include: [
[
sequelize.literal(`(
SELECT COUNT(*)
FROM reactions AS reaction
WHERE
reaction.postId = post.id
AND
reaction.type = "Laugh"
)`),
'laughReactionsCount'
]
]
},
order: [
[sequelize.literal('laughReactionsCount'), 'DESC']
]
});
```
Result:
```json
[
{
"id": 2,
"content": "My Second Post",
"laughReactionsCount": 3
},
{
"id": 1,
"content": "Hello World",
"laughReactionsCount": 1
}
]
```
\ No newline at end of file
# Transactions
Sequelize does not use [transactions](https://en.wikipedia.org/wiki/Database_transaction) by default. However, for production-ready usage of Sequelize, you should definitely configure Sequelize to use transactions.
Sequelize supports two ways of using transactions:
1. **Unmanaged transactions:** Committing and rolling back the transaction should be done manually by the user (by calling the appropriate Sequelize methods).
2. **Managed transactions**: Sequelize will automatically rollback the transaction if any error is thrown, or commit the transaction otherwise. Also, if CLS (Continuation Local Storage) is enabled, all queries within the transaction callback will automatically receive the transaction object.
## Unmanaged transactions
Let's start with an example:
```js
// First, we start a transaction and save it into a variable
const t = await sequelize.transaction();
try {
// Then, we do some calls passing this transaction as an option:
const user = await User.create({
firstName: 'Bart',
lastName: 'Simpson'
}, { transaction: t });
await user.addSibling({
firstName: 'Lisa',
lastName: 'Simpson'
}, { transaction: t });
// If the execution reaches this line, no errors were thrown.
// We commit the transaction.
await t.commit();
} catch (error) {
// If the execution reaches this line, an error was thrown.
// We rollback the transaction.
await t.rollback();
}
```
As shown above, the *unmanaged transaction* approach requires that you commit and rollback the transaction manually, when necessary.
## Managed transactions
Managed transactions handle committing or rolling back the transaction automatically. You start a managed transaction by passing a callback to `sequelize.transaction`. This callback can be `async` (and usually is).
The following will happen in this case:
* Sequelize will automatically start a transaction and obtain a transaction object `t`
* Then, Sequelize will execute the callback you provided, passing `t` into it
* If your callback throws, Sequelize will automatically rollback the transaction
* If your callback succeeds, Sequelize will automatically commit the transaction
* Only then the `sequelize.transaction` call will settle:
* Either resolving with the resolution of your callback
* Or, if your callback throws, rejecting with the thrown error
Example code:
```js
try {
const result = await sequelize.transaction(async (t) => {
const user = await User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, { transaction: t });
await user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, { transaction: t });
return user;
});
// If the execution reaches this line, the transaction has been committed successfully
// `result` is whatever was returned from the transaction callback (the `user`, in this case)
} catch (error) {
// If the execution reaches this line, an error occurred.
// The transaction has already been rolled back automatically by Sequelize!
}
```
Note that `t.commit()` and `t.rollback()` were not called directly (which is correct).
### Throw errors to rollback
When using the managed transaction you should *never* commit or rollback the transaction manually. If all queries are successful (in the sense of not throwing any error), but you still want to rollback the transaction, you should throw an error yourself:
```js
await sequelize.transaction(t => {
const user = await User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, { transaction: t });
// Woops, the query was successful but we still want to roll back!
// We throw an error manually, so that Sequelize handles everything automatically.
throw new Error();
});
```
### Automatically pass transactions to all queries
In the examples above, the transaction is still manually passed, by passing `{ transaction: t }` as the second argument. To automatically pass the transaction to all queries you must install the [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) (CLS) module and instantiate a namespace in your own code:
```js
const cls = require('cls-hooked');
const namespace = cls.createNamespace('my-very-own-namespace');
```
To enable CLS you must tell sequelize which namespace to use by using a static method of the sequelize constructor:
```js
const Sequelize = require('sequelize');
Sequelize.useCLS(namespace);
new Sequelize(....);
```
Notice, that the `useCLS()` method is on the *constructor*, not on an instance of sequelize. This means that all instances will share the same namespace, and that CLS is all-or-nothing - you cannot enable it only for some instances.
CLS works like a thread-local storage for callbacks. What this means in practice is that different callback chains can access local variables by using the CLS namespace. When CLS is enabled sequelize will set the `transaction` property on the namespace when a new transaction is created. Since variables set within a callback chain are private to that chain several concurrent transactions can exist at the same time:
```js
sequelize.transaction((t1) => {
namespace.get('transaction') === t1; // true
});
sequelize.transaction((t2) => {
namespace.get('transaction') === t2; // true
});
```
In most case you won't need to access `namespace.get('transaction')` directly, since all queries will automatically look for a transaction on the namespace:
```js
sequelize.transaction((t1) => {
// With CLS enabled, the user will be created inside the transaction
return User.create({ name: 'Alice' });
});
```
## Concurrent/Partial transactions
You can have concurrent transactions within a sequence of queries or have some of them excluded from any transactions. Use the `transaction` option to control which transaction a query belongs to:
**Note:** *SQLite does not support more than one transaction at the same time.*
### With CLS enabled
```js
sequelize.transaction((t1) => {
return sequelize.transaction((t2) => {
// With CLS enabled, queries here will by default use t2.
// Pass in the `transaction` option to define/alter the transaction they belong to.
return Promise.all([
User.create({ name: 'Bob' }, { transaction: null }),
User.create({ name: 'Mallory' }, { transaction: t1 }),
User.create({ name: 'John' }) // this would default to t2
]);
});
});
```
## Passing options
The `sequelize.transaction` method accepts options.
For unmanaged transactions, just use `sequelize.transaction(options)`.
For managed transactions, use `sequelize.transaction(options, callback)`.
## Isolation levels
The possible isolations levels to use when starting a transaction:
```js
const { Transaction } = require('sequelize');
// The following are valid isolation levels:
Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED // "READ UNCOMMITTED"
Transaction.ISOLATION_LEVELS.READ_COMMITTED // "READ COMMITTED"
Transaction.ISOLATION_LEVELS.REPEATABLE_READ // "REPEATABLE READ"
Transaction.ISOLATION_LEVELS.SERIALIZABLE // "SERIALIZABLE"
```
By default, sequelize uses the isolation level of the database. If you want to use a different isolation level, pass in the desired level as the first argument:
```js
const { Transaction } = require('sequelize');
await sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
}, async (t) => {
// Your code
});
```
You can also overwrite the `isolationLevel` setting globally with an option in the Sequelize constructor:
```js
const { Sequelize, Transaction } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:', {
isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
```
**Note for MSSQL:** _The `SET ISOLATION LEVEL` queries are not logged since the specified `isolationLevel` is passed directly to `tedious`._
## Usage with other sequelize methods
The `transaction` option goes with most other options, which are usually the first argument of a method.
For methods that take values, like `.create`, `.update()`, etc. `transaction` should be passed to the option in the second argument.
If unsure, refer to the API documentation for the method you are using to be sure of the signature.
Examples:
```js
await User.create({ name: 'Foo Bar' }, { transaction: t });
await User.findAll({
where: {
name: 'Foo Bar'
},
transaction: t
});
```
## The `afterCommit` hook
A `transaction` object allows tracking if and when it is committed.
An `afterCommit` hook can be added to both managed and unmanaged transaction objects:
```js
// Managed transaction:
await sequelize.transaction(async (t) => {
t.afterCommit(() => {
// Your logic
});
});
// Unmanaged transaction:
const t = await sequelize.transaction();
t.afterCommit(() => {
// Your logic
});
await t.commit();
```
The callback passed to `afterCommit` can be `async`. In this case:
* For a managed transaction: the `sequelize.transaction` call will wait for it before settling;
* For an unmanaged transaction: the `t.commit` call will wait for it before settling.
Notes:
* The `afterCommit` hook is not raised if the transaction is rolled back;
* The `afterCommit` hook does not modify the return value of the transaction (unlike most hooks)
You can use the `afterCommit` hook in conjunction with model hooks to know when a instance is saved and available outside of a transaction
```js
User.afterSave((instance, options) => {
if (options.transaction) {
// Save done within a transaction, wait until transaction is committed to
// notify listeners the instance has been saved
options.transaction.afterCommit(() => /* Notify */)
return;
}
// Save done outside a transaction, safe for callers to fetch the updated model
// Notify
});
```
## Locks
Queries within a `transaction` can be performed with locks:
```js
return User.findAll({
limit: 1,
lock: true,
transaction: t1
});
```
Queries within a transaction can skip locked rows:
```js
return User.findAll({
limit: 1,
lock: true,
skipLocked: true,
transaction: t2
});
```
...@@ -173,13 +173,10 @@ const MyDefineModel = <MyModelStatic>sequelize.define('MyDefineModel', { ...@@ -173,13 +173,10 @@ const MyDefineModel = <MyModelStatic>sequelize.define('MyDefineModel', {
} }
}); });
function stuffTwo() { async function stuffTwo() {
MyDefineModel.findByPk(1, { const myModel = await MyDefineModel.findByPk(1, {
rejectOnEmpty: true, rejectOnEmpty: true,
})
.then(myModel => {
console.log(myModel.id);
}); });
console.log(myModel.id);
} }
``` ```
# Upgrade to v6 # Upgrade to v6
Sequelize v6 is the next major release after v5 Sequelize v6 is the next major release after v5. Below is a list of breaking changes to help you upgrade.
## Breaking Changes ## Breaking Changes
### Support for Node 10 and up ### Support for Node 10 and up
Sequelize v6 will only support Node 10 and up [#10821](https://github.com/sequelize/sequelize/issues/10821) Sequelize v6 will only support Node 10 and up [#10821](https://github.com/sequelize/sequelize/issues/10821).
### CLS ### CLS
...@@ -20,28 +20,28 @@ You should now use [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) packag ...@@ -20,28 +20,28 @@ You should now use [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) packag
Sequelize.useCLS(namespace); Sequelize.useCLS(namespace);
``` ```
Bluebird [now supports](https://github.com/petkaantonov/bluebird/issues/1403) `async_hooks`. This configuration will automatically be enabled when invoking `Sequelize.useCLS`. Thus all promises should maintain CLS context without `cls-bluebird` patching. [Bluebird now supports `async_hooks`](https://github.com/petkaantonov/bluebird/issues/1403). This configuration will automatically be enabled when invoking `Sequelize.useCLS`. This way, using [`cls-bluebird`](https://www.npmjs.com/package/cls-bluebird) is no longer necessary.
### Model ### Model
**`options.returning`** #### `options.returning`
Option `returning: true` will no longer return attributes that are not defined in the model. Old behavior can be restored by using `returning: ['*']` Option `returning: true` will no longer return attributes that are not defined in the model. Old behavior can be achieved by using `returning: ['*']` instead.
**`Model.changed()`** #### `Model.changed()`
This method now tests for equality with `_.isEqual` and is now deep aware. Modifying nested value for JSON object won't mark them as changed, because it is still the same object. This method now tests for equality with [`_.isEqual`](https://lodash.com/docs/4.17.15#isEqual) and is now deep aware for JSON objects. Modifying a nested value for a JSON object won't mark it as changed (since it is still the same object).
```js ```js
const instance = await MyModel.findOne(); const instance = await MyModel.findOne();
instance.myJsonField.a = 1; instance.myJsonField.someProperty = 12345; // Changed from something else to 12345
console.log(instance.changed()); // logs `false` console.log(instance.changed()); // false
await instance.save(); // this will not save anything await instance.save(); // this will not save anything
instance.changed('myJsonField', true); instance.changed('myJsonField', true);
console.log(instance.changed()); // logs `["myJsonField"]` console.log(instance.changed()); // ['myJsonField']
await instance.save(); // will save await instance.save(); // will save
``` ```
......
# Raw queries
As there are often use cases in which it is just easier to execute raw / already prepared SQL queries, you can use the function `sequelize.query`.
By default the function will return two arguments - a results array, and an object containing metadata (affected rows etc.). Note that since this is a raw query, the metadata (property names etc.) is dialect specific. Some dialects return the metadata "within" the results object (as properties on an array). However, two arguments will always be returned, but for MSSQL and MySQL it will be two references to the same object.
```js
sequelize.query("UPDATE users SET y = 42 WHERE x = 12").then(([results, metadata]) => {
// Results will be an empty array and metadata will contain the number of affected rows.
})
```
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
```js
sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT})
.then(users => {
// We don't need spread here, since only the results will be returned for select queries
})
```
Several other query types are available. [Peek into the source for details](https://github.com/sequelize/sequelize/blob/master/lib/query-types.js)
A second option is the model. If you pass a model the returned data will be instances of that model.
```js
// Callee is the model definition. This allows you to easily map a query to a predefined model
sequelize
.query('SELECT * FROM projects', {
model: Projects,
mapToModel: true // pass true here if you have any mapped fields
})
.then(projects => {
// Each record will now be an instance of Project
})
```
See more options in the [query API reference](../class/lib/sequelize.js~Sequelize.html#instance-method-query). Some examples below:
```js
sequelize.query('SELECT 1', {
// A function (or false) for logging your queries
// Will get called for every SQL query that gets sent
// to the server.
logging: console.log,
// If plain is true, then sequelize will only return the first
// record of the result set. In case of false it will return all records.
plain: false,
// Set this to true if you don't have a model definition for your query.
raw: false,
// The type of query you are executing. The query type affects how results are formatted before they are passed back.
type: Sequelize.QueryTypes.SELECT
})
// Note the second argument being null!
// Even if we declared a callee here, the raw: true would
// supersede and return a raw object.
sequelize
.query('SELECT * FROM projects', { raw: true })
.then(projects => {
console.log(projects)
})
```
## "Dotted" attributes
If an attribute name of the table contains dots, the resulting objects will be nested. This is due to the usage of [dottie.js](https://github.com/mickhansen/dottie.js/) under the hood. See below:
```js
sequelize.query('select 1 as `foo.bar.baz`').then(rows => {
console.log(JSON.stringify(rows))
})
```
```json
[{
"foo": {
"bar": {
"baz": 1
}
}
}]
```
## Replacements
Replacements in a query can be done in two different ways, either using named parameters (starting with `:`), or unnamed, represented by a `?`. Replacements are passed in the options object.
* If an array is passed, `?` will be replaced in the order that they appear in the array
* If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice versa, an exception will be thrown.
```js
sequelize.query('SELECT * FROM projects WHERE status = ?',
{ replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
sequelize.query('SELECT * FROM projects WHERE status = :status ',
{ replacements: { status: 'active' }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
```
Array replacements will automatically be handled, the following query searches for projects where the status matches an array of values.
```js
sequelize.query('SELECT * FROM projects WHERE status IN(:status) ',
{ replacements: { status: ['active', 'inactive'] }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
```
To use the wildcard operator %, append it to your replacement. The following query matches users with names that start with 'ben'.
```js
sequelize.query('SELECT * FROM users WHERE name LIKE :search_name ',
{ replacements: { search_name: 'ben%' }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
```
## Bind Parameter
Bind parameters are like replacements. Except replacements are escaped and inserted into the query by sequelize before the query is sent to the database, while bind parameters are sent to the database outside the SQL query text. A query can have either bind parameters or replacements. Bind parameters are referred to by either $1, $2, ... (numeric) or $key (alpha-numeric). This is independent of the dialect.
* If an array is passed, `$1` is bound to the 1st element in the array (`bind[0]`)
* If an object is passed, `$key` is bound to `object['key']`. Each key must begin with a non-numeric char. `$1` is not a valid key, even if `object['1']` exists.
* In either case `$$` can be used to escape a literal `$` sign.
The array or object must contain all bound values or Sequelize will throw an exception. This applies even to cases in which the database may ignore the bound parameter.
The database may add further restrictions to this. Bind parameters cannot be SQL keywords, nor table or column names. They are also ignored in quoted text or data. In PostgreSQL it may also be needed to typecast them, if the type cannot be inferred from the context `$1::varchar`.
```js
sequelize.query('SELECT *, "text with literal $$1 and literal $$status" as t FROM projects WHERE status = $1',
{ bind: ['active'], type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
sequelize.query('SELECT *, "text with literal $$1 and literal $$status" as t FROM projects WHERE status = $status',
{ bind: { status: 'active' }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
```
# Transactions
Sequelize supports two ways of using transactions:
1. **Managed**, One which will automatically commit or rollback the transaction based on the result of a promise chain and, (if CLS enabled) pass the transaction to all calls within the callback
2. **Unmanaged**, One which leaves committing, rolling back and passing the transaction to the user
The key difference is that the managed transaction uses a callback that expects a promise to be returned to it while the unmanaged transaction returns a promise.
## Managed transaction (auto-callback)
Managed transactions handle committing or rolling back the transaction automatically. You start a managed transaction by passing a callback to `sequelize.transaction`.
Notice how the callback passed to `transaction` returns a promise chain, and does not explicitly call `t.commit()` nor `t.rollback()`. If all promises in the returned chain are resolved successfully the transaction is committed. If one or several of the promises are rejected, the transaction is rolled back.
```js
return sequelize.transaction(t => {
// chain all your queries here. make sure you return them.
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(user => {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});
}).then(result => {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback
}).catch(err => {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback
});
```
### Throw errors to rollback
When using the managed transaction you should _never_ commit or rollback the transaction manually. If all queries are successful, but you still want to rollback the transaction (for example because of a validation failure) you should throw an error to break and reject the chain:
```js
return sequelize.transaction(t => {
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(user => {
// Woops, the query was successful but we still want to roll back!
throw new Error();
});
});
```
### Automatically pass transactions to all queries
In the examples above, the transaction is still manually passed, by passing `{ transaction: t }` as the second argument. To automatically pass the transaction to all queries you must install the [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) (CLS) module and instantiate a namespace in your own code:
```js
const cls = require('cls-hooked');
const namespace = cls.createNamespace('my-very-own-namespace');
```
To enable CLS you must tell sequelize which namespace to use by using a static method of the sequelize constructor:
```js
const Sequelize = require('sequelize');
Sequelize.useCLS(namespace);
new Sequelize(....);
```
Notice, that the `useCLS()` method is on the *constructor*, not on an instance of sequelize. This means that all instances will share the same namespace, and that CLS is all-or-nothing - you cannot enable it only for some instances.
CLS works like a thread-local storage for callbacks. What this means in practice is that different callback chains can access local variables by using the CLS namespace. When CLS is enabled sequelize will set the `transaction` property on the namespace when a new transaction is created. Since variables set within a callback chain are private to that chain several concurrent transactions can exist at the same time:
```js
sequelize.transaction((t1) => {
namespace.get('transaction') === t1; // true
});
sequelize.transaction((t2) => {
namespace.get('transaction') === t2; // true
});
```
In most case you won't need to access `namespace.get('transaction')` directly, since all queries will automatically look for a transaction on the namespace:
```js
sequelize.transaction((t1) => {
// With CLS enabled, the user will be created inside the transaction
return User.create({ name: 'Alice' });
});
```
## Concurrent/Partial transactions
You can have concurrent transactions within a sequence of queries or have some of them excluded from any transactions. Use the `{transaction: }` option to control which transaction a query belong to:
**Warning:** _SQLite does not support more than one transaction at the same time._
### With CLS enabled
```js
sequelize.transaction((t1) => {
return sequelize.transaction((t2) => {
// With CLS enable, queries here will by default use t2
// Pass in the `transaction` option to define/alter the transaction they belong to.
return Promise.all([
User.create({ name: 'Bob' }, { transaction: null }),
User.create({ name: 'Mallory' }, { transaction: t1 }),
User.create({ name: 'John' }) // this would default to t2
]);
});
});
```
## Isolation levels
The possible isolations levels to use when starting a transaction:
```js
Sequelize.Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED // "READ UNCOMMITTED"
Sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED // "READ COMMITTED"
Sequelize.Transaction.ISOLATION_LEVELS.REPEATABLE_READ // "REPEATABLE READ"
Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE // "SERIALIZABLE"
```
By default, sequelize uses the isolation level of the database. If you want to use a different isolation level, pass in the desired level as the first argument:
```js
return sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
}, (t) => {
// your transactions
});
```
The `isolationLevel` can either be set globally when initializing the Sequelize instance or
locally for every transaction:
```js
// globally
new Sequelize('db', 'user', 'pw', {
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
// locally
sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
```
**Note:** _The SET ISOLATION LEVEL queries are not logged in case of MSSQL as the specified isolationLevel is passed directly to tedious_
## Unmanaged transaction (then-callback)
Unmanaged transactions force you to manually rollback or commit the transaction. If you don't do that, the transaction will hang until it times out. To start an unmanaged transaction, call `sequelize.transaction()` without a callback (you can still pass an options object) and call `then` on the returned promise. Notice that `commit()` and `rollback()` returns a promise.
```js
return sequelize.transaction().then(t => {
return User.create({
firstName: 'Bart',
lastName: 'Simpson'
}, {transaction: t}).then(user => {
return user.addSibling({
firstName: 'Lisa',
lastName: 'Simpson'
}, {transaction: t});
}).then(() => {
return t.commit();
}).catch((err) => {
return t.rollback();
});
});
```
## Usage with other sequelize methods
The `transaction` option goes with most other options, which are usually the first argument of a method.
For methods that take values, like `.create`, `.update()`, etc. `transaction` should be passed to the option in the second argument.
If unsure, refer to the API documentation for the method you are using to be sure of the signature.
## After commit hook
A `transaction` object allows tracking if and when it is committed.
An `afterCommit` hook can be added to both managed and unmanaged transaction objects:
```js
sequelize.transaction(t => {
t.afterCommit((transaction) => {
// Your logic
});
});
sequelize.transaction().then(t => {
t.afterCommit((transaction) => {
// Your logic
});
return t.commit();
})
```
The function passed to `afterCommit` can optionally return a promise that will resolve before the promise chain
that created the transaction resolves
`afterCommit` hooks are _not_ raised if a transaction is rolled back
`afterCommit` hooks do _not_ modify the return value of the transaction, unlike standard hooks
You can use the `afterCommit` hook in conjunction with model hooks to know when a instance is saved and available outside
of a transaction
```js
model.afterSave((instance, options) => {
if (options.transaction) {
// Save done within a transaction, wait until transaction is committed to
// notify listeners the instance has been saved
options.transaction.afterCommit(() => /* Notify */)
return;
}
// Save done outside a transaction, safe for callers to fetch the updated model
// Notify
})
```
## Locks
Queries within a `transaction` can be performed with locks
```js
return User.findAll({
limit: 1,
lock: true,
transaction: t1
})
```
Queries within a transaction can skip locked rows
```js
return User.findAll({
limit: 1,
lock: true,
skipLocked: true,
transaction: t2
})
```
{
"manual/dialects.html": "dialect-specific-things.html",
"manual/instances.html": "model-instances.html"
}
\ No newline at end of file
'use strict';
const jetpack = require('fs-jetpack');
const redirectMap = require('./../redirects.json');
function makeBoilerplate(url) {
return `
<!DOCTYPE html>
<meta charset="utf-8">
<title>Redirecting...</title>
<meta http-equiv="refresh" content="0; URL=${url}">
<link rel="canonical" href="${url}">
`;
}
for (const source of Object.keys(redirectMap)) {
jetpack.write(`esdoc/${source}`, makeBoilerplate(redirectMap[source]));
}
\ No newline at end of file
...@@ -3,10 +3,24 @@ ...@@ -3,10 +3,24 @@
const _ = require('lodash'); const _ = require('lodash');
const manualGroups = require('./../manual-groups.json'); const manualGroups = require('./../manual-groups.json');
module.exports = function transform($) { function extractFileNameFromPath(path) {
const listItems = $('nav div.manual-toc-root div[data-ice=manual]'); if (/\.\w+$/.test(path)) {
return /([^/]*)\.\w+$/.exec(path)[1];
}
return /[^/]*$/.exec(path)[0];
}
$(listItems.get(0)).before(` const hiddenManualNames = manualGroups.__hidden__.map(extractFileNameFromPath);
function isLinkToHiddenManual(link) {
const linkTargetName = extractFileNameFromPath(link);
return hiddenManualNames.includes(linkTargetName);
}
module.exports = function transform($, filePath) {
const sidebarManualDivs = $('nav div.manual-toc-root div[data-ice=manual]');
$(sidebarManualDivs.get(0)).before(`
<div class='manual-group'> <div class='manual-group'>
<a href='index.html' style='color: black'>Home</a> <a href='index.html' style='color: black'>Home</a>
</div> </div>
...@@ -14,11 +28,28 @@ module.exports = function transform($) { ...@@ -14,11 +28,28 @@ module.exports = function transform($) {
let count = 0; let count = 0;
_.each(manualGroups, (manuals, groupName) => { _.each(manualGroups, (manuals, groupName) => {
$(listItems.get(count)).before(` if (groupName !== '__hidden__') {
<div class='manual-group' style='pointer-events: none'> const groupTitleElement = $(`<div class="manual-group no-mouse">${groupName}</div>`);
${groupName} $(sidebarManualDivs.get(count)).before(groupTitleElement);
</div> }
`);
count += manuals.length; count += manuals.length;
}); });
// Remove links to hidden manuals
sidebarManualDivs.each(/* @this */ function() {
const link = $(this).find('li.indent-h1').data('link');
if (isLinkToHiddenManual(link)) {
$(this).remove();
}
});
// Remove previews for hidden manuals in index.html
if (filePath.endsWith('index.html') && $('div.manual-cards').length > 0) {
$('div.manual-card-wrap').each(/* @this */ function() {
const link = $(this).find('a').attr('href');
if (isLinkToHiddenManual(link)) {
$(this).remove();
}
});
}
}; };
\ No newline at end of file
...@@ -834,10 +834,6 @@ class Model { ...@@ -834,10 +834,6 @@ class Model {
* The table columns are defined by the hash that is given as the first argument. * The table columns are defined by the hash that is given as the first argument.
* Each attribute of the hash represents a column. * Each attribute of the hash represents a column.
* *
* For more about <a href="/manual/tutorial/models-definition.html#validations"/>Validations</a>
*
* More examples, <a href="/manual/tutorial/models-definition.html"/>Model Definition</a>
*
* @example * @example
* Project.init({ * Project.init({
* columnA: { * columnA: {
...@@ -860,9 +856,13 @@ class Model { ...@@ -860,9 +856,13 @@ class Model {
* sequelize.models.modelName // The model will now be available in models under the class name * sequelize.models.modelName // The model will now be available in models under the class name
* *
* @see * @see
* {@link DataTypes} * <a href="/master/manual/model-basics.html">Model Basics</a> guide
*
* @see * @see
* {@link Hooks} * <a href="/master/manual/model-basics.html">Hooks</a> guide
*
* @see
* <a href="/master/manual/validations-and-constraints.html"/>Validations & Constraints</a> guide
* *
* @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: * @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:
* @param {string|DataTypes|Object} attributes.column The description of a database column * @param {string|DataTypes|Object} attributes.column The description of a database column
...@@ -1667,7 +1667,7 @@ class Model { ...@@ -1667,7 +1667,7 @@ class Model {
* @param {number} [options.limit] Limit for result * @param {number} [options.limit] Limit for result
* @param {number} [options.offset] Offset for result * @param {number} [options.offset] Offset for result
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {string|Object} [options.lock] Lock the selected rows. Possible options are transaction.LOCK.UPDATE and transaction.LOCK.SHARE. Postgres also supports transaction.LOCK.KEY_SHARE, transaction.LOCK.NO_KEY_UPDATE and specific model locks with joins. See [transaction.LOCK for an example](transaction#lock) * @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.
* @param {boolean} [options.skipLocked] Skip locked rows. Only supported in Postgres. * @param {boolean} [options.skipLocked] Skip locked rows. Only supported in Postgres.
* @param {boolean} [options.raw] Return raw result. See sequelize.query for more information. * @param {boolean} [options.raw] Return raw result. See sequelize.query for more information.
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
...@@ -3826,10 +3826,13 @@ class Model { ...@@ -3826,10 +3826,13 @@ class Model {
} }
/** /**
* Validate this instance, and if the validation passes, persist it to the database. It will only save changed fields, and do nothing if no fields have changed. * Validates this instance, and if the validation passes, persists it to the database.
*
* 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).
*
* This method is optimized to perform an UPDATE only into the fields that changed. If nothing has changed, no SQL query will be performed.
* *
* On success, the callback will be called with this instance. On validation error, the callback will be called with an instance of `Sequelize.ValidationError`. * 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.
* This error will have a property for each of the fields for which validation failed, with the error message for that field.
* *
* @param {Object} [options] save options * @param {Object} [options] save options
* @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved. * @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
......
...@@ -392,9 +392,8 @@ class Sequelize { ...@@ -392,9 +392,8 @@ class Sequelize {
* *
* @see * @see
* {@link Model.init} for a more comprehensive specification of the `options` and `attributes` objects. * {@link Model.init} for a more comprehensive specification of the `options` and `attributes` objects.
* @see <a href="/manual/tutorial/models-definition.html">Model definition</a> Manual related to model definition
* @see * @see
* {@link DataTypes} For a list of possible data types * <a href="/master/manual/model-basics.html">Model Basics</a> guide
* *
* @returns {Model} Newly defined model * @returns {Model} Newly defined model
* *
......
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
"test-docker": "npm run test-docker-unit && npm run test-docker-integration", "test-docker": "npm run test-docker-unit && npm run test-docker-integration",
"test-docker-unit": "npm run test-unit", "test-docker-unit": "npm run test-unit",
"test-docker-integration": "env-cmd $npm_package_options_env_cmd npm run test-integration", "test-docker-integration": "env-cmd $npm_package_options_env_cmd npm run test-integration",
"docs": "esdoc -c docs/esdoc-config.js && node docs/run-docs-transforms.js && cp docs/favicon.ico esdoc/favicon.ico && cp docs/ROUTER.txt esdoc/ROUTER", "docs": "rimraf esdoc && esdoc -c docs/esdoc-config.js && cp docs/favicon.ico esdoc/favicon.ico && cp docs/ROUTER.txt esdoc/ROUTER && node docs/run-docs-transforms.js && node docs/redirects/create-redirects.js",
"teaser": "node scripts/teaser", "teaser": "node scripts/teaser",
"test-unit": "mocha --require scripts/mocha-bootload --globals setImmediate,clearImmediate --exit --check-leaks --colors -t 30000 --reporter spec \"test/unit/**/*.js\"", "test-unit": "mocha --require scripts/mocha-bootload --globals setImmediate,clearImmediate --exit --check-leaks --colors -t 30000 --reporter spec \"test/unit/**/*.js\"",
"test-unit-mariadb": "cross-env DIALECT=mariadb npm run test-unit", "test-unit-mariadb": "cross-env DIALECT=mariadb npm run test-unit",
......
...@@ -2620,11 +2620,13 @@ export abstract class Model<T = any, T2 = any> extends Hooks { ...@@ -2620,11 +2620,13 @@ export abstract class Model<T = any, T2 = any> extends Hooks {
public previous<K extends keyof this>(key: K): this[K]; public previous<K extends keyof this>(key: K): this[K];
/** /**
* Validate this instance, and if the validation passes, persist it to the database. * Validates this instance, and if the validation passes, persists it to the database.
* *
* On success, the callback will be called with this instance. On validation error, the callback will be * 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).
* called with an instance of `Sequelize.ValidationError`. This error will have a property for each of the *
* fields for which validation failed, with the error message for that field. * This method is optimized to perform an UPDATE only into the fields that changed. If nothing has changed, no SQL query will be performed.
*
* 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.
*/ */
public save(options?: SaveOptions): Promise<this>; public save(options?: SaveOptions): Promise<this>;
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!