my models looks like this:
user belongs to many categories
post belongs to many categories
through a joint tables of UserCategories and PostCategories
How do I select all posts which has the same categories as user ( for all categories which user is related ).
Get all categories for user.
const userCategory = await UserCategory.findAll({
where: {
userId: 1,
},
raw: true,
});
Get all posts for user categories.
const posts = await PostCategory.findAll({
include: [
{
model: Post,
required: true,
},
{
model: Category,
required: true,
include: {
model: UserCategory,
required: true,
include: {
model: User,
required: true,
where: {
id: userCategory.map(item => item.categoryId),
},
},
},
},
],
});
Related
I am trying to findAll records included nested tables, but did not understand how could I filter included tables by where clause. Here are the words:
const players = await PlayerService.findPlayers({
attributes: { exclude: ['password'] },
include: [
{ all: true },
{
model: Team,
as: 'captainTeams',
attributes: {exclude: ['createdAt', 'updatedAt']}
},
{
model: Team,
as: 'teams',
where: { type: 1 },
required: true,
through: {attributes: []},
attributes: {exclude: ['createdAt', 'updatedAt']}
}
]
})
Here is the result:
If I delete where and required from including Team clause, here is the result:
I would like to filter teams.type=2. Could you help me ?
I would like to know how can I "merge" the result to get result from 2 tables.
Currently I have 3 tables :
posts [id, title...]
feeds [id, fk_people_id, fk_post_id]
posts_peoples [id, fk_people_id, fk_post_id]
I would like to return the posts where people is present in feeds table and posts_peoples table.
When I run this request, I have only the post where people is present in feeds table :
// Request
const resultRequest = await db.Post.findAll({
include: [
{
model: db.Feed,
as: "Feed",
where: {
fk_people_id: 2,
},
},
],
})
When I run this request, I have only the post where people is present in posts_peoples table :
// Request
const resultRequest = await db.Post.findAll({
include: [
{
model: db.PostPeople,
as: "PostPeople",
where: {
fk_people_id: 2,
},
},
],
})
When I add feeds and posts_peoples, it doesn't work.
// Request
const resultRequest = await db.Post.findAll({
include: [
{
model: db.Feed,
as: "Feed",
where: {
fk_people_id: 2,
},
},
{
model: db.PostPeople,
as: "PostPeople",
where: {
fk_people_id: 2,
},
},
],
})
The result is an empty array.
Add required: false to your includes to generate SQL with a LEFT JOIN to include results from both tables.
// Request
const resultRequest = await db.Post.findAll({
include: [{
model: db.Feed,
as: "Feed",
where: {
fk_people_id: 2,
},
required: false,
},
{
model: db.PostPeople,
as: "PostPeople",
where: {
fk_people_id: 2,
},
required: false,
}],
})
I'm trying to get the information about a user and include all the reservation he has done as well as the total sales.
I'm trying to add a before get hook and include both reservations and sales. When I include just one of these it gets the COUNT right, but when I add both, it actually counts reservations and sales and adds them together for each one.
get: [
authenticate("jwt"),
context => {
context.params.sequelize = {
include: [
{
association: "reservacion",
required: false,
attributes: [[Sequelize.fn("COUNT", Sequelize.col("reservacion.id")), "total_res"]]
},
{
association: "venta",
required: false,
attributes: [[Sequelize.fn("COUNT", Sequelize.col("venta.id")), "ventas"]]
}
]
};
return context;
}
],
If you use DISTINCT you should avoid the problem of accumulation of results
get: [
authenticate("jwt"),
context => {
context.params.sequelize = {
include: [
{
association: "reservacion",
required: false,
attributes: [[Sequelize.fn("COUNT", Sequelize.fn("DISTINCT", Sequelize.col("reservacion.id")), "total_res"]]
},
{
association: "venta",
required: false,
attributes: [[Sequelize.fn("COUNT", Sequelize.fn("DISTINCT", Sequelize.col("venta.id"))), "ventas"]]
}
]
};
return context;
}
],
So, I have an existing MySQL database that I'm trying to connect to with Sequelize in Node that has a products table, a categories table and a categories_products table. What I want to do is return products, with each product containing all of the categories it belongs to. Here's what I've got:
// Declare Product Model
const Product = sequelize.define('products', {
name: Sequelize.STRING,
description: Sequelize.STRING,
single_price: Sequelize.BOOLEAN,
oz_price: Sequelize.FLOAT,
half_price: Sequelize.FLOAT,
quarter_price: Sequelize.FLOAT,
eigth_price: Sequelize.FLOAT,
gram_price: Sequelize.FLOAT,
unit_price: Sequelize.FLOAT
},
{
underscored: true
});
// Declare Category Model
const Category = sequelize.define('categories', {
name: Sequelize.STRING,
parent_id: Sequelize.INTEGER,
picture_file_name: Sequelize.STRING
},
{
underscored: true
});
// Join Table
const ProductCategory = sequelize.define('categories_products', {
product_id: Sequelize.INTEGER,
category_id: Sequelize.INTEGER,
}, {
timestamps: false,
underscored: true
});
// Do this because there is no id column on ProductCategory table
ProductCategory.removeAttribute('id');
Category.hasMany(Category, { as: 'children', foreignKey: 'parent_id' });
ProductCategory.belongsTo(Product);
ProductCategory.belongsTo(Category);
Product.hasMany(ProductCategory);
Category.hasMany(ProductCategory);
Using this setup, I query as follows:
Product.findAll({
include: [{
model: ProductCategory,
include: [ Category ]
}],
where: { active: true },
limit: 10
}).then(prods => {
res.send(prods);
}).catch(err => {
res.status(500).send(err);
});
I get back my products and each one has an array of categories, BUT each product only shows a max of one category. I have products that should have many categories, but it only shows the first.
Am I missing something? Any help would be greatly appreciated.
I think you should use belongsToMany association here.
You can define association like this
Product.belongsToMany(Category, { through: ProductCategory, foreignKey: 'product_id' });
Category.belongsToMany(Product, { through: ProductCategory, foreignKey: 'category_id' });
and the query can be
Product.findAll({
include: [Category]
}).then((res) => {
console.log(res);
})
Though the questioner might have gotten the solution but I ran into this composite key table problem and this is the solution with code example. Notice the "through" keyword. That is what solves the association where you want to limit your findings to say a category as AbhinavD asked above. Your category id would go in the literal expression. Applies to findAll too.
const products = await Product.findAndCountAll({
include: [Category],
through: { where: { category_id: `${category_id}` } },
attributes: [
'product_id',
'name',
],
limit: limitPage,
offset: offsett,
});
I'm building a shop, where in the database i have orders and items. Here's the code for the models:
Item:
var Sequelize = require('sequelize')
var sequelize = require('./sequelize')
var Item = sequelize.define('item', {
image: {
type: Sequelize.STRING,
allowNull: false
},
itemName: {
type: Sequelize.STRING,
allowNull: false,
field: 'item_name'
},
price: {
type: Sequelize.INTEGER,
allowNull: false
}
})
module.exports = Item
Order:
var Sequelize = require('sequelize')
var sequelize = require('./sequelize')
var Order = sequelize.define('order', {
orderNumber: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
},
shop: {
type: Sequelize.INTEGER
},
location: {
type: Sequelize.STRING
}
})
module.exports = Order
They are related through belongs to many:
Item.belongsToMany(Order, {through: OrderItem})
Order.belongsToMany(Item, {through: OrderItem})
The OrderItem has an additional field, 'count', which i need to return:
var Sequelize = require('sequelize')
var sequelize = require('./sequelize')
var OrderItem = sequelize.define('OrderItem', {
count: Sequelize.INTEGER
})
module.exports = OrderItem
However, when i try to include the OrderItem model, it doesn't work. No errors, nothing. The query just doesn't return:
Order.findAll({
where: {
userId: userId
},
include: [{
model: Item,
include: [OrderItem]
}]
}).then(orders => {
console.log(orders)
res.status(200).json(orders)
})
How to get what i need from sequeilize?
You may try something like this:
Order.findAll({
where: {
userId: userId
},
include: [{ model: Item,
as:'item',
through:{attributes:['count']} // this may not be needed
}]
}).then(orders => {
console.log(orders)
res.status(200).json(orders)
})
Also, your models must have a right naming strategy. E.g. :
Item - must have itemId field instead of itemNumber,
Order - must have orderId as primary field
OrderItem's structure:
var OrderItem = sequelize.define('OrderItem', {
orderId: Sequelize.INTEGER,
itemId: Sequelize.INTEGER,
count: Sequelize.INTEGER
})
Another one is to use direct names of related fields and models when you use belongsToMany()
More here: http://docs.sequelizejs.com/en/latest/docs/associations/
Turns out that the OrderItem is already nested inside the Item object. However, this doesn't make a nice return format , so the question is still open.
I know this is year later, but yet..
You can use the property joinTableAttributes to get a junction table fields .
for example :
Order.findAll({
where: {
userId: userId
},
joinTableAttributes: ['count'],
include: [{
model: Item,
}]
}).then(orders => {
console.log(orders)
res.status(200).json(orders)
})
I am using Graphql and I had the same problem with the formatting. I did a small map after getting the result.
For example:
let orders = await Order.findAll({
where: {
userId: userId
},
include: [{ model: Item,
as:'item',
through:{attributes:['count']}
}]
});
let ordersWithCount = orders.map((o) => {
let orderWithCount = {};
orderWithCount = o;
orderWithCount.count = o.OrderItem.count;
return orderWithCount;
});