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

Commit 4615d7f7 by Sushant Committed by GitHub

docs(migration): explain how to use migrations (#8319)

* docs(migration): explain how to use migrations

* docs(migration): storage, .sequelizerc

* docs(migrations): dynamic config, storage, ssl, url option, using env varibales

* docs(migrations): seeds and production tips

* docs: simplify queryInterface intro

* docs: QueryInterface API + seeding docs + styling

* docs: expose describe table to public
1 parent 0af04063
...@@ -15,10 +15,16 @@ ...@@ -15,10 +15,16 @@
color: #ffffff; color: #ffffff;
} }
.manual-toc-root ul li a { .manual-toc-root ul li {
margin-top: 0.5em;
margin-bottom: 0.5em;
font-weight: normal !important; font-weight: normal !important;
} }
.manual-toc .indent-h1 {
margin: 0.5em 0 0 .5em;
}
/* Class Summay*/ /* Class Summay*/
.summary thead tr { .summary thead tr {
background-color: #074278; background-color: #074278;
......
# Migrations # Migrations
Sequelize `2.0.0` introduces a new CLI which is based on [gulp][0] and combines [sequelize-cli][1] and [gulp-sequelize][2]. The CLI ships support for migrations and project bootstrapping. With migrations you can transfer your existing database into another state and vice versa: Those state transitions are saved in migration files, which describe the way how to get to the new state and how to revert the changes in order to get back to the old state. Just like you use Git / SVN to manage changes in your source code, you can use migrations to keep track of changes to database. With migrations you can transfer your existing database into another state and vice versa: Those state transitions are saved in migration files, which describe the way how to get to the new state and how to revert the changes in order to get back to the old state.
You will need [Sequelize CLI][0]. The CLI ships support for migrations and project bootstrapping.
## The CLI ## The CLI
In order to use the CLI you need to install the respective package: ### Installing CLI
Lets start with installing CLI, you can find instructions [here][0]. Most preferred way is installing locally like this
```bash ```bash
$ npm install --save sequelize-cli $ npm install --save sequelize-cli
``` ```
As with any npm package, you can use the global flag (`-g`) to install the CLI globally. If you have installed the CLI without the global flag, use `node_modules/.bin/sequelize [command]` instead of `sequelize [command]`. ### Bootstrapping
To create an empty project you will need to execute `init` command
```bash
$ node_modules/.bin/sequelize init
```
This will create following folders
1) `config`, contains config file, which tells CLI how to connect with database
2) `models`, contains all models for your project
3) `migrations`, contains all migration files
4) `seeders`, contains all seed files
#### Configuration
Before continuing further we will need to tell CLI how to connect to database. To do that lets open default config file `config/config.json`. It looks something like this
```json
{
development: {
username: 'root',
password: null,
database: 'database_development',
host: '127.0.0.1',
dialect: 'mysql'
},
test: {
username: 'root',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: 'root',
password: null,
database: 'database_production',
host: '127.0.0.1',
dialect: 'mysql'
}
}
```
Now edit this file and set correct database credentials and dialect.
**Note:** _If your database doesn't exists yet, you can just call `db:create` command. With proper access it will create that database for you._
### Creating first Model (and Migration)
Once you have properly configured CLI config file you are ready to create you first migration. Its as simple as executing a simple command.
We will use `model:generate` command. This command requires two options
The CLI currently supports the following commands: 1) `name`, Name of the model
2) `attributes`, List of model attributes
Lets create a model named `User`
```bash
$ node_modules/.bin/sequelize model:generate --name User --attributes firstName:string,lastName:string,email:string
```
This will do following
1) Create a model file `user` in `models` folder
2) Create a migration file with name like `XXXXXXXXXXXXXX-create-user.js` in `migrations` folder
**Note:** _Sequelize will only use Model files, its the table representation. On other hand migration file is a change in that model or more specifically that table, used by CLI. Treat migrations like a commit or a log for some change in database._
### Running Migrations
Now till this step CLI haven't inserted anything into database. We have just created required model and migration files for our first model `User`. Now to actually create that table in database you need to run `db:migrate` command.
```bash ```bash
$ sequelize db:migrate # Run pending migrations. $ node_modules/.bin/sequelize db:migrate
$ sequelize db:migrate:undo # Revert the last migration run.
$ sequelize help # Display this help text.
$ sequelize init # Initializes the project.
$ sequelize migration:create # Generates a new migration file.
$ sequelize version # Prints the version number.
``` ```
Further and more detailed information about the available commands This command will execute these steps
can be obtained via the help command:
1) It will ensure a table called `SequelizeMeta` in database. This table is used to record which migration have ran on current database
2) Start looking for any migration files which haven't ran yet. This is possible by checking `SequelizeMeta` table. In this case it will run `XXXXXXXXXXXXXX-create-user.js` migration, which we created in last step.
3) Creates a table called `User` with all columns as specified in its migration file.
### Undoing Migrations
Now our table has been created and saved in database. With migration you can revert to old state by just running a command.
You can use `db:migrate:undo`, this command will revert most recent migration.
```bash ```bash
$ sequelize help:init $ node_modules/.bin/sequelize db:migrate:undo
$ sequelize help:db:migrate
$ sequelize help:db:migrate:undo
# etc
``` ```
The latter one for example will print out the following output: You can revert back to initial state by undoing all migrations with `db:migrate:undo:all` command. You can also revert back to a specific migration by passing its name in `--to` option.
```bash ```bash
Sequelize [CLI: v0.0.2, ORM: v1.7.5] $ node_modules/.bin/sequelize db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
```
COMMANDS ### Creating First Seed
sequelize db:migrate:undo -- Revert the last migration run. Suppose we want to insert some data into few tables by default. If we follow up on previous example we can consider creating a demo user for `User` table.
DESCRIPTION To manage all data migrations you can use seeders. Seed files are some change in data that can be used to populate database table with sample data or test data.
Revert the last migration run.
OPTIONS Lets create a seed file which will add a demo user to our `User` table.
--env The environment to run the command in. Default: development
--options-path The path to a JSON file with additional options. Default: none ```bash
--coffee Enables coffee script support. Default: false $ node_modules/.bin/sequelize seed:generate --name demo-user
--config The path to the config file. Default: config/config.json
``` ```
## Skeleton This command will create a seed file in `seeders` folder. File name will look something like `XXXXXXXXXXXXXX-demo-user.js`, It follows same `up / down` semantics like migration files.
The following skeleton shows a typical migration file. All migrations are expected to be located in a folder called `migrations` at the very top of the project. The sequelize binary can generate a migration skeleton. See the above section for more details. Now we should edit this file to insert demo user to `User` table.
```js ```js
'use strict';
module.exports = { module.exports = {
up: (queryInterface, Sequelize) => { up: (queryInterface, Sequelize) => {
// logic for transforming into the new state return queryInterface.bulkInsert('User', [{
firstName: 'John',
lastName: 'Doe',
email: 'demo@demo.com'
}], {});
}, },
 
down: (queryInterface, Sequelize) => { down: (queryInterface, Sequelize) => {
// logic for reverting the changes return queryInterface.bulkDelete('User', null, {});
} }
} };
``` ```
The passed `queryInterface` object can be used to modify the database. The `Sequelize` object stores the available data types such as `STRING` or `INTEGER`. Function `up` or `down` should return a `Promise`. Here is some code: ### Running Seeds
In last step you have create a seed file. Its still not committed to database. To do that we need to run a simple command.
```js ```bash
module.exports = { $ node_modules/.bin/sequelize db:seed
up: (queryInterface, Sequelize) => {
return queryInterface.dropAllTables();
}
}
``` ```
The available methods of the queryInterface object are the following. This will execute that seed file and you will have a demo user inserted into `User` table.
## Functions **Note:** _Seeders execution is not stored anywhere unlike migration which use `SequelizeMeta` table. If you wish to override this please read `Storage` section_
Using the `queryInterface` object described before, you will have access to most of already introduced functions. Furthermore there are some other methods, which are designed to actually change the database schema. ### Undoing Seeds
Seeders if they are using any storage can be undo. There are two commands available for that
### createTable(tableName, attributes, options) If you wish to undo most recent seed
This method allows creation of new tables. It is allowed to pass simple or complex attribute definitions. You can define the encoding of the table and the table's engine via options ```bash
node_modules/.bin/sequelize db:seed:undo
```js
queryInterface.createTable(
'nameOfTheNewTable',
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
createdAt: {
type: Sequelize.DATE
},
updatedAt: {
type: Sequelize.DATE
},
attr1: Sequelize.STRING,
attr2: Sequelize.INTEGER,
attr3: {
type: Sequelize.BOOLEAN,
defaultValue: false,
allowNull: false
},
//foreign key usage
attr4: {
type: Sequelize.INTEGER,
references: {
model: 'another_table_name',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
}
},
{
engine: 'MYISAM', // default: 'InnoDB'
charset: 'latin1', // default: null
schema: 'public' // default: public, PostgreSQL only.
}
)
``` ```
### dropTable(tableName, options) If you wish to undo all seeds
This method allows deletion of an existing table. ```bash
node_modules/.bin/sequelize db:migrate:undo:all
```js
queryInterface.dropTable('nameOfTheExistingTable')
``` ```
### dropAllTables(options) ## Advance Topics
This method allows deletion of all existing tables in the database. ### Migration Skeleton
The following skeleton shows a typical migration file.
```js ```js
queryInterface.dropAllTables() module.exports = {
up: (queryInterface, Sequelize) => {
// logic for transforming into the new state
},
 
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
}
}
``` ```
### renameTable(before, after, options) The passed `queryInterface` object can be used to modify the database. The `Sequelize` object stores the available data types such as `STRING` or `INTEGER`. Function `up` or `down` should return a `Promise`. Lets look at an example
This method allows renaming of an existing table.
```js ```js
queryInterface.renameTable('Person', 'User') module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Person', {
name: Sequelize.STRING,
isBetaMember: {
type: Sequelize.BOOLEAN,
defaultValue: false,
allowNull: false
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
}
}
``` ```
### showAllTables(options) ### The `.sequelizerc` File
This is a special configuration file. It let you specify various options that you would usually pass as arguments to CLI. Some scenarios where you can use it.
This method returns the name of all existing tables in the database. 1) You want to override default path to `migrations`, `models`, `seeders` or `config` folder.
2) You want to rename `config.json` to something else like `database.json`
```js And a whole lot more. Let see how you can use this file for custom configuration.
queryInterface.showAllTables().then(tableNames => {})
```
### describeTable(tableName, options) For starters lets create an empty file in root directory of your project.
This method returns an array of hashes containing information about all attributes in the table. ```bash
$ touch .sequelizerc
```js
queryInterface.describeTable('Person').then(attributes => {
/*
attributes will be something like:
 
{
name: {
type: 'VARCHAR(255)', // this will be 'CHARACTER VARYING' for pg!
allowNull: true,
defaultValue: null
},
isBetaMember: {
type: 'TINYINT(1)', // this will be 'BOOLEAN' for pg!
allowNull: false,
defaultValue: false
}
}
*/
})
``` ```
### addColumn(tableNameOrOptions, attributeName, dataTypeOrOptions, options) Now lets work with an example config.
This method allows adding columns to an existing table. The data type can be simple or complex.
```js ```js
queryInterface.addColumn( const path = require('path');
'nameOfAnExistingTable',
'nameOfTheNewAttribute',
Sequelize.STRING
)
 
// or
 
queryInterface.addColumn(
'nameOfAnExistingTable',
'nameOfTheNewAttribute',
{
type: Sequelize.STRING,
allowNull: false
}
)
// or with first attribute to put the column at the beginning of the table module.exports = {
// currently supports only in MySQL 'config': path.resolve('config', 'database.json'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
'migrations-path': path.resolve('db', 'migrations')
}
```
queryInterface.addColumn( With this config you are telling CLI to
'nameOfAnExistingTable',
'nameOfTheNewAttribute',
{
type: Sequelize.STRING,
first: true
}
)
// or with after attribute to put the column after a specific column 1) Use `config/database.json` file for config settings
// currently supports only in MySQL 2) Use `db/models` as models folder
3) Use `db/seeders` as seeders folder
4) Use `db/migrations` as migrations folder
queryInterface.addColumn( ### Dynamic Configuration
'nameOfAnExistingTable', Configuration file is by default a JSON file called `config.json`. But sometimes you want to execute some code or access environment variables which is not possible in JSON files.
'nameOfTheNewAttribute',
{
type: Sequelize.STRING,
after: 'nameOfAnExistingColumn'
}
)
// or with an explicit schema: Sequelize CLI can read from both `JSON` and `JS` files. This can be setup with `.sequelizerc` file. Let see how
queryInterface.addColumn({ First you need to create a `.sequelizerc` file in root folder of your project. This file should override config path to a `JS` file. Like this
tableName: 'Person',
schema: 'public'
},
'signature',
Sequelize.STRING
)
```js
const path = require('path');
module.exports = {
'config': path.resolve('config', 'config.js')
}
``` ```
### removeColumn(tableNameOrOptions, attributeName, options) Now Sequelize CLI will load `config/config.js` for getting configuration options. Since this is a JS file you can have any code executed and export final dynamic configuration file.
This method allows deletion of a specific column of an existing table. An example of `config/config.js` file
```js ```js
queryInterface.removeColumn('Person', 'signature') const fs = require('fs');
// or with an explicit schema: module.exports = {
development: {
queryInterface.removeColumn({ username: 'database_dev',
tableName: 'Person', password: 'database_dev',
schema: 'public' database: 'database_dev',
}, 'signature'); host: '127.0.0.1',
dialect: 'mysql'
},
test: {
username: 'database_test',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOSTNAME,
dialect: 'mysql',
dialectOptions: {
ssl: {
ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
}
}
}
};
``` ```
### changeColumn(tableName, attributeName, dataTypeOrOptions, options) ### Using Environment Variables
With CLI you can directly access the environment variables inside the `config/config.js`. You can use `.sequelizerc` to tell CLI to use `config/config.js` for configuration. This is explained in last section.
This method changes the meta data of an attribute. It is possible to change the default value, allowance of null or the data type. Please make sure, that you are completely describing the new data type. Then you can just expose file with proper environment variables.
```js ```js
queryInterface.changeColumn( module.exports = {
'nameOfAnExistingTable', development: {
'nameOfAnExistingAttribute', username: 'database_dev',
{ password: 'database_dev',
type: Sequelize.FLOAT, database: 'database_dev',
allowNull: false, host: '127.0.0.1',
defaultValue: 0.0 dialect: 'mysql'
},
test: {
username: process.env.CI_DB_USERNAME,
password: process.env.CI_DB_PASSWORD,
database: process.env.CI_DB_NAME,
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOSTNAME,
dialect: 'mysql'
} }
)
``` ```
### renameColumn(tableName, attrNameBefore, attrNameAfter, options) ### Specifying Dialect Options
Sometime you want to specify a dialectOption, if its a general config you can just add it in `config/config.json`. Sometime you want to execute some code to get dialectOptions, you should use dynamic config file for those cases.
This methods allows renaming attributes.
```js ```json
queryInterface.renameColumn('Person', 'signature', 'sig') {
"production": {
"dialect":"mysql",
"dialectOptions": {
"bigNumberStrings": true
}
}
}
``` ```
### addIndex(tableName, attributes, options) ### Production Usages
Some tips around using CLI and migration setup in production environment.
This methods creates indexes for specific attributes of a table. The index name will be automatically generated if it is not passed via in the options (see below). 1) Use environment variables for config settings. This is better achieved with dynamic configuration. A sample production safe configuration may look like.
```js ```js
// This example will create the index person_firstname_lastname const fs = require('fs');
queryInterface.addIndex('Person', ['firstname', 'lastname'])
module.exports = {
// This example will create a unique index with the name SuperDuperIndex using the optional 'options' field. development: {
// Possible options: username: 'database_dev',
// - indicesType: UNIQUE|FULLTEXT|SPATIAL password: 'database_dev',
// - indexName: The name of the index. Default is __ database: 'database_dev',
// - parser: For FULLTEXT columns set your parser host: '127.0.0.1',
// - indexType: Set a type for the index, e.g. BTREE. See the documentation of the used dialect dialect: 'mysql'
// - logging: A function that receives the sql query, e.g. console.log },
// - where: A hash of attributes to limit your index(Filtered Indexes - MSSQL & PostgreSQL only) test: {
queryInterface.addIndex( username: 'database_test',
'Person', password: null,
['firstname', 'lastname'], database: 'database_test',
{ host: '127.0.0.1',
indexName: 'SuperDuperIndex', dialect: 'mysql'
indicesType: 'UNIQUE' },
} production: {
) username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
queryInterface.addIndex( database: process.env.DB_NAME,
'Person', host: process.env.DB_HOSTNAME,
['firstname', 'lastname'], dialect: 'mysql',
{ dialectOptions: {
where: { ssl: {
lastname: { ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
$ne: null
} }
} }
} }
) };
``` ```
### removeIndex(tableName, indexNameOrAttributes, options) Our goal is to use environment variables for various database secrets and not accidentally checkout them to source control.
This method deletes an existing index of a table. ### Storage
There are three types of storage that you can use: `sequelize`, `json`, and `none`.
```js - `sequelize` : stores migrations and seeds in a table on the sequelize database
queryInterface.removeIndex('Person', 'SuperDuperIndex') - `json` : stores migrations and seeds on a json file
  - `none` : does not store any migration/seed
// or
 
queryInterface.removeIndex('Person', ['firstname', 'lastname'])
```
### addConstraint(tableName, attributes, options)
This method adds a new constraint of the specified type.
- tableName - Name of the table to add the constraint on
- attributes - Array of column names to apply the constraint over
- options - An object to define the constraint name, type etc.
Avalable options:
- type - Type of constraint. One of the values in available constraints(case insensitive)
- name - Name of the constraint. If not specifed, sequelize automatically creates a named constraint based on constraint type, table & column names
- defaultValue - The value for the default constraint
- where - Where clause/expression for the CHECK constraint
- references - Object specifying target table, column name to create foreign key constraint
- references.table - Target table name or table
- references.field - Target column name
Available constraints:
- UNIQUE
- DEFAULT (MSSQL only)
- CHECK (MySQL - Ignored by the database engine )
- FOREIGN KEY
- PRIMARY KEY
```js #### Migration Storage
//UNIQUE By default the CLI will create a table in your database called `SequelizeMeta` containing an entry
queryInterface.addConstraint('Users', ['email'], { for each executed migration. To change this behavior, there are three options you can add to the
type: 'unique', configuration file. Using `migrationStorage`, you can choose the type of storage to be used for
name: 'custom_unique_constraint_name' migrations. If you choose `json`, you can specify the path of the file using `migrationStoragePath`
}); or the CLI will write to the file `sequelize-meta.json`. If you want to keep the information in the
database, using `sequelize`, but want to use a different table, you can change the table name using
//CHECK `migrationStorageTableName`.
queryInterface.addConstraint('Users', ['roles'], {
type: 'check',
where: {
roles: ['user', 'admin', 'moderator', 'guest']
}
});
//Default - MSSQL only
queryInterface.addConstraint('Users', ['roles'], {
type: 'default',
defaultValue: 'guest'
});
//Primary Key
queryInterface.addConstraint('Users', ['username'], {
type: 'primary key',
name: 'custom_primary_constraint_name'
});
//Foreign Key
queryInterface.addConstraint('Posts', ['username'], {
type: 'FOREIGN KEY',
name: 'custom_fkey_constraint_name',
references: { //Required field
table: 'target_table_name',
field: 'target_column_name'
},
onDelete: 'cascade',
onUpdate: 'cascade'
});
```
### removeConstraint(tableName, constraintName, options) ```json
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
This method deletes an existing constraint of a table // Use a different storage type. Default: sequelize
"migrationStorage": "json",
```js // Use a different file name. Default: sequelize-meta.json
queryInterface.removeConstraint('Users', 'my_constraint_name'); "migrationStoragePath": "sequelizeMeta.json",
// Use a different table name. Default: SequelizeMeta
"migrationStorageTableName": "sequelize_meta"
}
}
```
**Note:** _The `none` storage is not recommended as a migration storage. If you decide to use it, be
aware of the implications of having no record of what migrations did or didn't run._
#### Seed Storage
By default the CLI will not save any seed that is executed. If you choose to change this behavior (!),
you can use `seederStorage` in the configuration file to change the storage type. If you choose `json`,
you can specify the path of the file using `seederStoragePath` or the CLI will write to the file
`sequelize-data.json`. If you want to keep the information in the database, using `sequelize`, you can
specify the table name using `seederStorageTableName`, or it will default to `SequelizeData`.
```json
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// Use a different storage. Default: none
"seederStorage": "json",
// Use a different file name. Default: sequelize-data.json
"seederStoragePath": "sequelizeData.json",
// Use a different table name. Default: SequelizeData
"seederStorageTableName": "sequelize_data"
}
}
``` ```
### showConstraint(tableName, options) ### Configuration Connection String
As an alternative to the `--config` option with configuration files defining your database, you can
use the `--url` option to pass in a connection string. For example:
Lists all the constraints on the given table. ```bash
$ node_modules/.bin/sequelize db:migrate --url 'mysql://root:password@mysql_host.com/database_name'
```
```js ### Connecting over SSL
queryInterface.showConstraint('Users'); Ensure ssl is specified in both `dialectOptions` and in the base config.
// Returns array of objects/constraints
```json
{
"production": {
"dialect":"postgres",
"ssl": true,
"dialectOptions": {
"ssl": true
}
}
}
``` ```
## Programmatic use ### Programmatic use
Sequelize has a [sister library](https://github.com/sequelize/umzug) for programmatically handling execution and logging of migration tasks. Sequelize has a [sister library][1] for programmatically handling execution and logging of migration tasks.
## Query Interface
Using `queryInterface` object described before you can change database schema. To see full list of public methods it supports check [QueryInterface API][2]
[0]: http://gulpjs.com/ [0]: https://github.com/sequelize/cli
[1]: https://github.com/sequelize/cli [1]: https://github.com/sequelize/umzug
[2]: https://github.com/sequelize/gulp-sequelize [2]: /class/lib/query-interface.js~QueryInterface.html
...@@ -12,8 +12,8 @@ const QueryTypes = require('./query-types'); ...@@ -12,8 +12,8 @@ const QueryTypes = require('./query-types');
/** /**
* The interface that Sequelize uses to talk to all databases * The interface that Sequelize uses to talk to all databases
*
* @class QueryInterface * @class QueryInterface
* @private
*/ */
class QueryInterface { class QueryInterface {
constructor(sequelize) { constructor(sequelize) {
...@@ -21,18 +21,41 @@ class QueryInterface { ...@@ -21,18 +21,41 @@ class QueryInterface {
this.QueryGenerator = this.sequelize.dialect.QueryGenerator; this.QueryGenerator = this.sequelize.dialect.QueryGenerator;
} }
/**
* Creates a schema
*
* @param {String} schema Schema name to create
* @param {Object} [options] Query options
*
* @return {Promise}
*/
createSchema(schema, options) { createSchema(schema, options) {
options = options || {}; options = options || {};
const sql = this.QueryGenerator.createSchema(schema); const sql = this.QueryGenerator.createSchema(schema);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
} }
/**
* Drops a schema
*
* @param {String} schema Schema name to create
* @param {Object} [options] Query options
*
* @return {Promise}
*/
dropSchema(schema, options) { dropSchema(schema, options) {
options = options || {}; options = options || {};
const sql = this.QueryGenerator.dropSchema(schema); const sql = this.QueryGenerator.dropSchema(schema);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
} }
/**
* Drop all schemas
*
* @param {Object} [options] Query options
*
* @return {Promise}
*/
dropAllSchemas(options) { dropAllSchemas(options) {
options = options || {}; options = options || {};
...@@ -43,8 +66,14 @@ class QueryInterface { ...@@ -43,8 +66,14 @@ class QueryInterface {
} }
} }
/**
* Show all schemas
*
* @param {Object} [options] Query options
*
* @return {Promise<Array>}
*/
showAllSchemas(options) { showAllSchemas(options) {
options = _.assign({}, options, { options = _.assign({}, options, {
raw: true, raw: true,
type: this.sequelize.QueryTypes.SELECT type: this.sequelize.QueryTypes.SELECT
...@@ -57,6 +86,15 @@ class QueryInterface { ...@@ -57,6 +86,15 @@ class QueryInterface {
)); ));
} }
/**
* Returns database version
*
* @param {Object} [options] Query options
* @param {QueryType} [options.type] Query type
*
* @returns {Promise}
* @private
*/
databaseVersion(options) { databaseVersion(options) {
return this.sequelize.query( return this.sequelize.query(
this.QueryGenerator.versionQuery(), this.QueryGenerator.versionQuery(),
...@@ -64,6 +102,57 @@ class QueryInterface { ...@@ -64,6 +102,57 @@ class QueryInterface {
); );
} }
/**
* Create a table with given set of attributes
*
* ```js
* queryInterface.createTable(
* 'nameOfTheNewTable',
* {
* id: {
* type: Sequelize.INTEGER,
* primaryKey: true,
* autoIncrement: true
* },
* createdAt: {
* type: Sequelize.DATE
* },
* updatedAt: {
* type: Sequelize.DATE
* },
* attr1: Sequelize.STRING,
* attr2: Sequelize.INTEGER,
* attr3: {
* type: Sequelize.BOOLEAN,
* defaultValue: false,
* allowNull: false
* },
* //foreign key usage
* attr4: {
* type: Sequelize.INTEGER,
* references: {
* model: 'another_table_name',
* key: 'id'
* },
* onUpdate: 'cascade',
* onDelete: 'cascade'
* }
* },
* {
* engine: 'MYISAM', // default: 'InnoDB'
* charset: 'latin1', // default: null
* schema: 'public' // default: public, PostgreSQL only.
* }
* )
* ```
*
* @param {String} tableName Name of table to create
* @param {Array} attributes List of table attributes to create
* @param {Object} [options]
* @param {Model} [model]
*
* @return {Promise}
*/
createTable(tableName, attributes, options, model) { createTable(tableName, attributes, options, model) {
const keys = Object.keys(attributes); const keys = Object.keys(attributes);
const keyLen = keys.length; const keyLen = keys.length;
...@@ -170,6 +259,14 @@ class QueryInterface { ...@@ -170,6 +259,14 @@ class QueryInterface {
} }
} }
/**
* Drops a table from database
*
* @param {String} tableName Table name to drop
* @param {Object} options Query options
*
* @return {Promise}
*/
dropTable(tableName, options) { dropTable(tableName, options) {
// if we're forcing we should be cascading unless explicitly stated otherwise // if we're forcing we should be cascading unless explicitly stated otherwise
options = _.clone(options) || {}; options = _.clone(options) || {};
...@@ -205,8 +302,15 @@ class QueryInterface { ...@@ -205,8 +302,15 @@ class QueryInterface {
}); });
} }
/**
* Drop all tables from database
*
* @param {Object} [options]
* @param {Array} [options.skip] List of table to skip
*
* @return {Promise}
*/
dropAllTables(options) { dropAllTables(options) {
options = options || {}; options = options || {};
const skip = options.skip || []; const skip = options.skip || [];
...@@ -252,6 +356,14 @@ class QueryInterface { ...@@ -252,6 +356,14 @@ class QueryInterface {
}); });
} }
/**
* Drop all enums from database, Postgres Only
*
* @param {Object} options Query options
*
* @return {Promise}
* @private
*/
dropAllEnums(options) { dropAllEnums(options) {
if (this.sequelize.getDialect() !== 'postgres') { if (this.sequelize.getDialect() !== 'postgres') {
return Promise.resolve(); return Promise.resolve();
...@@ -265,18 +377,46 @@ class QueryInterface { ...@@ -265,18 +377,46 @@ class QueryInterface {
)); ));
} }
/**
* List all enums, Postgres Only
*
* @param {String} [tableName] Table whose enum to list
* @param {Object} [options] Query options
*
* @return {Promise}
* @private
*/
pgListEnums(tableName, options) { pgListEnums(tableName, options) {
options = options || {}; options = options || {};
const sql = this.QueryGenerator.pgListEnums(tableName); const sql = this.QueryGenerator.pgListEnums(tableName);
return this.sequelize.query(sql, _.assign({}, options, { plain: false, raw: true, type: QueryTypes.SELECT })); return this.sequelize.query(sql, _.assign({}, options, { plain: false, raw: true, type: QueryTypes.SELECT }));
} }
/**
* Renames a table
*
* @param {String} before Current name of table
* @param {String} after New name from table
* @param {Object} [options] Query options
*
* @return {Promise}
*/
renameTable(before, after, options) { renameTable(before, after, options) {
options = options || {}; options = options || {};
const sql = this.QueryGenerator.renameTableQuery(before, after); const sql = this.QueryGenerator.renameTableQuery(before, after);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
} }
/**
* Get all tables in current database
*
* @param {Object} [options] Query options
* @param {Boolean} [options.raw=true] Run query in raw mode
* @param {QueryType} [options.type=QueryType.SHOWTABLE]
*
* @return {Promise<Array>}
* @private
*/
showAllTables(options) { showAllTables(options) {
options = _.assign({}, options, { options = _.assign({}, options, {
raw: true, raw: true,
...@@ -287,6 +427,30 @@ class QueryInterface { ...@@ -287,6 +427,30 @@ class QueryInterface {
return this.sequelize.query(showTablesSql, options).then(tableNames => Utils._.flatten(tableNames)); return this.sequelize.query(showTablesSql, options).then(tableNames => Utils._.flatten(tableNames));
} }
/**
* Describe a table structure
*
* This method returns an array of hashes containing information about all attributes in the table.
*
* ```js
* {
* name: {
* type: 'VARCHAR(255)', // this will be 'CHARACTER VARYING' for pg!
* allowNull: true,
* defaultValue: null
* },
* isBetaMember: {
* type: 'TINYINT(1)', // this will be 'BOOLEAN' for pg!
* allowNull: false,
* defaultValue: false
* }
* }
* ```
* @param {String} tableName
* @param {Object} [options] Query options
*
* @return {Promise<Object>}
*/
describeTable(tableName, options) { describeTable(tableName, options) {
let schema = null; let schema = null;
let schemaDelimiter = null; let schemaDelimiter = null;
...@@ -320,6 +484,16 @@ class QueryInterface { ...@@ -320,6 +484,16 @@ class QueryInterface {
}); });
} }
/**
* Add a new column into a table
*
* @param {String} table Table to add column to
* @param {String} key Column name
* @param {Object} attribute Attribute definition
* @param {Object} [options] Query options
*
* @return {Promise}
*/
addColumn(table, key, attribute, options) { addColumn(table, key, attribute, options) {
if (!table || !key || !attribute) { if (!table || !key || !attribute) {
throw new Error('addColumn takes atleast 3 arguments (table, attribute name, attribute definition)'); throw new Error('addColumn takes atleast 3 arguments (table, attribute name, attribute definition)');
...@@ -330,6 +504,15 @@ class QueryInterface { ...@@ -330,6 +504,15 @@ class QueryInterface {
return this.sequelize.query(this.QueryGenerator.addColumnQuery(table, key, attribute), options); return this.sequelize.query(this.QueryGenerator.addColumnQuery(table, key, attribute), options);
} }
/**
* Remove a column from table
*
* @param {String} tableName Table to remove column from
* @param {String} attributeName Columns name to remove
* @param {Object} [options] Query options
*
* @return {Promise}
*/
removeColumn(tableName, attributeName, options) { removeColumn(tableName, attributeName, options) {
options = options || {}; options = options || {};
switch (this.sequelize.options.dialect) { switch (this.sequelize.options.dialect) {
...@@ -347,6 +530,16 @@ class QueryInterface { ...@@ -347,6 +530,16 @@ class QueryInterface {
} }
} }
/**
* Change a column definition
*
* @param {String} tableName Table name to change from
* @param {String} attributeName Column name
* @param {Object} dataTypeOrOptions Attribute definition for new column
* @param {Object} [options] Query options
*
* @return {Promise}
*/
changeColumn(tableName, attributeName, dataTypeOrOptions, options) { changeColumn(tableName, attributeName, dataTypeOrOptions, options) {
const attributes = {}; const attributes = {};
options = options || {}; options = options || {};
...@@ -370,6 +563,16 @@ class QueryInterface { ...@@ -370,6 +563,16 @@ class QueryInterface {
} }
} }
/**
* Rename a column
*
* @param {String} tableName Table name whose column to rename
* @param {String} attrNameBefore Current column name
* @param {String} attrNameAfter New column name
* @param {Object} [options] Query option
*
* @return {Promise}
*/
renameColumn(tableName, attrNameBefore, attrNameAfter, options) { renameColumn(tableName, attrNameBefore, attrNameAfter, options) {
options = options || {}; options = options || {};
return this.describeTable(tableName, options).then(data => { return this.describeTable(tableName, options).then(data => {
...@@ -407,6 +610,20 @@ class QueryInterface { ...@@ -407,6 +610,20 @@ class QueryInterface {
}); });
} }
/**
* Add index to a column
*
* @param {String} tableName Table name to add index on
* @param {Object} options
* @param {Array} options.fields List of attributes to add index on
* @param {Boolean} [options.unique] Create a unique index
* @param {String} [options.using] Useful for GIN indexes
* @param {String} [options.type] Type of index, available options are UNIQUE|FULLTEXT|SPATIAL
* @param {String} [options.name] Name of the index. Default is <table>_<attr1>_<attr2>
* @param {Object} [options.where] Where condition on index, for partial indexes
*
* @return {Promise}
*/
addIndex(tableName, attributes, options, rawTablename) { addIndex(tableName, attributes, options, rawTablename) {
// Support for passing tableName, attributes, options or tableName, options (with a fields param which is the attributes) // Support for passing tableName, attributes, options or tableName, options (with a fields param which is the attributes)
if (!Array.isArray(attributes)) { if (!Array.isArray(attributes)) {
...@@ -427,6 +644,15 @@ class QueryInterface { ...@@ -427,6 +644,15 @@ class QueryInterface {
return this.sequelize.query(sql, _.assign({}, options, { supportsSearchPath: false })); return this.sequelize.query(sql, _.assign({}, options, { supportsSearchPath: false }));
} }
/**
* Show indexes on a table
*
* @param {String} tableName
* @param {Object} [options] Query options
*
* @return {Promise<Array>}
* @private
*/
showIndex(tableName, options) { showIndex(tableName, options) {
const sql = this.QueryGenerator.showIndexesQuery(tableName, options); const sql = this.QueryGenerator.showIndexesQuery(tableName, options);
return this.sequelize.query(sql, _.assign({}, options, { type: QueryTypes.SHOWINDEXES })); return this.sequelize.query(sql, _.assign({}, options, { type: QueryTypes.SHOWINDEXES }));
...@@ -460,12 +686,91 @@ class QueryInterface { ...@@ -460,12 +686,91 @@ class QueryInterface {
}); });
} }
/**
* Remove an already existing index from a table
*
* @param {String} tableName Table name to drop index from
* @param {String} indexNameOrAttributes Index name
* @param {Object} [options] Query options
*
* @return {Promise}
*/
removeIndex(tableName, indexNameOrAttributes, options) { removeIndex(tableName, indexNameOrAttributes, options) {
options = options || {}; options = options || {};
const sql = this.QueryGenerator.removeIndexQuery(tableName, indexNameOrAttributes); const sql = this.QueryGenerator.removeIndexQuery(tableName, indexNameOrAttributes);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
} }
/**
* Add constraints to table
*
* Available constraints:
* - UNIQUE
* - DEFAULT (MSSQL only)
* - CHECK (MySQL - Ignored by the database engine )
* - FOREIGN KEY
* - PRIMARY KEY
*
* UNIQUE
* ```js
* queryInterface.addConstraint('Users', ['email'], {
* type: 'unique',
* name: 'custom_unique_constraint_name'
* });
* ```
*
* CHECK
* ```js
* queryInterface.addConstraint('Users', ['roles'], {
* type: 'check',
* where: {
* roles: ['user', 'admin', 'moderator', 'guest']
* }
* });
* ```
* Default - MSSQL only
* ```js
* queryInterface.addConstraint('Users', ['roles'], {
* type: 'default',
* defaultValue: 'guest'
* });
* ```
*
* Primary Key
* ```js
* queryInterface.addConstraint('Users', ['username'], {
* type: 'primary key',
* name: 'custom_primary_constraint_name'
* });
* ```
*
* Foreign Key
* ```js
* queryInterface.addConstraint('Posts', ['username'], {
* type: 'FOREIGN KEY',
* name: 'custom_fkey_constraint_name',
* references: { //Required field
* table: 'target_table_name',
* field: 'target_column_name'
* },
* onDelete: 'cascade',
* onUpdate: 'cascade'
* });
* ```
*
* @param {String} tableName Table name where you want to add a constraint
* @param {Array} attributes Array of column names to apply the constraint over
* @param {Object} options An object to define the constraint name, type etc
* @param {String} options.type Type of constraint. One of the values in available constraints(case insensitive)
* @param {String} [options.name] Name of the constraint. If not specified, sequelize automatically creates a named constraint based on constraint type, table & column names
* @param {String} [options.defaultValue] The value for the default constraint
* @param {Object} [options.where] Where clause/expression for the CHECK constraint
* @param {Object} [options.references] Object specifying target table, column name to create foreign key constraint
* @param {String} [options.references.table] Target table name
* @param {String} [options.references.field] Target column name
*
* @return {Promise}
*/
addConstraint(tableName, attributes, options, rawTablename) { addConstraint(tableName, attributes, options, rawTablename) {
if (!Array.isArray(attributes)) { if (!Array.isArray(attributes)) {
rawTablename = options; rawTablename = options;
...@@ -498,6 +803,14 @@ class QueryInterface { ...@@ -498,6 +803,14 @@ class QueryInterface {
return this.sequelize.query(sql, Object.assign({}, options, { type: QueryTypes.SHOWCONSTRAINTS })); return this.sequelize.query(sql, Object.assign({}, options, { type: QueryTypes.SHOWCONSTRAINTS }));
} }
/**
*
* @param {String} tableName Table name to drop constraint from
* @param {String} constraintName Constraint name
* @param {Object} options Query options
*
* @return {Promise}
*/
removeConstraint(tableName, constraintName, options) { removeConstraint(tableName, constraintName, options) {
options = options || {}; options = options || {};
...@@ -584,6 +897,26 @@ class QueryInterface { ...@@ -584,6 +897,26 @@ class QueryInterface {
}); });
} }
/**
* Insert records into a table
*
* ```js
* queryInterface.bulkInsert('roles', [{
* label: 'user',
* createdAt: new Date(),
* updatedAt: new Date()
* }, {
* label: 'admin',
* createdAt: new Date(),
* updatedAt: new Date()
* }]);
* ```
*
* @param {String} tableName Table name to insert record to
* @param {Array} records List of records to insert
*
* @return {Promise}
*/
bulkInsert(tableName, records, options, attributes) { bulkInsert(tableName, records, options, attributes) {
options = _.clone(options) || {}; options = _.clone(options) || {};
options.type = QueryTypes.INSERT; options.type = QueryTypes.INSERT;
...@@ -654,6 +987,14 @@ class QueryInterface { ...@@ -654,6 +987,14 @@ class QueryInterface {
}); });
} }
/**
* Delete records from a table
*
* @param {String} tableName Table name from where to delete records
* @param {Object} identifier Where conditions to find records to delete
*
* @return {Promise}
*/
bulkDelete(tableName, identifier, options, model) { bulkDelete(tableName, identifier, options, model) {
options = Utils.cloneDeep(options); options = Utils.cloneDeep(options);
options = _.defaults(options, {limit: null}); options = _.defaults(options, {limit: null});
......
...@@ -441,6 +441,10 @@ exports.removeTicks = removeTicks; ...@@ -441,6 +441,10 @@ exports.removeTicks = removeTicks;
* address.coordinates.latitude: 55.6779627, * address.coordinates.latitude: 55.6779627,
* address.coordinates.longitude: 12.5964313 * address.coordinates.longitude: 12.5964313
* } * }
*
* @param value, an Object
* @return Object, an flattened object
* @private
*/ */
function flattenObjectDeep(value) { function flattenObjectDeep(value) {
if (!_.isPlainObject(value)) return value; if (!_.isPlainObject(value)) return value;
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!