I'm trying to join 3 tables in node.js using sequelize but having trouble. What is the problem with my code? - mysql

Newer to Node.js any advice would be appreciated! Trying to join 3 tables with a common key but getting the error...
react_devtools_backend.js:4026 [GraphQL error]: Message: Unknown column 'google_responsive_descriptions.googleTextAdId' in 'on clause', Location: [object Object], Path: listGoogleTextAds
googleResponsiveAds.js file
module.exports = (sequelize, DataTypes) => {
const googleResponsiveAds = sequelize.define(
"google_responsive_headlines",
{
responsive_headlines: { type: DataTypes.STRING },
responsive_path_1: { type: DataTypes.STRING },
responsive_path_2: { type: DataTypes.STRING }
},
{
timestamps: false,
tableName: "google_responsive_headlines"
}
);
//googleResponsiveAds.associate = () => {};
googleResponsiveAds.associate = function (models) {
googleResponsiveAds.belongsTo(models.google_text_ads, { foreignKey: "ad_id" });
}
return googleResponsiveAds;
};
googleResponsiveDescriptionsAds.js file
module.exports = (sequelize, DataTypes) => {
const googleResponsiveDescriptionsAds = sequelize.define(
"google_responsive_descriptions",
{
responsive_descriptions: { type: DataTypes.STRING }
},
{
timestamps: false,
tableName: "google_responsive_descriptions"
}
);
//googleResponsiveDescriptionsAds.associate = () => {};
googleResponsiveDescriptionsAds.associate = function (models) {
googleResponsiveDescriptionsAds.belongsTo(models.google_text_ads, { foreignKey: "ad_id" });
}
return googleResponsiveDescriptionsAds;
};
googleTextAds.js file
module.exports = (sequelize, DataTypes) => {
const googleTextAds = sequelize.define(
"google_text_ads",
{
headline_pt_1: { type: DataTypes.STRING },
headline_pt_2: { type: DataTypes.STRING },
headline_pt_3: { type: DataTypes.STRING },
final_url: { type: DataTypes.STRING },
display_url: { type: DataTypes.STRING },
status: { type: DataTypes.STRING },
type: { type: DataTypes.STRING },
description1: { type: DataTypes.STRING },
description2: { type: DataTypes.STRING },
path1: { type: DataTypes.STRING },
path2: { type: DataTypes.STRING },
ad_id: { type: DataTypes.INTEGER }
},
{
timestamps: false,
tableName: "google_text_ads"
}
);
//googleTextAds.associate = () => {};
googleTextAds.associate = function (models) {
googleTextAds.hasMany(models.google_responsive_headlines, { sourceKey: 'ad_id' })
googleTextAds.hasMany(models.google_responsive_descriptions, { sourceKey: 'ad_id' });
};
return googleTextAds;
};
Here is the section of the queries/google.js
{
key: "listGoogleTextAds",
prototype:
"(customer_id: Int, start_date: String, end_date: String): [GoogleTextAds]",
run: async args => {
const allIds = await google_text_ads
.findAll({
attributes: [
"ad_id",
"date",
"impressions",
"clicks",
"cost"
],
include: [
{
model: google_responsive_descriptions,
as: 'google_responsive_descriptions',
required: true,
attributes: [
"ad_id",
"responsive_descriptions"
],
},
{
model: google_responsive_headlines,
as: 'google_responsive_headlines',
attributes: [
"ad_id",
"responsive_headlines",
"responsive_path_1",
"responsive_path_2"
]
}
],
where: {
customer_id: args.customer_id,
date: {
[Op.gte]: args.start_date,
[Op.lte]: args.end_date
},
status: {
[Op.in]: ["ENABLED"]
},
type: {
[Op.in]: ["EXPANDED_TEXT_AD", "RESPONSIVE_SEARCH_AD"]
}
}
})
}
}
EDIT: Here's my query
export const LIST_GOOGLE_TEXT_ADS = gql`
query listGoogleTextAds(
$customer_id: Int!
$start_date: String!
$end_date: String!
) {
listGoogleTextAds(
customer_id: $customer_id
start_date: $start_date
end_date: $end_date
) {
ad_id
type
headline_pt_1
headline_pt_2
headline_pt_3
description1
description2
path1
path2
status
final_url
display_url
impressions
clicks
cost
}
}
`;
and my models...
type GoogleTextAds {
ad_id: Int
type: String
headline_pt_1: String
headline_pt_2: String
headline_pt_3: String
description1: String
description2: String
path1: String
path2: String
final_url: String
display_url: String
status: String
impressions: Int
clicks: Int
cost: Float
}
type GoogleResponsiveAds{
ad_id: Int
responsive_headlines: String
responsive_path_1: String
responsive_path_2: String
}
type GoogleResponsiveDescriptionsAds{
ad_id: Int
responsive_descriptions: String
}
here is the query I am trying to replicate...
SELECT distinct google_responsive_headlines.responsive_headlines,
google_responsive_headlines.responsive_path_1,
google_responsive_headlines.responsive_path_2,
google_responsive_descriptions.responsive_descriptions,
google_text_ads.date,
google_text_ads.clicks,
google_text_ads.cost,
google_text_ads.impressions,
google_text_ads.ad_id,
google_text_ads.status,
google_text_ads.final_url,
google_text_ads.create_time
FROM irene_db.google_text_ads
inner JOIN irene_db.google_responsive_headlines ON google_responsive_headlines.ad_id = google_text_ads.ad_id
inner JOIN irene_db.google_responsive_descriptions ON google_responsive_descriptions.ad_id = google_text_ads.ad_id
where google_text_ads.customer_id = 144 and google_text_ads.date = '2022-06-09' and
google_responsive_headlines.customer_id = 144 and google_responsive_headlines.date = '2022-06-09' and
google_responsive_descriptions.customer_id = 144 and google_responsive_descriptions.date = '2022-06-09';
EDIT2: Where associate gets called...
const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const dotenv = require("dotenv");
dotenv.config();
const basename = path.basename(module.filename);
const db = {};
let sequelize;
const { DB_HOST, DB_USER, DB_PASS, DB_NAME } = process.env;
sequelize = new Sequelize(DB_NAME, DB_USER, DB_PASS, {
dialect: "mysql",
host: DB_HOST
});
fs.readdirSync(__dirname)
.filter(
file =>
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
)
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
EDIT 3:
After boc4life's code suggestions I'm now at least getting graphql to attempt the query. But it's using the wrong field name in the join on section. Here's the query it built...
SELECT
`google_text_ads`.`id`,
`google_text_ads`.`ad_id`,
`google_text_ads`.`date`,
`google_text_ads`.`impressions`,
`google_text_ads`.`clicks`,
`google_text_ads`.`cost`,
`google_responsive_descriptions`.`id` AS `google_responsive_descriptions.id`,
`google_responsive_descriptions`.`responsive_descriptions` AS `google_responsive_descriptions.responsive_descriptions`,
`google_responsive_headlines`.`id` AS `google_responsive_headlines.id`,
`google_responsive_headlines`.`responsive_headlines` AS `google_responsive_headlines.responsive_headlines`,
`google_responsive_headlines`.`responsive_path_1` AS `google_responsive_headlines.responsive_path_1`,
`google_responsive_headlines`.`responsive_path_2` AS `google_responsive_headlines.responsive_path_2`
FROM
`google_text_ads` AS `google_text_ads`
INNER JOIN
`google_responsive_descriptions` AS `google_responsive_descriptions` ON `google_text_ads`.`ad_id` = `google_responsive_descriptions`.`googleTextAdId`
INNER JOIN
`google_responsive_headlines` AS `google_responsive_headlines` ON `google_text_ads`.`ad_id` = `google_responsive_headlines`.`googleTextAdId`
WHERE
`google_text_ads`.`customer_id` = 142
AND (`google_text_ads`.`date` >= '2022-05-17'
AND `google_text_ads`.`date` <= '2022-06-17')
AND `google_text_ads`.`status` IN ('ENABLED')
AND `google_text_ads`.`type` IN ('EXPANDED_TEXT_AD' , 'RESPONSIVE_SEARCH_AD');

Try using the tableName defined while defining the table structure to access the model while creating associations.
Example: instead of
models.googleTextAds
do
models.google_text_ads

What jumps out to me is all of those nested includes. Since you are joining the Headlines and Descriptions tables on the Text_Ads table, I believe all you should need here is the one include array containing two objects, one for Headlines and one for Descriptions.
Currently you have Headlines nested under Descriptions, which won't work because Descriptions does not have an association with Headlines directly defined. You also have an include of Text_Ads nested inside of Descriptions, which should WORK, but should be unnecessary since that is the model you are calling findAll() on. You can bring the Text_Ads attributes you are querying for out into that parent object as a sibling of include. Something like this looks like a good starting point for getting the query cleaned up. I have also removed a bunch of the unnecessary sequelize.col() that you had in your initial post.
const allIds = await google_text_ads
.findAll({
attributes: [
"ad_id",
[sequelize.fn("max", sequelize.col("date")), "date"],
[sequelize.fn("sum", sequelize.col("impressions")), "impressions"],
[sequelize.fn("sum", sequelize.col("clicks")), "clicks"],
[sequelize.fn("sum", sequelize.col("cost")), "cost"]
],
include: [
{
model: Models.google_responsive_descriptions,
as: 'googleResponsiveDescriptionsAds',
required: true,
attributes: [
"ad_id",
"responsive_descriptions"
],
},
{
model: Models.google_responsive_headlines,
as: 'googleResponsiveAds',
attributes: [
"ad_id",
"responsive_headlines",
"responsive_path_1",
"responsive_path_2"
]
}
],
where: {
customer_id: args.customer_id,
date: {
[Op.gte]: args.start_date,
[Op.lte]: args.end_date
},
status: {
[Op.in]: ["ENABLED"]
},
type: {
[Op.in]: ["EXPANDED_TEXT_AD", "RESPONSIVE_SEARCH_AD"]
}
}
})

Related

How do I skip the primary key auto increment in nodejs ORM sequelize when unique constraint error occurs

How do I skip the primary key auto increment in sequelize node.js when unique constraint error occurs
When I enter same username twice that was defined as unique into mysql by using of Postman my program is running correct way but the problem is the incremental primary key is still continuing.
For example
when I insert another different username value the program is jumping at one of the sequential primary key as expected.
So that, How can I stop the auto increment id as I restricted not to insert duplicate username values in my database
/* DATABASE CONFIGURATION FILE */
const { Sequelize, QueryTypes, DataTypes, Op, UniqueConstraintError, ValidationErrorItem } = require(`sequelize`);
const sequelize = new Sequelize(`tutorialdb`, `root`, ``, {
host: `localhost`,
dialect: `mysql`,
logging: true,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
});
sequelize
.authenticate()
.then(() => {
console.log(`Connection has been established successfully...`);
})
.catch((err) => {
console.log(`Unable to connect to the database: `, err);
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.QueryTypes = QueryTypes;
db.DataTypes = DataTypes;
db.Op = Op;
db.ValidationErrorItem = ValidationErrorItem;
db.UniqueConstraintError = UniqueConstraintError;
db.postModel = require(`../models/post.model.jsx`)(sequelize, DataTypes);
db.sequelize.sync({ force: false, alter: false, match: /tutorialdb$/ }).then(() => {
console.log(`Tables were synced successfully`);
});
module.exports = db;
/* Model definition File */
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define(
`post`,
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
title: {
type: DataTypes.STRING(30),
allowNull: false,
validate: {
notEmpty: {
args: true,
msg: `Title is required`,
},
len: {
args: [3, 50],
msg: `Title must between 3 and 30 characters`,
},
},
},
text: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
notEmpty: {
args: true,
msg: `Text is required`,
},
len: {
args: [5, 100],
msg: `Text must between 5 and 100 characters`,
},
},
},
username: {
type: DataTypes.STRING(20),
allowNull: false,
unique: true,
validate: {
notEmpty: {
args: true,
msg: `Username is required`,
},
len: {
args: [3, 20],
msg: `Username must between 3 and 20 characters`,
},
},
},
},
{
timestamps: true,
paranoid: true,
}
);
Post.beforeCreate(async (post, options) => {
post.username = post.username.toLowerCase();
});
Post.beforeUpdate(async (post, options) => {
post.username = post.username.toLowerCase();
});
return Post;
};
/* Controller File */
const db = require(`../config/db.config.jsx`);
const postModel = db.postModel;
const Sequelize = db.Sequelize;
const sequelize = db.sequelize;
const QueryTypes = db.QueryTypes;
const DataTypes = db.DataTypes;
const Op = db.Op;
const ValidationErrorItem = db.ValidationErrorItem;
const UniqueConstraintError = db.UniqueConstraintError;
/* Create new Post */
exports.create = async (req, res) => {
const transactions = await sequelize.transaction();
try {
const trim = (noSpace) => {
return noSpace.replace(/\s/g, ``);
};
const post = await postModel.create(
{
title: req.body.title,
text: req.body.text,
username: trim(req.body.username),
},
{ transaction: transactions }
);
await transactions.commit();
res.status(200).json(post);
} catch (err) {
await transactions.rollback();
const messages = {};
let message;
err.errors.forEach((error) => {
messages[error.path] = error.message;
message = messages[error.path];
});
res.status(500).json(message);
}
};
/* Find All posts */
exports.findAll = async (req, res) => {
const transactions = await sequelize.transaction();
try {
const title = req.query.title;
const text = req.query.text;
const username = req.query.username;
let finder = title ? { title: { [Op.like]: `%${title}%` } } : text ? { text: { [Op.like]: `%${text}%` } } : username ? { username: { [Op.like]: `%${username}%` } } : null;
const posts = await postModel.findAll({
as: `posts`,
attributes: [`id`, `title`, `text`, `username`, `createdAt`, `updatedAt`, `deletedAt`],
transaction: transactions,
lock: false,
paranoid: false,
order: [[`id`, `DESC`]],
where: finder,
});
await transactions.commit();
res.status(200).json(posts);
} catch (err) {
await transactions.rollback();
res.status(500).json(err.message);
}
};
/* Router File */
module.exports = (app) => {
const router = require(`express`).Router();
const postCtrl = require(`../controllers/post.controller.jsx`);
router.route(`/post`).post(postCtrl.create).get(postCtrl.findAll);
app.use(`/api/v1`, router);
};
/* MiddleWare Logger File */
const moment = require(`moment`);
/* Create Logger */
const logger = (req, res, next) => {
console.log(`${req.protocol}://${req.get(`host`)}${req.originalUrl} : ${moment().format()}`);
next();
};
module.exports = logger;
/* Server File */
const express = require(`express`);
const cors = require(`cors`);
const logger = require(`./src/middleware/logger.jsx`);
const app = express();
const corsOptions = {
origin: `http://localhost:4001`,
optionsSuccessStatus: 200,
};
app
.use(cors(corsOptions))
.use(logger)
.use(express.json())
.use(express.urlencoded({ extended: false }))
.get(`/`, (req, res) => res.status(200).send(`Welcome to fullstack tutorial application`));
require(`./src/routes/routers.jsx`)(app);
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => console.log(`Server is running on port ${PORT}...`));
The output result is working well. But the primary Key auto-increment is still continuing
http://localhost:4000/api/v1/post : 2022-08-28T11:02:47+03:00
Executing (ac12d76f-d7dc-4040-9692-3d6b853feac9): START TRANSACTION;
Executing (ac12d76f-d7dc-4040-9692-3d6b853feac9): INSERT INTO posts
(id,title,text,username,createdAt,updatedAt) VALUES
(DEFAULT,?,?,?,?,?); Executing (ac12d76f-d7dc-4040-9692-3d6b853feac9):
ROLLBACK;
I had attempted the following solution and works me perfectly.
/* Create new User */
exports.create = async (req, res) => {
const trim = (noSpace) => {
return noSpace.replace(/\s/g, ``);
};
const transactions = await sequelize.transaction();
try {
const { username, password } = req.body;
const users = await userModel.findOne({
where: { username: trim(username) },
transaction: transactions,
});
if (users !== null) {
await transactions.rollback();
res.json(`Username ${username} already exist`);
} else {
const user = await userModel.create(
{
username: trim(username),
password: trim(password),
},
{
transaction: transactions,
}
);
await transactions.commit();
res.status(200).json(user);
}
} catch (err) {
await transactions.rollback();
const messages = {};
let message;
err.errors.forEach((error) => {
messages[error.path] = error.message;
message = messages[error.path];
});
res.status(500).json(message);
}
};
exports.create = async (req, res) => {
const transactions = await sequelize.transaction();
try {
const trim = (noSpace) => {
return noSpace.replace(/\s/g, ``);
};
const [user, created] = await userModel.findOrCreate({
where: { username: trim(req.body.username) },
defaults: { password: trim(req.body.password) },
transaction: transactions,
});
return created ? (await transactions.commit(), res.status(200).json(user)) : user ? (await transactions.rollback(), res.json(`Username already exist`)) : err;
} catch (err) {
await transactions.rollback();
const messages = {};
let message;
err.errors.forEach((error) => {
messages[error.path] = error.message;
message = messages[error.path];
});
res.status(500).json(message);
}
};
I am not sure about issue's existence in previous versions of sequelize. But this issue does not exist if using Object.findOrCreate() with following mentioned versions.
However this issue does appear if using Object.create() method with unique constraint set for field value and not checking field value existence prior to using Object.create() e.g in following code email unique property is set and if user.create() is used for an existing email in db an error is thrown but userid is incremented thus for next successful creation userid is not as expected.
An alternate solution is using user.findOne() prior to use user.create() but out of the scope of this answer and issue can be avoided using object.findOrCreate() as following
Versions: "mysql2": "^2.3.3", "sequelize": "^6.28.0"
To avoid the issue try using following approach
const router = require("express").Router();
const { Sequelize, DataTypes, Model } = require("sequelize");
const dotenv = require("dotenv");
dotenv.config();
const sequelize = new Sequelize(
process.env.MYSQL_DB_NAME,
process.env.MYSQL_DB_USER,
process.env.MYSQL_DB_PASS,
{
host: process.env.MYSQL_DB_HOST,
dialect: "mysql",
}
);
class User extends Model {}
User.init(
{
userid: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
field: "fUserID",
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
field: "fEmail",
},
password: {
type: DataTypes.STRING(1024),
allowNull: false,
field: "fPassword",
},
firstname: {
type: DataTypes.STRING,
field: "fFirstName",
},
lastname: {
type: DataTypes.STRING,
field: "fLastName",
},
metadata: {
type: DataTypes.STRING(2048),
field: "fMetaData",
},
created: {
type: DataTypes.DATE,
field: "fCreated",
},
updated: {
type: DataTypes.DATE,
field: "fUpdated",
},
},
{
sequelize,
tableName: "tbl_user",
timestamps: true,
id: "userid",
createdAt: "created",
updatedAt: "updated",
}
);
router.post("/register", async (req, res) => {
try {
const [user, created] = await User.findOrCreate({
where: { email: req.body.email },
defaults: {
password: req.body.password,
firstname: req.body.firstname,
lastname: req.body.lastname,
metadata: "Any thing",
},
});
if (created === false) return res.status(400).send("email already exist");
res.send(user.toJSON());
} catch (ex) {
res.status(400).send(ex.errors[0].message);
}
});
module.exports = router;

No Sequelize instance passed for discordjs

https://github.com/qaiswaz/ticket
hear is the code please help me I've bean searching for a solution for a week now.
every time i try to run the bot hear is what it says
bot is online
Executing (default): SELECT 1+1 AS result
connected to DB
Error: No Sequelize instance passed
at Function.init (C:\Users\qaisw\OneDrive\Desktop\ALL FILES\discording\ticket\node_modules\sequelize\lib\model.js:921:13)
at Function.init (C:\Users\qaisw\OneDrive\Desktop\ALL FILES\discording\ticket\models\TicketConfig.js:5:20)
at C:\Users\qaisw\OneDrive\Desktop\ALL FILES\discording\ticket\bot.js:13:22
at processTicksAndRejections (internal/process/task_queues.js:88:5)
bot.js
require('dotenv').config();
const { Client } = require('discord.js');
const client = new Client({ partials: ['MESSAGE', 'REACTION'] });
const db = require('./database');
const Ticket = require('./models/Ticket');
const TicketConfig = require('./models/TicketConfig');
client.once('ready', () => {
console.log('bot is online');
db.authenticate()
.then(() => {
console.log('connected to DB');
Ticket.init(db);
TicketConfig.init(db);
Ticket.sync();
TicketConfig.sync();
}).catch((err) => console.log(err));
});
client.on('message', async message => {
if (message.author.bot || message.channel.type === 'dm') return;
if (message.content.toLowerCase() ==='?setup' && message.guild.ownerID === message.author.id) {
try {
const filter = (m) => m.author.id === message.author.id;
message.channel.send('please enter the message id for this ticket');
const msgId = (await message.channel.awaitMessages(filter, { max: 1})).first().content;
console.log(msgId)
const fetchMsg = message.channel.messages.fetch(msgId);
message.channel.send('please enter the category id for this ticket');
const categoryId = (await message.channel.awaitMessages(filter, { max: 1})).first().content;
console.log(categoryId)
const categoryChannel = client.channels.catche.get(categoryId);
message.channel.send('please enter all of the roles that have access to tickets');
const roles = (await message.channel.awaitMessages(filter, {max: 1})).first().content.split(/,\s*/);
if (fetchMsg && categoryChannel) {
for (const roleId of roles)
if (!message.guild.roles.cache.get(roleId)) throw new Error('role does not exist');
const ticketConfig = await TicketConfig.create({
messageId: msgId,
guildId: message.guild.id,
roles: json.stringify(roles),
parentId: categoryChannel.id
});
message.channel.send('saved config to db');
}else throw new error('invaild fields');
}catch (err) {
}
}
});
client.login(process.env.BOT_TOKEN);
database.js
const { Sequelize } = require('sequelize');
module.exports = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, {
dialect: 'mysql',
host: process.env.DB_HOST
});
*models/Ticket.js
const { DataTypes, Model } = require('sequelize');
module.exports = class Tickets extends Model {
static init(sequelize) {
return super.init( {
ticketId: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
channelId: {
type: DataTypes.STRING,
},
guildId: {
type: DataTypes.STRING,
},
resolved:{
type: DataTypes.BOOLEAN,
},
closedMessageId:{
type: DataTypes.STRING,
},
authorId:{
type: DataTypes.STRING,
},
},
{
sequelize: sequelize,
modelName: 'Ticket'
})
}
}
models/TicketConfig.js
const { DataTypes, Model } = require('sequelize');
module.exports = class TicketConfig extends Model {
static init(sequelize) {
return super.init({
messegeId: {
type: DataTypes.STRING,
primaryKey: true
},
guildld: {
type: DataTypes.STRING
},
roles: {
type: DataTypes.STRING
},
parentld: {
type: DataTypes.STRING
},
sequelize: sequelize,
modelName: 'TicketConfig',
})
}
}
I added sequelizeInstance while define model
*models/Ticket.js
const { DataTypes, Model } = require('sequelize');
const sequelizeInstance = require('./database');
module.exports = class Tickets extends Model {
static init(sequelize) {
return super.init( {
ticketId: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
channelId: {
type: DataTypes.STRING,
},
guildId: {
type: DataTypes.STRING,
},
resolved:{
type: DataTypes.BOOLEAN,
},
closedMessageId:{
type: DataTypes.STRING,
},
authorId:{
type: DataTypes.STRING,
},
},
{
sequelize: sequelizeInstance, // <-
modelName: 'Ticket'
})
}
}
models/TicketConfig.js
const { DataTypes, Model } = require('sequelize');
const sequelizeInstance = require('./database')
module.exports = class TicketConfig extends Model {
static init(sequelize) {
return super.init({
messegeId: {
type: DataTypes.STRING,
primaryKey: true
},
guildld: {
type: DataTypes.STRING
},
roles: {
type: DataTypes.STRING
},
parentld: {
type: DataTypes.STRING
},
sequelize: sequelizeInstance, // <-
modelName: 'TicketConfig',
})
}
}

SQL to Sequelize

Sequelize was going great, but I stopped in a problem, how could I make this statement valid for Sequelize, I was testing the COUNT functions and reading the documentation.
SELECT videos.*, COUNT(*) AS views FROM views INNER JOIN videos WHERE views.videoId = videos.id GROUP BY videoId ORDER BY views DESC
I tried
const data = await Views.count({
group: 'videoId',
include: [
{
model: Videos
},
],
});
but COUNT ignores the 'include' attributes, obviously I'm not surprised that it doesn't work but I couldn't get something like that.
Model: Video.js
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Video extends Model {
}
Video.init({
title: { type: DataTypes.STRING, },
}, {
sequelize,
modelName: "Video",
defaultScope: {
attributes: { exclude: ["createdAt", "updatedAt"] }
},
});
return Video;
};
Model: View.js
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class View extends Model {
static associate(models) {
this.belongsTo(models.Video, {
foreignKey: "videoId",
as: 'video'
});
}
}
View.init({
videoId: { type: DataTypes.INTEGER, },
}, {
sequelize,
modelName: "View",
defaultScope: {
attributes: { exclude: ["createdAt", "updatedAt"] }
},
});
return View;
};
Controller:
var v = await db.View.findAll({
attributes: [
[Sequelize.fn(`COUNT`, Sequelize.col("videoId")), 'count'],
],
group: ['videoId'],
include: ['video'],
})
console.log(JSON.parse(JSON.stringify(v)))
// output
[ { count: 8, video: { id: 1, title: 'k1' } } ]

Sequelize include options cannot get another models

I have two model that are belongs to each other (order_items.js & products.js) productId as a foreign key in order_items, code as below:
order_items.js
const { DataTypes } = require('sequelize')
const db_config = require(`../config/config`)
const Product = require('./product')
const OrderItem = db_config.define('order_item', {
productId : { type: DataTypes.INTEGER, allowNull:false, references: {model: Product, key: 'id'} },
quantity: { type: DataTypes.INTEGER }
}, {
freezeTableName: true
})
module.exports = OrderItem
product.js
const { DataTypes } = require('sequelize')
const db_config = require(`../config/config`)
const Category = require('./category')
const Product = db_config.define('product', {
productName : { type: DataTypes.STRING, allowNull:false },
productPrice: { type: DataTypes.INTEGER, allowNull:false },
productDescription: { type: DataTypes.STRING, allowNull:true },
productImage: { type: DataTypes.STRING, allowNull:true },
productStock: { type: DataTypes.INTEGER, validate: { min: 0 }, defaultValue: 0, allowNull: false },
CategoryId: { type: DataTypes.INTEGER, allowNull:false, defaultValue: 1, references: {model: Category, key: 'id'}}
}, {
freezeTableName: true
})
module.exports = Product
order_routes.js
router.get('/', async (req, res) => {
try {
const dataList = await OrderItem.findAll({include: [{model:Product, required:true}]})
res.send({
status: "success",
message: "data found",
data: dataList
})
} catch (err) {
res.send({
status: "failed",
message: err})
}
})
Result in postman
Can anyone help please? what I'm trying to do is that when I get the order_item, it also get the products refers to the id of the products
Where are the associations in the model definition? I see a reference on column field but you also needs to do below definitions seperately
Inside OrderItem Model File
OrderItem.associate = models => {
OrderItem.belongsTo(Product, {as: "product", foreignKey: "productId", sourceKey: "id"});
};
Inside Products Model File
Product.associate = models => {
Product.hasMany(OrderItem, {as: "orders", foreignKey: "productId", sourceKey: "id"});
};
Also I would suggest you to store price in OrderItem collection as well so in case in future when the product price changes your past order data is not incorrect.

Able to make migration with associations but not able to query with associations (association not found)

I've built a database with a couple tables that are associated with each other: Auction and Bids. Each Auction should have many Bids and each Bid should have only one Auction. I made a migration fine to add foreignKeys but when I try to look up a certain Bid on an Auction I receive a SequelizeEagerLoadingError: Bids is not associated to Auctions! error.
migrations file:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn(
'Auctions', // name of target model
'BidId',
{
type: Sequelize.INTEGER,
references:{
model: "bids",
key: "bid_id",
},
},
);
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Auction');
}
};
bids.js
const Sequelize = require('sequelize');
// const resolver = require('graphql-sequelize');
const sequelize = require('../config/database');
const Auction = require('./Auction');
const tableName = 'bids';
const Bids = sequelize.define('Bids', {
bid_id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
createdAt: {
type: Sequelize.DATE,
// defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
},
amount: {
type: Sequelize.INTEGER
},
bid_amount: {
type:Sequelize.STRING
},
bid_no: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
},
},
{tableName})
Bids.associate = () => {
Bids.hasOne(Auction, {foreignKey:"BidId"})
};
auction.js
const Sequelize = require('sequelize');
const sequelize = require('../config/database');
const tableName = 'Auctions';
const Auction = sequelize.define('Auctions', {
auc_id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4, // generate the UUID automatically
primaryKey: true,
},
features: {
type: Sequelize.JSONB,
},
bid_amount: {
type:Sequelize.STRING
},
BidId: {
type: Sequelize.UUID,
}
}, { tableName });
Auction.hasMany(Bids, {foreignKey: 'BidId'})
module.exports = Auction
query.js
const findBidOnAuction = () => {Auction.findOne({where:{BidId:2}, include:[{model:Bids}]}).then(data => console.log("result", data))}
How do I properly associate these tables?
edit: Also on pgAdmin I can that the relationship exists, BidId is a foreignKey on Auction linked to bid_id on Bids
I do the following for add a foreignKey on migrations:
1.I create a migration for the parent model with a ref function.
const TABLE_NAME = 'parent';
function up(queryInterface, Sequelize) {
return queryInterface.createTable(
TABLE_NAME, //Bid model
{
//attributes
},
);
}
function down(queryInterface, Sequelize) {
return queryInterface.dropTable(TABLE_NAME);
}
function ref(Sequelize) {
return {type: Sequelize.STRING(2), references: {model: TABLE_NAME, key: 'id'}}; // 'id' here is your parent (Bids) primary key
}
module.exports = {up, down, ref};
2.On the child model where you are going to add the reference, you import the ref function and added like this, so your migration actually knows which model are you refering:
const {ref: REF_Model} = require('name-of-your-migration-file'); //import model
async function up(queryInterface, Sequelize){
await queryInterface.addColumn('Auctions', 'BidId', {...REF_Model(Sequelize), allowNull: true});
}
async function down(queryInterface){
await queryInterface.removeColumn('Auctions', 'BidId');
}
module.exports = {up, down};