Sequelize Join with Count of matching id - javascript

I have two models, Post and PostLikes, where PostLikes.postId references Post.id. I am trying to run Post.findAll({}) where PostLike.postId = Post.id. I have tried many, many things and have been unable to get anywhere. Here is what I tried last, which shows all of the info from Post, but LikeCount is 0.
await Post.findAll({
attributes: [
"id",
"username",
"title",
"body",
"createdAt",
"updatedAt",
[sequelize.fn("COUNT", sequelize.col("PostLikes.postId")), "LikeCount"]
],
include: [
{
model: PostLike,
attributes: []
}
],
group: ["Post.id"]
})
Edit: As requested, here are my model defs.
Post:
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define(
"Post",
{
title: DataTypes.STRING,
body: DataTypes.TEXT,
userId: DataTypes.INTEGER,
username: DataTypes.STRING
},
{}
);
Post.associate = function(models) {
Post.belongsTo(models.User, {
foreignKey: "username",
onDelete: "cascade"
});
Post.belongsTo(models.User, {
foreignKey: "userId",
onDelete: "cascade"
});
Post.hasMany(models.Comment, { foreignKey: "id" });
Post.hasMany(models.PostLike, { foreignKey: "id" });
};
return Post;
};
PostLike:
module.exports = (sequelize, DataTypes) => {
const PostLike = sequelize.define(
"PostLike",
{
liked: DataTypes.BOOLEAN,
userId: DataTypes.INTEGER,
postId: DataTypes.INTEGER
},
{}
);
PostLike.associate = function(models) {
PostLike.belongsTo(models.Post, {
foreignKey: "postId",
onDelete: "cascade"
});
PostLike.belongsTo(models.User, {
foreignKey: "userId",
onDelete: "cascade"
});
};
return PostLike;
};

So there seems to be something wrong with this setup, i never setup my associations this way(sequelize is very confusing and the docs seem to be inconsistent with their examples), so it's hard to tell. But i've played with your models, and i was getting some constraint error, when i was trying to add more than one PostLike. I changed it a bit, and got it to work( i removed the user references, being that they are irrelevant for the problem here):
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define(
"Post",
{
title: DataTypes.STRING,
body: DataTypes.TEXT,
userId: DataTypes.INTEGER,
username: DataTypes.STRING
},
{}
);
Post.associate = function (models) {
Post.hasMany(models.PostLike);
};
return Post;
};
module.exports = (sequelize, DataTypes) => {
const PostLike = sequelize.define(
"PostLike",
{
liked: DataTypes.BOOLEAN,
userId: DataTypes.INTEGER,
},
{}
);
PostLike.associate = function(models) {//
PostLike.belongsTo(models.Post);
};
return PostLike;
};
After inserting one post, and two postLikes, this is the result:

Related

How to get only the associated records using Sequelize ORM Node js

I have two models.
1 - Size
2 - Stock
Size Model
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const Size = sequelize.define("Size", {
type: DataTypes.STRING,
});
// associations can be defined here
Size.associate = function (models) {
// Size --> Stock
Size.hasMany(models.Stock, { as: "sizes", foreignKey: "id" });});
};
return Size;
};
Stock Model
```"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const Stock = sequelize.define("Stock", {
startingStock: {
type: DataTypes.INTEGER,
allowNull: false,
},
total: DataTypes.INTEGER,
sizeId: { type: DataTypes.INTEGER, foreignKey: "sizeId" },
});
// associations can be defined here
Stock.associate = function (models) {
Stock.belongsTo(models.Size, {
as: "sizes",
foreignKey: "sizeId",
onDelete: "CASCADE",
});
};
return Stock;
};```
How can it get only size records that are associated to stock.
Only sizes that have relation with stocks.
```Size.findAll({
include: [
{
model: Stock,
as: "sizes",
where: {
sizeId: Sequelize.col("Size.id"),
},
},
],
}); ```
I am using this query but it give me all sizes including that don't have associated record in Stock.
How can I query only those record that has associated records.
Looks like you want to do an inner join.
Add the required field
Size.findAll({
include: [
{
required: true
model: Stock,
as: "sizes",
where: {
sizeId: Sequelize.col("Size.id"),
},
},
],
});

Sequelize findOrCreate including associates - "Cannot read property 'field' of undefined"

Hello I'm using the findOrCreate method to insert records, but it seems that the method does not work on include associates. Getting error if the include associate exists in DB
Using the following inputs.
Cannot read property 'field' of undefined
First Attemp (Successful, relationship also created in the "through" table )
[
{
"album_type": "compilation",
"id": "21132268",
"name": "Album One",
"authors": [
{
"name": "Node Js",
"suffix": "Dr."
}
]
}
]
Second Attempt Failed (Having a new Album but with the same author(s))
[
{
"album_type": "compilation",
"id": "23398868",
"name": "Album Two",
"authors": [
{
"name": "Node Js",
"suffix": "Dr."
}
]
}
]
Error:
"Cannot read property 'field' of undefined",....... "TypeError: Cannot
read property 'field' of undefined at
options.defaults.Object.keys.map.name ...........
Here's the DB Functon
var createAlbum = async (albums) => {
try {
// transaction handle
return await models.sequelize.transaction (t => {
// Prepare each album to be created
return Promise.all(albums.map(album => {
return models.Album.findOrCreate({
transaction: t,
where: {name: album.name}, // where: {id: album.id}
defaults: album,
include: ['authors']
})
}))
});
} catch (error) {
// transaction will be rolled back if error
logger.error(error);
throw error;
}
}
Models
//album.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var Album = sequelize.define('Album', {
id: { type: DataTypes.INTEGER, primaryKey: true, allowNull: false },
name: { type: DataTypes.STRING, primaryKey: true, allowNull: false },
album_type: { type: DataTypes.STRING, allowNull: false },
});
Album.associate = (models) => {
models.Album.belongsToMany(models.Author, {
through: 'AlbumAuthor',
as: 'authors',
foreignKey: 'album_id',
otherKey: 'author_name'
})
}
return Album;
}
//author.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var Author = sequelize.define('Author', {
name: { type: DataTypes.STRING, primaryKey: true, allowNull: false },
suffix: { type: DataTypes.STRING, allowNull: true }
});
return Author;
}
//album_author.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var AlbumAuthor = sequelize.define('AlbumAuthor', {
album_id: { type: DataTypes.INTEGER, primaryKey: true, allowNull: false },
author_name: { type: DataTypes.STRING, primaryKey: true, allowNull: false }
});
AlbumAuthor.associate = (models) => {
models.AlbumAuthor.belongsTo(models.Album, {
onDelete: 'CASCADE',
foreignKey: 'album_id',
targetKey: 'id',
}),
models.AlbumAuthor.belongsTo(models.Author, {
onDelete: 'CASCADE',
foreignKey: 'author_name',
targetKey: 'name'
})
}
return AlbumAuthor;
}

Returning full list after post in sequelize

I am using the Sequelize ORM and want to make a post request in order to create what I call a 'card'. The body of the post request would, for example, look like this:
{
"card": {"title": "cardTitle", "link": "cardLink", "categoryId": 1, "userId": 1},
"tags": [{"title": "tagA", "userId": 1}, {"title": "tagB", "userId": 1}, {"title": "tagC", "userId": 1}]
}
After making this post (in the create function), I want the full list of cards to be returned, as seen in the list function. I am not sure how to do this, especially since I am iterating through each individual Card to create a m:m join. Please see the controller below. Thanks!
const Card = require('../models').card;
const Tag = require('../models').tag;
const CardTag = require('../models').card_tag;
const Category = require('../models').category;
module.exports = {
create(req, res) {
Promise.all([
Card.create(
{
title: req.body.card.title,
link: req.body.card.link,
categoryId: req.body.card.categoryId,
userId: req.body.card.userId
},
{returning: true}
),
Tag.bulkCreate(req.body.tags, {returning: true})
])
.then(([Card, Tag]) =>
Tag.map(tag =>
CardTag.create({cardId: Card.id, tagId: tag.id})
),
// How do I instead list all the Cards, just like I do in the list function below?
res.status(201).send({message: "Card created"})
)
.catch(error => res.status(400).send(error));
},
list(req, res) {
return Card
.findAll({
attributes: ['id', 'title', 'link', 'createdAt', 'updatedAt'],
include: [
{model: Category, attributes: ['title']},
{model: Tag, as: 'tag', attributes: ['title'], through: {attributes: []}}
]})
.then(cards => res.status(200).send(cards))
.catch(error => res.status(400).send(error));
}
};
Models:
card:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Card = sequelize.define('card', {
title: {
type: DataTypes.STRING,
allowNull: false
},
link: {
type: DataTypes.STRING,
allowNull: false
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
}
});
Card.associate = (models) => {
Card.belongsToMany(models.tag, {through: 'card_tag', as: 'tag'});
Card.belongsTo(models.category);
Card.belongsTo(models.user);
};
return Card;
};
card_tag:
'use strict';
module.exports = function(sequelize, DataTypes) {
const CardTag = sequelize.define('card_tag', {
cardId: DataTypes.INTEGER,
tagId: DataTypes.INTEGER
});
return CardTag;
};
category:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Category = sequelize.define('category', {
title: {
type: DataTypes.STRING,
allowNull: false
}
});
Category.associate = (models) => {
};
return Category;
};
tag:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Tag = sequelize.define('tag', {
title: {
type: DataTypes.STRING,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
}
});
Tag.associate = (models) => {
Tag.belongsToMany(models.card, { through: 'card_tag', as: 'card'});
Tag.belongsTo(models.user);
};
return Tag;
};

Sequelize join data in tree

I have 3 models that work like a tree: Plants, Genre and family.
Each family can have a lot of genres each genre is associated to 1 family.
Same for Genre, each 1 can have a lot of plants and 1 plant can have 1 genre.
So based on that, i have this models:
Plant
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
var User = require('./User');
module.exports = function (sequelize, DataTypes) {
var Plant = sequelize.define("Plant", {
specie: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: "No description for this plant yet"
},
directory: {
type: DataTypes.STRING,
allowNull: false
},
genreId: {
type: DataTypes.INTEGER,
allowNull: true
}
},
{
associate: function (models) {
Plant.hasMany(models.Foto, { foreignKey: "plantId", as: 'fotos' });
}
}
);
Genre
module.exports = function (sequelize, DataTypes) {
var Genre = sequelize.define("Genre", {
name: {
type: DataTypes.STRING,
allowNull: false
},
familyId: {
type: DataTypes.INTEGER,
allowNull: true
},
directory: {
type: DataTypes.STRING,
allowNull: false
}
},
{
associate: function (models) {
Genre.hasMany(models.Plant, { foreignKey: "genreId", as: 'plant' });
}
}
);
Family
module.exports = function (sequelize, DataTypes) {
var Family = sequelize.define("Family", {
name: {
type: DataTypes.STRING,
allowNull: false
},
directory: {
type: DataTypes.STRING,
allowNull: false
}
},
{
associate: function (models) {
Family.hasMany(models.Genre, { foreignKey: "familyId", as: 'genre' });
}
}
);
now, i do a query where i want to get all data related to the plant(genre and family) so i pass the id for the plant in the routing, via req.params.id.
after that i try to do a include so i can get the data with eager loading, because i need to get a json with all the data related to the plant.
But i can't get any data related to the other models, just with the specific plant table, any help?
Here is the controller code on the server:
specificPlant: function (req, res, next) {
Plant.findAll({
where: {
id: req.params.id,
},
include: [{ all: true }]
}).then(function (plant) {
console.log(plant);
return res.send(plant);
}).catch(function (err) {
return res.status(400).send({ message: err.stack }); //
})
}
First, define associations that will allow you to get data Plant->Genre->Family
Plant.hasMany(models.Genre, {foreignKey: "genreId", as: 'genre' });
Genre.hasMany(models.Family, { foreignKey: "familyId", as: 'family' });
Then you can query
Plant.findAll({
where: {
id: req.params.id,
},
include: [{
model: Genre,
as: 'genre',
include: [{
model: Family,
as: 'family'
}]
}]
}).then(function (plant) {
//plant
//plant.genre
//plant.genre.family
});

Multiple foreign keys on sequelize (one-to-many or many-to-many)?

I have two tables: users and alerts. Users should be able to upvote and downvote on alerts.
My models are defined like that:
Alert
module.exports = function(sequelize, DataTypes) {
var Alert = sequelize.define("alert", {
ID: { type: DataTypes.BIGINT(11).UNSIGNED, primaryKey: true, autoIncrement: true },
title: DataTypes.STRING,
alert_type: DataTypes.INTEGER
},
{
classMethods: {
associate: function(models) {
Alert.belongsTo(models.user, {
onDelete: "CASCADE",
foreignKey: {
allowNull: false
}
});
}
}
});
return Alert;
};
User
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("user", {
ID: { type: DataTypes.BIGINT(11).UNSIGNED, primaryKey: true, autoIncrement: true },
user_login: DataTypes.STRING(50),
user_pass: DataTypes.STRING(50),
user_email: DataTypes.STRING(50),
},
{
classMethods: {
associate: function(models) {
User.hasMany(models.alert);
}
}
});
return User;
};
What's the way of doing it using sequelize.js and MySQL(InnoDB)?
Should I use belongToMany() ?
Something like :
module.exports = function(sequelize, DataTypes) {
var AlertVote = sequelize.define("user_alert_vote", {
ID: { type: DataTypes.BIGINT(11).UNSIGNED, primaryKey: true, autoIncrement: true },
vote_up: DataTypes.INTEGER,
vote_down: DataTypes.INTEGER
},
{
classMethods: {
// belongToMany(models.alert, ...);
// belongToMany(models.user, ...);
}
});
return AlertVote ;
};
You have here a 'many-to-many' relationship between a User and an Alert:
A User can vote zero or many Alerts
An Alert can be voted by zero or many Users
In addition, the relationship has properties that are upVote and downVote (I will not discussion your design to have these two properties as I'm not aware about the details of the use case).
As such, you need to use .belongToMany() on User and Alert.
A abbreviated code would be as follows:
var User = sequelize.define('User', {})
var Alert = sequelize.define('Alert', {})
var AlertVote = sequelize.define('AlertVote', {
upVote: DataTypes.INTEGER,
downVote: DataTypes.INTEGER,
})
Alert.belongsToMany(User, {through: 'AlertVote'});
User.belongsToMany(Alert, {through: 'AlertVote'});
For more details, please refer to Belongs-To-Many associations from the official documentation.

Categories