Sequelize migration converting Sequelize.UUID primary key field to integer autoincrement in MYSQL - migration

I am using sequelize CLI to generate and run db migrations. The issue I am having is the id field set to data type Sequelize.UUID appearing as an autoincrement integer in mysql. Here is my user model and migration:
User Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
UserName: {type:DataTypes.STRING,unique:true,allowNull:false},
FirstName:{type: DataTypes.STRING,allowNull:true},
LastName: {type:DataTypes.STRING,allowNull:true},
Email: {type:DataTypes.STRING,allowNull:false,unique:true,validate: { isEmail: {msg: "Invalid Email"} }},
Password: {type:DataTypes.STRING,allowNull:false},
Avatar: {type:DataTypes.STRING,allowNull:true},
}, {});
User.associate = function(models) {
User.hasMany(models.RoleUser,
{
foreignKey:'UserId',
as:'userroles',
sourceKey:'id'
}),
User.belongsTo(models.Country,
{
foreignKey:'CountryId',
targetKey:'id'
}),
User.belongsToMany(models.Role,
{
through: 'RoleUser',
foreignkey:'UserId'
})
};
return User;
};
**User Migration file:**
'use strict';
const uuidv4 = require('uuid/v4');
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
UserName: {
type: Sequelize.STRING
},
FirstName: {
type: Sequelize.STRING
},
LastName: {
type: Sequelize.STRING
},
Email: {
type: Sequelize.STRING
},
Password: {
type: Sequelize.STRING
},
Avatar: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
AFTER MIGRATION, THE HIS FIELD IS CONVERTED TO INT AUTOINCREMENT IN MYSQL:
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
Any pointer as to why this is happening? Please assist. Even the associations seem not to be formed at all as foreign keys are of type Sequelize.UUID

I fixed the problem by adding the id field on model with primary key property set to true.
id: {
type:DataTypes.UUID,
allowNull:false,
unique:true,
primaryKey:true
},
Its like sequelize will automatically generate id field of type INTEGER AUTOINCREAMENT if the model does not have a field with primary key set to true.

Related

Why my sequelize association doesn't work?

I've created two Models User and Gasto(means spent in portuguese), these two tables has an association like One User to Many Gasto, so I'm trying to implement the OnDelete 'CASCADE' to when I delete one User all Gasto from this User has to be deleted together, but my association is not working. Can somebody help me?
My Database Config:
import 'dotenv/config'
import { Options } from 'sequelize'
const config: Options = {
username: process.env.DB_USER ?? 'root',
password: process.env.DB_PASS ?? '123456',
database: 'gastos_app_db',
host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.DB_PORT) ?? 3002,
dialect: 'mysql',
dialectOptions: {
timezone: 'Z'
},
logging: false
}
module.exports = config
Migration from User, Gasto has the same example.
'use strict'
/** #type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
onDelete: 'CASCADE'
},
username: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
}
})
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('users')
}
}
My Instance of the Sequelize and configurations.
import { Sequelize } from 'sequelize'
import * as config from '../config/database'
export default new Sequelize(config)
My Model of user and the Association.
import { DataTypes } from 'sequelize'
import db from '.'
import { Gasto } from './Gasto'
export const User = db.define('User', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
username: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
},
role: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: false,
tableName: 'users',
underscored: true
})
User.hasMany(Gasto, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
Gasto.hasOne(User, {
foreignKey: 'id'
})
Gasto Model:
import { DataTypes } from 'sequelize'
import db from '.'
export const Gasto = db.define('Gasto', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
type: {
type: DataTypes.STRING,
allowNull: false
},
value: {
type: DataTypes.INTEGER,
allowNull: false
},
gastoDate: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: false,
tableName: 'gastos',
underscored: true
})
Gasto Migration
'use strict'
/** #type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('gastos', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false
},
type: {
type: Sequelize.STRING,
allowNull: false
},
value: {
type: Sequelize.STRING,
allowNull: false
},
gasto_date: {
type: Sequelize.STRING,
allowNull: false
}
})
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('gastos')
}
}
I've already readed the all documentations, and checked some old projects, but I can't found the right way to fix this.
Your user_id field in your migration to create the gastos table must have a Foreign Key definition inside of it, along with the on update/on delete logic you desire (Cascade).
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade'
}
You can find an example of this in the sequelize query interface documentation.

I have a trouble with association with sequelize and postgres

I have two tables defined, one for movies, one for characters, these are interconnected, by the MovieCharacter table.
Setting the tables to maintain a one-to-many relationship via belongstomany allows me to create a duplicate relationship, and I'm not getting around it.
I leave my code below, i have some experience with mongoose and nosql db but this is new for me.
Thanks!
charModel.js
const { Model, DataTypes } = require('sequelize');
const CHARACTER_TABLE = 'character';
const CharacterSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
age: {
type: DataTypes.BIGINT,
allowNull: false,
},
weight: {
type: DataTypes.BIGINT,
allowNull: false,
},
history: {
type: DataTypes.STRING,
allowNull: false
},
image: {
type: DataTypes.STRING,
allowNull: false
},
};
modelMovie.js
class Character extends Model {
static associate(models){
this.belongsToMany(models.Movie, {
as: "movies",
through: "MovieCharacter",
foreignKey: "characterId",
otherKey: "movieId",
});
}
static config(sequelize){
return {
sequelize,
tableName: CHARACTER_TABLE,
modelName: 'Character',
timestamps: false,
}
}
}
const moment = require("moment");
const { Model, DataTypes } = require("sequelize");
const { GENRES_TABLE } = require("./genre.model");
const MOVIES_TABLE = "movie";
const MovieSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
creationDate: {
field: "creation_date",
type: DataTypes.STRING,
get(){
return moment(this.getDataValue('creationDate')).format('DD-MM-YYYY')
},
allowNull: false,
},
rating: {
type: DataTypes.INTEGER,
allowNull: false,
},
image: {
type: DataTypes.STRING,
allowNull: false,
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
genreId: {
field: "genre_id",
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: GENRES_TABLE,
key: "id",
},
onUpdate: "CASCADE",
onDelete: "SET NULL",
},
};
class Movie extends Model {
static associate(models) {
this.belongsToMany(models.Character, {
as: "characters",
through: "MovieCharacter",
foreignKey: "movieId",
otherKey: "characterId",
});
this.belongsTo(models.Genre, {
as: "genre",
});
}
static config(sequelize) {
return {
sequelize,
tableName: MOVIES_TABLE,
modelName: "Movie",
timestamps: false,
};
}
}
module.exports = { MOVIES_TABLE, MovieSchema, Movie };
modelCharMov.js
const { Model, DataTypes} = require('sequelize');
const { CHARACTER_TABLE} = require('./character.model');
const { MOVIES_TABLE} = require('./movies.model')
const MOVIES_CHARACTERS_TABLE = 'movies_characters';
const MoviesCharactersSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
movieId:{
field: 'movie_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: MOVIES_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
characterId:{
field: 'character_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: CHARACTER_TABLE,
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
}
};
class MovieCharacter extends Model {
static config(sequelize) {
return {
sequelize,
tableName: MOVIES_CHARACTERS_TABLE,
modelName: 'MovieCharacter',
timestamps: false
};
}
}
Add 2 BelongsTo associations on the junction table pointing to the tables that are using it to find one another. That would be my first guess because that is the only thing that would have been different in my code compared to yours if implementing this. If that doesn’t work, more details please sir 🧐

How to substitute an array from my Table? Sequelize

I've the following table:
User.init({
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false
},
purchased_book: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: false,
},
surname: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
timestamps: false,
sequelize: sequelizeConnection,
paranoid: true
})
I want to remove only one item from the purchased_book array. purchased_book is an array of book:
export interface book{
title: string;
author: string;
price: number;
id: number | undefined;
}
How can I access to the field User.purchased_book.id? I've tried with:
async function remove_book_from_user_array(user_id: number, book_id: number){
var new_array: book[] = User.purchased_book;
await User.update({purchased_book: new_array}, {
where: {
id: user_id
}
})
}

ExpressJs - Mongoose: Delete documents with Many To Many relationship

I've two Models, Post and Tag with Many To Many relationships.
Post Schema:
const postSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: [true, 'A post must belong to a user.'],
},
title: {
type: String,
unique: [true, 'A Post already exists with this title.'],
required: [true, 'A Post must have a title.'],
},
slug: { type: String, unique: true },
body: { type: String, required: [true, 'A Post must have a body.'] },
coverImage: String,
images: Array,
isDraft: { type: Boolean, default: false },
isPublished: { type: Boolean, default: false },
tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }],
},
{
timestamps: { currentTime: () => Math.floor(Date.now() / 1000) },
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
)
Tag Schema:
const tagSchema = new Schema(
{
title: { type: String, required: true },
slug: { type: String },
posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }],
},
{
timestamps: { currentTime: () => Math.floor(Date.now() / 1000) },
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
)
Now I want to remove all the references of the Posts from the Tag documents when a Post is deleted.
I'm trying to the following remove middleware in the Post model but it is not working. The post get deleted but the reference still there on the Tag documents.
postSchema.pre('remove', function (next) {
var post = this
post
.model('Tag')
.update(
{ posts: { $in: post.tags } },
{ $pull: { posts: post._id } },
{ multi: true },
next
)
})
After trying many times I finally fired out what wrong I was doing. Following the fix I made to make it work:
In Post Controller I was previously doing this:
const post = await Post.findByIdAndDelete(req.params.id)
Which I changed to:
const post = await Post.findById(req.params.id)
await post.remove()
And in Post Model:
postSchema.pre('remove', async function (next) {
await this.model('Tag').updateMany(
{ posts: this._id },
{ $pull: { posts: this._id } },
{ multi: true },
next
)
})

Sequelize Count and Group Nested Include

I have got a few sql tables as
export default (sequelize, Sequelize) => {
return sequelize.define('teacher', {
tagline: {
type: Sequelize.TEXT,
},
modeOfPayment: {
type: Sequelize.STRING,
},
modeOfSession: {
type: Sequelize.STRING,
},
preferredTimeZones: {
type: Sequelize.STRING,
},
titleForSessions: {
type: Sequelize.STRING,
},
availableForWork: {
type: Sequelize.BOOLEAN,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('skill', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('category', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
Here is the model relation between them
Teacher.belongsToMany(Skill, {
through: 'skill_teacher',
});
Skill.belongsToMany(Teacher, {
through: 'skill_teacher',
});
Category.hasMany(Skill);
Skill.belongsTo(Category);
Earlier I needed to query count of teachers in each skill, here's my controller for that
// #desc Get skill count for each skill
// #route GET /api/skills/count
// #access Public
const getSkillCount = asyncHandler(async (req, res) => {
try {
const skills = await Skill.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
include: [{ attributes: [], model: Teacher }],
group: ['skill.id'],
});
res.json(skills);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});
Now, the skills are grouped in categories. So I wanna query a list of skills having their own teacher count grouped in their own categories, which category also having a skillCount column. I tried this but it is not giving my desired results
// #desc Get category and their skills counts
// #route GET /api/categories/skills/count
// #access Public
const getCategorySkillCounts = asyncHandler(async (req, res) => {
try {
const categories = await Category.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('skills.id')), 'skillCount'],
],
include: {
model: Skill,
include: [{ model: Teacher }],
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
group: ['skill.id'],
},
group: ['category.id'],
});
res.json(categories);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});