Define partial index in Sequelize migration? - node.js

I'm using the following index in my model definition right now:
{
name: 'unique_partner_id',
unique: true,
fields: ['partnerId'],
where: {
email: {
$ne: null
}
}
}
However, I want to use migrations instead of sync, so I'm trying to move this definition from model to the initial migration file.
There is a queryInterface.addIndex() method, however, I can't find any documentation for it.
SO, how do I define a partial index using queryInterface?

I was looking for how to do this in a model and your question answered that for me. So now I'm going to answer this for you! Here's the up function that worked for me:
up: function(queryInterface, Sequelize) {
return queryInterface.addIndex(
'tablename',
['column1', 'column2'],
{
name: 'indexname',
where: {column3: {[Sequelize.Op.ne]: null}}
}
);
}

Here's an example using the where querying syntax with operators:
up: (queryInterface, Sequelize) => queryInterface.addIndex(
'column_name',
['new_id'],
{
name: 'index_name',
where: { new_id: { [Sequelize.Op.ne]: null } },
},
)

Related

Understanding Sequelize database migrations and seeds

I'm trying to wrap my head around Sequelize's migrations and how they work together with seeds (or maybe migrations and seeds in general).
I set up everything to get the migrations working.
First, lets create a users table:
// migrations/01-create-users.js
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable("Users", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
email: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updatedAt: {
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable("Users");
}
};
Fine. If I want to seed an (admin) user, I can do this as follows:
// seeders/01-demo-user.js
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert(
"Users",
[
{
email: "demo#demo.com"
}
],
{}
);
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete("Users", null, {});
}
};
Then to make the magic happen, I do:
$ sequelize db:migrate
Which creates the users table in the database. After running the migrations, seeding is the next step, so:
$ sequelize db:seed:all
Tataa, now I have a user in the users database. Great.
But now I want to add firstname to the users table, so I have to add another migration:
// migrations/02-alter-users.js
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn("Users", "firstname", {
type: Sequelize.STRING
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.removeColumn("Users", "firstname");
}
};
Running migrations again would only run the second one because it was saved in the database that the first one was already executed. But by default sequelize re-runs all seeders. So should I adjust the seeders/01-demo-user.js or change the default behavior and also store the seeders in the DB and create a new one that just updates the firstname?
What if firstname couldn't be null, then running migrations first and then the old version of seeders/01-demo-user.js would throw an error because firstname can't be null.
Re-running seeders leads to another problem: there is already a user with the demo#demo.com email. Running it a second time would duplicate the user. Or do I have to check for things like this in the seeder?
Previously, I just added the user-account in the migration so I could be sure when it was added to the DB and when I had to update it. But someone told me I was doing it all wrong and that I have to use seeders for tasks like this.
Any help/insights much appreciated.
In my opinion, a seeder is something, that is intended to run only once, while the migration is something that you add layer by layer to your DB structure continuously.
I would use seeders for populating some lookups or other data that most likely is not going to change, or test data. In the sequelize docs it's said, that "Seed files are some change in data that can be used to populate database table with sample data or test data."
If you want to make some dynamic data updates when data structure has already changed, you can run raw queries directly in your migrations if needed. So, if you, for instance, added some column in the up method, you can update the rows in the DB according to your business logic, e.g.:
// Add column, allow null at first
await queryInterface.addColumn("users", "user_type", {
type: Sequelize.STRING,
allowNull: true
});
// Update data
await queryInterface.sequelize.query("UPDATE users SET user_type = 'simple_user' WHERE is_deleted = 0;");
// Change column, disallow null
await queryInterface.changeColumn("users", "user_type", {
type: Sequelize.STRING,
allowNull: false
});
There is also an interesting discussion on this topic in Google Groups. I hope, this helps you.
In my experience migrations change structure. Seeders... seed data. Recently I was on a project that didn't have seeders configured. https://sequelize.org/master/manual/migrations.html#seed-storage. This will allow you to setup a file so your data isn't seeded more than once. Migration configuration is right there as well.
Hi try with bulkUpdate...
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.addColumn(
'Users',
'user_type',
{
type: Sequelize.STRING,
allowNull: false,
}
).then(()=>
queryInterface.bulkUpdate('Users', {
user_type: 'simple_user',
}, {
is_deleted : 0,
})
),
down: (queryInterface, Sequelize) =>
queryInterface.removeColumn('Users', 'user_type')
};

Sequelize: how to implement a search based on associated keywords?

I am looking to return all articles from the database associated with one or more keywords, but I am not sure there right way to go about this?
I am using Sequelize 3.x, with node.js 3.7.0.
The data model looks at follows:
const Article = sequelize.define('article', {
...
}
const Keyword = sequelize.define('keyword', {
name: Sequelize.STRING,
lang: Sequelize.STRING
}
const ArticleKeyword = sequelize.define('article_keyword', {
articleId: Sequelize.INTEGER,
keywordId: Sequelize.INTEGER
}
(Article).belongsToMany(
Keyword, { through: ArticleKeyword, as: 'keyword' });
(Keyword).belongsToMany(
Article { through: ArticleKeyword, as: 'article' });
Then the query I tried:
var keywordFilter;
if (req.body.keywords) {
var keywords = req.body.keywords);
if (typeof keywords === 'string') {
keywords = keywords.split(/ *, */);
}
keywordFilter = { name: { $in: keywords } };
}
Article.findAll({
where: {
deleted: false
},
include: [{
model: Keyword,
as: 'keywords',
where: keywordFilter,
attributes: ['name'],
through: {
attributes: []
}
}]
}).then(function(articles) {
...
});
The issue I am finding here is rather than selecting just the articles with the matching keywords it returns all the articles and then simply selects the keywords specified in the query for the results.
Can anyone suggest the right way to go about this?
Hi can you try passing
require:true
in the include block for inner join and check
Ref : https://stackoverflow.com/a/31680398/4583460

CHECK constraints in Seqeulize PostgreSQL ORM (Node.js)

I'm using Sequelize as ORM with a PostgreSQL engine. When using raw queries I can create a table and have columns with 'CHECK' constraints such as
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
In the docs I cannot find a way to do this in Sequelize when defining models. Is there any way of doing this? I don't want to reinvent the wheel ;)
Thanks!!
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Products', {
product_no: {
type: Sequelize.INTEGER
},
price: {
type: Sequelize.NUMERIC
},
name: {
type: Sequelize.TEXT
}
}).
then(() => queryInterface.addConstraint('Products', ['price'], {
type: 'check',
where: {
price: {
[Sequelize.Op.gt]: 0
}
}
}));
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Products');
}
};
Take a look at the Validations section.
var Product = sequelize.define('product', {
price: {
validate: {
min: 0 // Only allow values >= 0
}
}
});
You can also set custom validation rules:
var Product = sequelize.define('product', {
price: {
validate: {
isPositive: function (value) {
return value > 0; // Don't allow 0.
}
}
}
});

Sequelize and querying on complex relations

I'm trying to understand how best to perform a query over multiple entities using Sequelize and Node.js.
I have defined a model "User" which has a belongsToMany relation with a model "Location". I then have a model "Asset" which also has a belongsToMany relation with "Location". When I have an instance of a User I would like to fetch all Assets that are associated with Locations that the User is associated with.
I tried the following which doesn't seem to work...
user.getLocations().then(function(userLocations) { return Asset.findAll({ where: { "Locations" : { $any : userLocations } }) })
Could anyone offer any suggestions?
Try this query:
User.findById(user_id, {
include: [{
model: Location,
required: true
}]
}).then(user => Asset.findAll({
where: {
user_id: user.id,
location_id: {
$in: user.locations.map(location => location.id)
}
}
})).then(assets => {
// The rest of your logic here...
});
This was the final result...
User.findById(user_id, {
include: [{
model: Location,
as: 'Locations', // Was needed since the original relation was defined with 'as'
required: true
}]
}).then(user => Asset.findAll({
include: [{
model: Location,
as: 'Locations',
where: {
id: {
$in: user.Locations.map(location => location.id)
}
}
}]
})).then(assets => {
// The rest of your logic here...
});

How to insert initial data using sequelize migrations/seeds?

I'm trying to create my initial migration to populate the test database but I can't get it working. This is what I have in my migration:
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return [
queryInterface.bulkInsert('Users', [
{ username: "user1" },
{ username: "user2" }
])];
},
down: function (queryInterface, Sequelize) {
return queryInterface.dropTable('Users');
}
};
And I get this error:
== 20151024144833-create-conjugation: migrating =======
{ [SequelizeUniqueConstraintError: Validation error]
name: 'SequelizeUniqueConstraintError',
message: 'Validation error',
errors: [],
fields: [] }
There must be an easier way to do this. I've checked other SO questions, but the syntax has changed in the current version of sequelize.
UPDATE
Ok, I realized my mistake: I was assuming that sequelize would take care of the timestamps. This fixes the problem:
up: function (queryInterface, Sequelize) {
console.log(User);
return [
queryInterface.bulkInsert('Users', [
{ username: "user1", createdAt: Date.now(), updatedAt: Date.now() },
{ username: "user2", createdAt: Date.now(), updatedAt: Date.now() }
])
];
}
But I'm still wondering if this is the right way to seed my database. Is there a way to do it using User.create({})?
new Date()
also required for mysql, i.e.
return queryInterface.bulkInsert('users', [
{
"Forename":"A",
"Surname": "User",
"UserType":"1",
"Email":"auser#gmail.com",
"Password":"password",
"LastLogin":0,
"Tokens": JSON.stringify({"tokens":[]}),
"CreatedBy": 999,
"CreatedAt": new Date(),
"UpdatedAt": new Date()
}]);
You can use next:
const City = sequelize.define('city', {
name: { type: Sequelize.STRING },
order_: { type: Sequelize.INTEGER }
});
City.sync().then(() => {
City.create({
name: 'Neuquen',
order_: 0
});
City.create({
name: 'General Roca',
order_: 1
});
});
Or read about "migrations" at http://docs.sequelizejs.com/en/latest/docs/migrations/
An alternative could be use : sequelize fixtures , you could init your tables with default data declared as a json file or other format.
For a quick and easy way (without seeds or migrations) on sequelize v6:
I modified my sequelize.sync() call:
import { Sequelize } from 'sequelize';
// This will create an in-memory sqlite db
const sequelize = new Sequelize('sqlite::memory:', {
logging: sequelizeLogger
});
await sequelize
.sync({ force: true })
.then(() => {
// seed db
Users.create({ username: 'user1' })
});

Resources