Sequelize: extra column in query - node.js

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'})

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

using "or" operator when using .populate on a query?

I have my invoices model which has a "sales" field ,which is array of sales. In my sales model I have item field which either can come from Vehicles schema or AfterSale schema.
export const Sales = new Schema<ISale>(
{
customer: { type: Schema.Types.ObjectId, ref: "Customer", required: true },
category: { type: String, enum: enu, required: true },
item: {
type: Schema.Types.ObjectId,
required: true,
ref: (doc) => doc.category,
},
total: { type: Number, required: true },
discount: { type: Number },
purchaseDate: { type: Date, required: true },
branch: { type: Schema.Types.ObjectId, ref: "Branch", required: true },
},
{ timestamps: true },
);
I want to populate the sales.item on my invoice schema, but the problem is , since item can reference to multiple schemas, I can only populate one of them.
async findAll(req: any, query: SharedPreferences): Promise<InvoiceClass[]> {
const invoices = this.invoiceModel
.find(req.searchObj)
.sort({ [query.sort]: query.orderBy === "desc" ? -1 : 1 })
.populate([
"customer",
"sales",
{
path: "sales",
populate: [
{
path: "customer",
model: "Customer",
},
{
path: "branch",
model: "Branch",
},
{
path: "item",
model: "Vehicle",
},
{
path: "item",
model: "AfterSale", //in this case only AfterSale will be populated since
// its last and has same path
}
],
},
]);
my perfect solution would have been using the or operator , but that doesnt work
async findAll(req: any, query: SharedPreferences): Promise<InvoiceClass[]> {
const invoices = this.invoiceModel
.find(req.searchObj)
.sort({ [query.sort]: query.orderBy === "desc" ? -1 : 1 })
.populate([
"customer",
"sales",
{
path: "sales",
populate: [
{
path: "customer",
model: "Customer",
},
{
path: "branch",
model: "Branch",
},
{
path: "item",
model: { $or: ["Vehicle", "AfterSale"] },
},
],
},
]);
any reference/help will be appreciated, thanks.

subquery Sequelize with nested query (include)

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

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']
}]
});

SQLite SequelizeJs adding an extra filed in query

I am using sequelize and SQLite3. When I use the model in my code then it is generating wrong query. Can any one help me to fix this issue
This is my model defiantion
module.exports = function(sequelize, DataTypes) {
let product = sequelize.define('product', {
id: {
type: DataTypes.INTEGER(10).UNSIGNED,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(200),
},
code: {
type: DataTypes.STRING(100),
},
desc: {
type: "BLOB",
},
productCategoryId: {
type: DataTypes.INTEGER(10).UNSIGNED,
references: {
model: 'product_category',
key: 'id'
}
},
costPrice: {
type: DataTypes.FLOAT,
},
sellPrice: {
type: DataTypes.FLOAT,
},
markup: {
type: DataTypes.FLOAT,
},
markupType: {
type: DataTypes.ENUM('AMOUNT','PERCENTAGE'),
},
imgAttachment: {
type: DataTypes.INTEGER(1),
},
minOrderQuantity: {
type: DataTypes.INTEGER(10),
},
minStockQuantity: {
type: DataTypes.INTEGER(10),
},
isComposite: {
type: DataTypes.INTEGER(1),
},
isAllowedOutOfStockSale: {
type: DataTypes.INTEGER(1),
defaultValue: '0'
},
isActive: {
type: DataTypes.INTEGER(1),
defaultValue: '0'
},
isDeceptive: {
type: DataTypes.INTEGER(1),
defaultValue: '0'
},
createdAt: {
type: DataTypes.DATE,
},
createdBy: {
type: DataTypes.INTEGER(10).UNSIGNED,
references: {
model: 'user',
key: 'id'
}
},
deletedAt: {
type: DataTypes.DATE,
},
deletedBy: {
type: DataTypes.INTEGER(10).UNSIGNED,
references: {
model: 'user',
key: 'id'
}
},
updatedAt: {
type: DataTypes.DATE,
},
updatedBy: {
type: DataTypes.INTEGER(10).UNSIGNED,
references: {
model: 'user',
key: 'id'
}
}
}, {
tableName: 'product',
timestamps: false,
defaultScope: {
where: {
isActive: true,
deletedAt: null,
}
}
});
// Association
product.associate = function(models) {
models.product.belongsTo(models.user);
models.product.belongsTo(models.user);
models.product.belongsTo(models.user);
models.product.belongsTo(models.product_category);
models.product.hasMany(models.product_composition);
};
return product;
}
This is my model implementation
models.findAll({})
.then(data => {
console.log(data)
.catch(err => {
console.log(err)
});
I am getting SequelizeDatabaseError. After investigation I trace out the generated query
SELECT `id`, `name`, `code`, `desc`, `productCategoryId`, `costPrice`, `sellPrice`, `markup`, `markupType`, `imgAttachment`, `minOrderQuantity`, `minStockQuantity`, `isComposite`, `isAllowedOutOfStockSale`, `isActive`, `isDeceptive`, `createdAt`, `createdBy`, `deletedAt`, `deletedBy`, `updatedAt`, `updatedBy`, `userId` FROM `product` AS `product` WHERE `product`.`id` = 1 AND `product`.`isActive` = 1 AND `product`.`deletedAt` IS NULL;
Why it is adding userId in query. This query works fine when I remove userId field from this generated query
That is because of this line :
This line will add a userId attribute to product to hold the primary key value for Product
models.product.belongsTo(models.user);
But This will not add the field , reason is , naming convention is followed for foreign key name productCategoryId but not in above case ,
models.product.belongsTo(models.product_category);
for that you should define that explicitly and you should also add alias name for association coz you are using one table for 3 relations , like
models.product.belongsTo(models.user , { as : 'delete_by' ,foreignKey: 'deletedBy'} );
models.product.belongsTo(models.user , { as : 'created_by' ,foreignKey: 'createdBy'} );
models.product.belongsTo(models.user , { as : 'updated_by' , foreignKey: 'updatedBy'} );
For more detail : DO READ

Resources