Unable to execute the raw query in Sequelize migrations - node.js

I am trying to update my database using Sequelize migrations so I have tried to write a Sequelize migrations like this
'use strict';
module.exports = {
up: (queryInterface, Sequelize, migration) => {
queryInterface.addColumn('coaching_class_entries', 'bill_cycle', {
type: Sequelize.INTEGER(4),
allowNull: false,
defaultValue: '0',
field: 'bill_cycle',
after: 'fees'
})
.then(() => queryInterface.addColumn('coaching_classes', 'bill_plans', {
type: Sequelize.JSON,
allowNull: false,
defaultValue: 'NULL',
field: 'bill_plans',
after: 'bill_cycle'
}))
.then(() =>
migration.migrator.Sequelize.query('UPDATE coaching_classes SET bill_plans = JSON_ARRAY(JSON_OBJECT("cycle", bill_cycle, "fee", fees));'));
},
down: (queryInterface, Sequelize) => {
let migrations = [];
migrations.push(queryInterface.removeColumn('coaching_class_entries', 'bill_cycle'))
migrations.push(queryInterface.removeColumn('coaching_classes', 'bill_plans'))
return Promise.all(migrations);
}
};
But it is always giving me error in raw query line
Cannot read property 'Sequelize' of undefined
What is the correct syntax for this?

I found it myself only we have to use simple queryInterface.sequelize.query

Related

Sequelize: Unhandled rejection TypeError: admin.hasOperation_sav is not a function

operation_sav model is defined as follows:
operation_sav.js
module.exports = function(sequelize, DataTypes) {
var operation_sav = sequelize.define(
"operation_sav",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
unique: true
},
nom_operation: DataTypes.STRING,
status: {
type: DataTypes.ENUM(
"en_cours_livraison",
"en_reparation",
"repare",
"expedie"
),
defaultValue: "en_cours_livraison"
},
num_serie: DataTypes.STRING,
address_mac: DataTypes.STRING,
sous_garantie: DataTypes.BOOLEAN,
description: DataTypes.STRING,
adminId: DataTypes.INTEGER,
produitId: DataTypes.INTEGER,
status: {
type: DataTypes.ENUM(
"produit_recu",
"en_livraison",
"en_reparation",
"repare",
"en_expedition",
"expedie",
"annule"
),
defaultValue: "produit_recu"
}
},
{ timestamps: true }
);
return operation_sav;
};
Notice there's an external foreign key adminId
The relationship between table Admin and table Operation_sav is defined as follows:
/** admin Has Many operation_sav*/
model.operation_sav.belongsTo(model.admin, {
foreignKeyConstraint: true,
onDelete: "cascade"
});
model.admin.hasMany(model.operation_sav, {
foreignKeyConstraint: true,
onDelete: "cascade"
});
This is the function that creates a row inside the Operation_sav table:
var createSavOperation = function(operationInformation) {
return models.operation_sav.create(operationInformation);
};
I have used it to create this row:
{
"id": 4,
"nom_operation": "Répartion viwone plus",
"status": "produit_recu",
"num_serie": "5525ggffdd",
"address_mac": "ff:ff:ff:ff:ff",
"sous_garantie": true,
"description": "Panne bouton droit",
"adminId": 10,
"produitId": 6,
"updatedAt": "2020-02-27T09:22:47.430Z",
"createdAt": "2020-02-27T09:22:47.430Z"
}
Notice that adminId is 10 and operation_savId is 4. So I try to see if the whole association has worked properly by running this code:
models.operation_sav.findByPk(4).then(operation_sav => {
admin = models.admin.findByPk(10);
admin.hasOperation_sav(operation_sav);
});
However, I get this error:
Unhandled rejection TypeError: admin.hasOperation_sav is not a
function
Is this because I have manually written the adminId value when creating that operation_sav row?
EDIT 1: When I added this console.log line:
admin = models.admin.findByPk(10);
console.log(admin instanceof models.admin); // false!!
admin.hasoperation_sav(operation_sav);
I get false which doesn't make any sense but it kindof explains why sequelize is not throwing the admin.hasoperation_sav is not a function error..
EDIT 2: I have fixed EDIT 1 like this:
models.admin.findByPk(10).then(adminFound => {
console.log(
"admin is instance of models.admin",
adminFound instanceof models.admin
); // true
adminFound.hasoperation_sav(operation_sav);
});
But, hasoperation_sav is still unrecognized as a function.
I have solved it like this:
models.operation_sav.findByPk(4).then(operation_sav => {
models.admin.findByPk(10).then(adminFound => {
console.log(
"admin is instance of models.admin",
adminFound instanceof models.admin
);
// admin is instance of models.admin true
adminFound.hasOperation_sav(operation_sav).then(result => {
console.log("admin has operation sav", result);
//admin has operation sav true
});
});
});

sequelize.js association accessor

I made API server with Node.js
Also I use sequelize.js(version 4) for communicate with MySQL.
My table structure is here.
[Article]
no(PK)
subject
content
created_at
updated_at
[Comment]
no(PK)
content
created_at
updated_at
article_no(FK to Article)
[index.controller.js]
import { Article, Comment } from '../model/model';
export const index = (req, res) => {
res.send('controller index');
};
export const getArticle = (req, res) => {
try {
Article.all()
.then(article => {
res.status(200).json({status: true, result: article});
});
} catch(e) {
res.status(500).json({status: false, result: "get article fail"});
}
}
export const addArticle = (req, res) => {
const { subject, content } = req.body;
try {
Article.create({
subject: subject,
content: content
})
res.status(200).json({status: true, result: "article write success"});
} catch(e) {
res.status(500).json({status: false, result: "article fail"});
}
}
export const getComment = (req, res) => {
try {
Comment.all()
.then(comment => {
res.status(200).json({status: true, result: comment})
});
} catch(e) {
res.status(500).json({status: false, result: "get comment fail"});
}
}
export const addComment = (req, res) => {
const { content, article_no } = req.body;
try {
Comment.create({
content: content,
article_no: article_no
})
.then(() => res.status(200).json({status: true, result: "comment write success"}))
} catch(e) {
console.log(e);
res.status(500).json({status: false, result: "comment fail"});
}
}
[index.js]
import express from 'express';
import { index, getArticle, getComment,addArticle, addComment } from './index.controller';
const router = express.Router();
router.get('/', index);
router.get('/article', getArticle);
router.post('/article', addArticle);
router.get('/comment', getComment);
router.post('/comment', addComment);
export default router;
[model.js]
import Sequelize from 'sequelize';
const sequelize = new Sequelize('db', 'id', 'pw', {
host: '127.0.0.1',
dialect: 'mysql'
})
export const Article = sequelize.define('article', {
no: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
subject: {
type: Sequelize.STRING,
allowNull: false
},
content: {
type: Sequelize.STRING,
allowNull: false
}
}, {
freezeTableName: true,
underscored: true
})
export const Comment = sequelize.define('comment', {
no: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
content: {
type: Sequelize.STRING,
allowNull: false
}
}, {
freezeTableName: true,
underscored: true
})
Article.hasMany(Comment, {as: 'Comments'}); // association
Comment.belongsTo(Article); // association
sequelize.sync({
force: false
});
Because of association(hasMany, belongsTo), article_no column will be added to Comment table.
Refer to this document, http://docs.sequelizejs.com/manual/tutorial/associations.html#one-to-many-associations-hasmany-
It says that Instances of Project will get the accessors getWorkers and setWorkers.
In my case, it will be getComments and setComments.
But I don't know exactly how can I get all the comments related articles with using accessor.
Current output is here. (If I connect to GET /article)
{
"status":true,
"result":[
{
"no":1,
"content":"comment test",
"created_at":"2018-07-18T05:00:45.000Z",
"updated_at":"2018-07-18T05:00:45.000Z",
"article_no":1
}
]
}
Desired output is here
{
"status":true,
"result":[
{
"no":1,
"content":"comment test",
"created_at":"2018-07-18T05:00:45.000Z",
"updated_at":"2018-07-18T05:00:45.000Z",
"article_no":1,
"comments": [
// related comments here!
]
}
]
}
Thanks.
When you want to join another model you should use include in your query
User.findAll({
include: [
{ model: Profile, required: true // inner join }
],
limit: 3
});
Check out the Sequelize model usage docs.
To access the comments with accessors you will need do something like this:
const articles = await Article.all();
articles.forEach(article => {
const comments = await article.getComments();
})
The idea behind is that each article sequelize object will have the accessor getComments but internally what it does when you execute getComments it makes a new request to the database with the prepopulated articleId in the comments where query. This is called lazy loading because you can load the data when you need it. But that is not your case.
For the desired output I suggest to use the include method cause it will make a single request to the database.

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 do I seed database with Geometry value?

I use Sequelize to connect to my PostgreSQL database and during development, I use seed files to populate database with example data. I recently installed PostGIS for my database and wanted to use the GEOMETRY('POINT') type to describe the latitude/longitude position.
However, I have no idea how to put some GEOMETRY data using seeders. I tried following the examples in Sequelize docs:
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.bulkInsert('Vets', [{
title: 'Centrum Zdrowia Małych Zwierząt',
city: 'Poznań',
googleMapsID: 'ChIJQ8EgpGpDBEcR1d0wYZTGPbI',
position: {
type: 'Point',
coordinates: [52.458415, 16.904740]
},
rodents: true
}], {}),
down: (queryInterface, Sequelize) =>
queryInterface.bulkDelete('Vets', null, {})
};
but when I run the sequelize db:seed:all command, following error occurs:
ERROR: Invalid value [object Object]
I guess I just need to specify the position in some other way, but the Sequelize docs don't mention any for seeds. Can anyone help me with this problem?
The migration file for the Vets database is as follows:
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Vets', {
...
rodents: {
type: Sequelize.BOOLEAN
},
position: {
type: Sequelize.GEOMETRY
},
websiteUrl: {
type: Sequelize.STRING
},
...
}, {
indexes: [
{
unique: true,
fields: ['title', 'city']
}
]
}),
down: (queryInterface, Sequelize) =>
queryInterface.dropTable('Vets')
};
And the model definition:
module.exports = (sequelize, DataTypes) => {
const Vet = sequelize.define('Vet', {
...
rodents: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
position: DataTypes.GEOMETRY('POINT'),
websiteUrl: DataTypes.STRING,
...
}, {
indexes: [
{
unique: true,
fields: ['title', 'city']
}
]
});
Vet.associate = (models) => {
Vet.belongsTo(models.User, { as: 'suggestedBy' });
Vet.belongsTo(models.User, { as: 'acceptedBy' });
};
return Vet;
};
I found a solution (or rather a workaround), turns out I needed to use Sequelize.fn():
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.bulkInsert('Vets', [
{
...
position: Sequelize.fn('ST_GeomFromText', 'POINT(52.458415 16.904740)'),
...
}
], {}),
down: (queryInterface, Sequelize) =>
queryInterface.bulkDelete('Vets', null, {})
};
I would like to add to your answer, that if you want to add a type: 'Polygon' and as a geoJson you can do the following:
up: (queryInterface, Sequelize) => {
const polygon = { type: 'Polygon', coordinates: [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0] ]
]};
const cityCreated = {
....
boundaries: Sequelize.fn('ST_GeomFromGeoJSON', JSON.stringify(polygon)),
....
}
return queryInterface.bulkInsert('cities', [cityCreated], {});
},
in this case, the column boundaries is the one defined as GEOMETRY
this is good for adding boundaries, and geoJson is a more common dataType than text for this purpose.

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