subquery Sequelize with nested query (include) - node.js

I have this two models :
const Category = sequelize.define('Category', {
name: {
type: DataTypes.STRING,
unique: true
},
description: {
type: DataTypes.STRING,
},
status: {
type: DataTypes.BOOLEAN,
default: true
},
image: DataTypes.STRING,
}, {});
Category.associate = function (models) {
Category.hasMany(models.Product)
Category.hasMany(models.Category, { as: 'child', foreignKey: 'ParentId' });
Category.belongsTo(models.Category, { as: 'parent', foreignKey: 'ParentId' });
};
return Category;
const Product = sequelize.define('Product', {
name: { type: DataTypes.STRING, unique: true },
description: { type: DataTypes.STRING },
price: { type: DataTypes.INTEGER },
status: {
type: DataTypes.BOOLEAN,
default: true
},
image: DataTypes.STRING,
}, {});
Product.associate = function (models) {
Product.belongsTo(models.Category)
};
return Product;
nothing fancy here .my category model can has a CategoryId for nested purposes.so my question is is there a way I can have a subquery in Category model that select products with a related category Id?
Category.findAll({
limit: page_size,
where: {
ParentId: {
[sequelize.Op.eq]: null
}
},
include: [{
model: Category,
as: "child",
attributes: [
[sequelize.literal('(SELECT * FROM "Products" WHERE "CategoryId" = "child"."id")'),
'products'],
],
group: ['products']
}],
offset: offset_value,
order: [
['id', 'DESC'],
],
})
I tried to include and in the include section I need to query to my products that group it as a field.
p.s: if category instance has no CategoryId it will be considered as
Parent.
and I wrote this query and tested it comes with this error:
subquery must return only one column

Related

multiple inner join at same table sequelize

I'm trying to make a query with multiple joins but when i execute it doesn't work says me
EagerLoadingError [SequelizeEagerLoadingError]: tbl_admon_brands is associated to tbl_admon_vehicles multiple times. To identify the correct association, you must use the 'as' keyword to specify the alias of the association you want to include
I was looking at the documentation of sequalize but i don't understand how can i make a polymorphic association with my models and controllers
Actually i have my getAll like this
const vehicles = await Vehicle.findAll({
attributes: {
include: [
[sequalize.col("brand.name"), "brandName"],
[sequalize.col("brandChassis.name"), "brandChassisName"],
[sequalize.col("brandEnginer.name"), "brandEnginerName"],
/* [sequalize.col("brandBodyWork.name"), "brandBodyWorkName"],
*/ [
sequalize.col("enginePosition.description"),
"enginePositionName",
],
],
},
include: [
{
model: Brand,
as: "brand",
attributes: [],
required: true,
},
{
model: Brand,
as: "brandChassis",
attributes: [],
required: true,
},
{
model: Brand,
as: "brandEnginer",
attributes: [],
required: true,
},
/*{
model: Brand,
as: "engine",
attributes: ['name'],
required: true,
},*/
{
model: Position,
as: "enginePosition",
attributes: [],
required: true,
},
],
where: {
deleted: 0,
},
logging: console.log
});
but when i unomment the line 7 and/or the as engine is when throw me the error.
My model
const { Sequelize } = require("sequelize");
const sequalize = require("../database/database");
const Models = require("./Models");
const Brand = require("./Brand");
const Position = require("./Position");
const Engines = require("./Engines");
const Vehicle = sequalize.define("tbl_admon_vehicles", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.TEXT,
},
cut: {
type: Sequelize.TEXT,
},
vin: {
type: Sequelize.TEXT,
},
numberEconomic: {
type: Sequelize.TEXT,
},
unitNumber: {
type: Sequelize.INTEGER,
},
brandId: {
type: Sequelize.INTEGER,
},
modelId: {
type: Sequelize.INTEGER,
},
date_acquisition: {
type: Sequelize.DATE,
},
deleted: {
type: Sequelize.INTEGER,
},
brandidchassis: {
type: Sequelize.INTEGER,
},
brandidenginer: {
type: Sequelize.INTEGER,
},
brandidbodywork: {
type: Sequelize.INTEGER,
},
enginepositionid: {
type: Sequelize.INTEGER,
},
typefuelid: {
type: Sequelize.INTEGER,
},
stateplate: {
type: Sequelize.TEXT,
},
federalplate: {
type: Sequelize.TEXT,
},
serie: {
type: Sequelize.TEXT,
},
endorsement: {
type: Sequelize.DATE,
},
unitNumber: {
type: Sequelize.INTEGER,
},
clasificationId: {
type: Sequelize.INTEGER,
},
section: {
type: Sequelize.TEXT,
},
insurerid: {
type: Sequelize.INTEGER,
},
policy: {
type: Sequelize.INTEGER,
},
expedition: {
type: Sequelize.DATE,
},
expiration: {
type: Sequelize.DATE,
},
});
Vehicle.belongsTo(Models, {
foreignKey: "modelId",
as: "model",
});
Vehicle.belongsTo(Brand, {
foreignKey: "brandId",
as: "brand",
});
Vehicle.belongsTo(Brand, {
foreignKey: "brandidchassis",
as: "brandChassis",
});
Vehicle.belongsTo(Brand, {
foreignKey: "brandidenginer",
as: "brandEnginer",
});
Vehicle.belongsTo(Engines, {
foreignKey: "brandidbodywork",
as: "engine",
});
Vehicle.belongsTo(Position, {
foreignKey: "enginepositionid",
as: "enginePosition",
});
module.exports = Vehicle;
my query that i'm trying to do is
SELECT "tbl_admon_vehicles"."id", "tbl_admon_vehicles"."name",
"tbl_admon_vehicles"."cut", "tbl_admon_vehicles"."vin",
"tbl_admon_vehicles"."numberEconomic", "tbl_admon_vehicles"."unitNumber",
"tbl_admon_vehicles"."brandId", "tbl_admon_vehicles"."modelId",
"tbl_admon_vehicles"."date_acquisition", "tbl_admon_vehicles"."deleted",
"tbl_admon_vehicles"."brandidchassis", "tbl_admon_vehicles"."brandidenginer",
"tbl_admon_vehicles"."brandidbodywork", "tbl_admon_vehicles"."enginepositionid",
"tbl_admon_vehicles"."typefuelid", "tbl_admon_vehicles"."stateplate",
"tbl_admon_vehicles"."federalplate", "tbl_admon_vehicles"."serie",
"tbl_admon_vehicles"."endorsement", "tbl_admon_vehicles"."clasificationId",
"tbl_admon_vehicles"."section", "tbl_admon_vehicles"."insurerid",
"tbl_admon_vehicles"."policy", "tbl_admon_vehicles"."expedition",
"tbl_admon_vehicles"."expiration", "tbl_admon_vehicles"."createdAt",
"tbl_admon_vehicles"."updatedAt", "brand"."name" AS "brandName",
"brandChassis"."name" AS "brandChassisName", "brandEnginer"."name"
AS "brandEnginerName", "enginePosition"."description" AS "enginePositionName"
FROM "tbl_admon_vehicles" AS "tbl_admon_vehicles"
INNER JOIN "tbl_admon_brands" AS "brand" ON "tbl_admon_vehicles"."brandId" = "brand"."id"
INNER JOIN "tbl_admon_brands" AS "brandChassis"
ON "tbl_admon_vehicles"."brandidchassis" = "brandChassis"."id"
INNER JOIN "tbl_admon_brands" AS "brandEnginer"
ON "tbl_admon_vehicles"."brandidenginer" = "brandEnginer"."id"
INNER JOIN "tbl_admon_brands" AS "engine"
ON "tbl_admon_vehicles"."brandidbodywork" = "engine"."id"
LEFT OUTER JOIN "tbl_admon_engine_positions" AS "enginePosition"
ON "tbl_admon_vehicles"."enginepositionid" = "enginePosition"."id"
WHERE "tbl_admon_vehicles"."deleted" = 0
I hope someone can helpe or explain me how can i make the polymorphic association

Sequelize fetch include data based on alias where condition

I have two tables Employee and Department
Department
const Department = Sequelize.define(
"Department",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
underscored: true,
timestamps: true,
paranoid: true,
modelName: "Department",
tableName: "departments",
},
);
Department.associate = function (models) {
// associations can be defined here
models.Department.hasMany(models.Employee, {
foreignKey: "department_id",
as: "employees",
});
};
return Department;
};
Employee
const Employee = Sequelize.define(
"Employee",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
status: {
type: DataTypes.STRING,
defaultValue: "active",
},
departmentId: {
type: DataTypes.INTEGER,
},
},
{
underscored: true,
timestamps: true,
modelName: "Employee",
tableName: "employees",
},
);
Employee.associate = function (models) {
models.Employee.belongsTo(models.Department, {
foreignKey: "department_id",
as: "department",
});
};
return Employee;
};
Now I have to fetch the list of employees and putting a filter of department_id = 1
const { departmentId } = req.body;
const employees = await Employee.findAll({
include: [
{
model: Department,
where: {
id: departmentId,
},
},
],
});
I am getting the issue. Department is mapped by association "departments"
Cannot fetch the data.
I found the answer on sequelize docs
const employees = await Employee.findAll({
include: [
{
association: "department", // this is the place to change
where: {
id: departmentId,
},
},
],
});
Learnings:
We will not be able to put association and model together.
We will be able to use the Model if no association is there.
We will be able to use association if there is one.
References: https://sequelize.org/master/manual/eager-loading.html#:~:text=You%20can%20also%20include%20by%20alias%20name%20by%20specifying%20a%20string%20that%20matches%20the%20association%20alias

Sequelize: extra column in query

i have the following table define
product table
const Product = sequelize.define('product', {
name: { type: Sequelize.TEXT },
amount: { type: Sequelize.DECIMAL },
category: { type: Sequelize.BIGINT, field: 'categoryId' },
brand: { type: Sequelize.BIGINT, field: 'brandId' },
vendor: { type: Sequelize.BIGINT, field: 'vendorId' },
status: { type: Sequelize.BIGINT, field: 'statusId' },
image: { type: Sequelize.TEXT },
stock: { type: Sequelize.INTEGER },
description: { type: Sequelize.TEXT },
model: { type: Sequelize.TEXT },
code: { type: Sequelize.TEXT },
});
status table
const Statuses = sequelize.define('statuses', {
name: {
type: Sequelize.TEXT
},
});
associations
Product.belongsTo(Statuses, { foreignKey: 'statusId', as: 'productStatus'});
when i run thhe following:
product = await Product.findOne({
where: {
id: req.query.id
},
include: ['productStatus']
})
I get the following query:
SELECT
"product"."id",
"product"."name",
"product"."amount",
"product"."categoryId" AS "category",
"product"."brandId" AS "brand",
"product"."vendorId" AS "vendor",
"product"."statusId" AS "status",
"product"."image",
"product"."stock",
"product"."description",
"product"."model",
"product"."code",
"product"."createdAt",
"product"."updatedAt",
"product"."statusId",
"product"."categoryId",
"product"."vendorId",
"product"."brandId",
"productStatus"."id" AS "productStatus.id",
"productStatus"."name" AS "productStatus.name",
"productStatus"."createdAt" AS "productStatus.createdAt",
"productStatus"."updatedAt" AS "productStatus.updatedAt",
"productStatus"."status" AS "productStatus.status"
FROM "products" AS "product"
LEFT OUTER JOIN "statuses" AS "productStatus" ON "product"."statusId" = "productStatus"."id"
WHERE "product"."id" = '1'
not sure why
"productStatus"."status" AS "productStatus.status"
is there and it causes an unknown column error since i don't have that column in statuses table
User.hasOne(Status, { foreignKey: 'status', as: 'UserStatus'}) means Status has the status column which is not true. That's why Sequelize knows that Status model has status column and adds it to SQL-query as well.
I suppose User has status column instead of Status. If so, all you need is to change association like this:
User.belongsTo(Status, { foreignKey: 'status', as: 'UserStatus'})

How can I use current query value in sequelize?

I am new with sequelize and i need to include multiple table, i try belove:
const result = await product.findAll({
where: {
isDeleted: false,
sellerId
},
attributes: ["name", "type", "isActive", "subCategoryIds"],
include: [
{
model: category,
as: 'categories',
attributes: ["name"],
},
{
model: category,
as: 'subCategories',
attributes: ["name"],
where: {
id: _.split(sequelize.col("subCategoryIds"), ",")
},
},
],
offset: (page - 1) * limit,
limit
});
But it's return error :
SequelizeDatabaseError: operator does not exist: character varying = integer
Product model is:
const product = sequelize.define("products",
{
id: {
type: DataTypes.BIGINT.UNSIGNED,
field: "id",
primaryKey: true,
autoIncrement: true,
index: true,
},
name: {
type: DataTypes.STRING,
field: "name",
allowNull: false,
},
type: {
type: DataTypes.STRING,
field: "type",
allowNull: false,
},
categoryId: {
type: DataTypes.NUMBER,
field: "categoryId",
references: {
key: "id",
model: category,
},
allowNull: false,
},
sellerId: {
type: DataTypes.NUMBER,
field: "sellerId",
references: {
key: "id",
model: user,
},
allowNull: false,
},
subCategoryIds: {
type: DataTypes.STRING,
allowNull: false,
get() {
const result = _.split(this.getDataValue("subCategoryIds"), ",").map(item => +item);
return result;
}
},
isDeleted: {
type: DataTypes.BOOLEAN,
field: "isDeleted",
defaultValue: false,
},
createdAt: {
type: DataTypes.DATE,
field: "createdAt",
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
field: "updatedAt",
defaultValue: DataTypes.NOW,
}
}, {
tableName: "products",
timestamps: false
}
);
product.belongsTo(category, { as: "categories", foreignKey: 'categoryId' });
category.hasMany(product, { as: "products", foreignKey: 'categoryId' });
product.belongsTo(category, { as: "subCategories", foreignKey: 'subCategoryIds' });
I am not getting what's going wrong
is there any solution for this
details in table like
categoryId => 11 subCategoryIds => 11, 12
if i remove
{
model: category,
as: 'subCategories',
attributes: ["name"],
where: {
id: _.split(sequelize.col("subCategoryIds"), ",")
},
},
then it's working fine, problem is sequelize.col("subCategoryIds") return string col("subCategoryIds") not the actual value of the subCategories ids 11, 12

How to add array of ids in a query with many to many relationships Sequelize

I have 2 entities and one for n:m relationship:
const Item = db.define('item', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true,
},
title: Sequelize.STRING,
description: Sequelize.STRING,
price: {
type: Sequelize.FLOAT,
defaultValue: 0.0,
},
});
const Category = db.define('category', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true,
},
title: Sequelize.STRING,
});
const ItemCategory = db.define('item_category', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true,
},
category_id: {
type: Sequelize.BIGINT
},
item_id: {
type: Sequelize.BIGINT
}
});
And relations:
Category.belongsToMany(Item, {
through: {
model: ItemCategory,
unique: false
},
foreignKey: 'category_id',
constraints: false
});
Item.belongsToMany(Category, {
through: {
model: ItemCategory,
unique: false
},
foreignKey: 'item_id',
constraints: false
});
Association is working fine(I guess). But when I try to query Item, the result comes without categories field.
Also I can add include option and it returns category objects:
Item.findAll({ include: [{ model: Category }] })
The QUESTION IS: How to associate ONLY categories_ids when querying Item objects to have something like this in response:
{
id: 1,
categories_ids: [1,3,4],
price: 20
}
You can't actually do that because of how data of nested associations are arranged by default.
Suppose you did
Item.findOne({
where: { item_id: 1},
include: [{
model: ItemCategory,
as: 'categories_ids',
attributes: ['category_id']
}]
})
You will get
{
id: 1,
categories_ids: [
{ category_id: 1 },
{ category_id: 2 },
{...},
{...}
],
price: 20,
}
Of which you can probably re-arrange the information, which involves the process of something like this:
let pojo = JSON.parse(JSON.stringify(data))
pojo.categories_ids.forEach(function(el, index) {
pojo.categories_ids[index] = el.category_id
})
Try something like this
associate your through model directly to item as well so you can include in query
Item.hasMany(ItemCategory, {
foreignKey: 'item_id',
constraints: false
});
Item.findAll({
include: [{
model: ItemCategory,
as: 'categories_ids',
attributes: ['category_id']
}]
});

Resources