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
Related
The problem:
Whenever I fetch a user, I always have to declare/include the association on the query to get its role:
const user = await DB.PORTALDB.models.AccessUser.findOne({
where: { email },
include: [ // EVERY QUERY, I HAVE TO INCLUDE THIS
{
model: DB.PORTALDB.models.AccessUserRoleLup,
as: 'role'
}
]
});
Now there are instance where I forget to include this association so I get a undefined role.
My question is, is there a way where I only set this association once so that I don't have to include this later on my queries?
This the model for my AccessUser table
const AccessUser = <AccessUserStatic>sequelize.define<AccessUserInstance>(
'AccessUser',
{
user_id: {
autoIncrement: true,
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true
},
email: {
type: DataTypes.STRING(255),
allowNull: false
},
firstname: {
type: DataTypes.STRING(255),
allowNull: false
},
lastname: {
type: DataTypes.STRING(255),
allowNull: false
},
password: {
type: DataTypes.STRING(255),
allowNull: false
},
disable: {
type: DataTypes.TINYINT,
allowNull: false,
defaultValue: 0
},
role_id: {
type: DataTypes.SMALLINT,
allowNull: false,
defaultValue: 0
},
created_modified: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
}, {
tableName: 'access_user',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "user_id" },
]
},
]
});
AccessUserRoleLup table
const AccessUserRoleLup = <AccessUserRoleLupStatic>sequelize.define<AccessUserRoleLupInstance>(
'AccessUserRoleLup',
{
role_id: {
autoIncrement: true,
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true
},
role_name: {
type: DataTypes.STRING(50),
allowNull: false
},
role_code: {
type: DataTypes.CHAR(50),
allowNull: false,
defaultValue: ""
}
}, {
tableName: 'access_user_role_lup',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "role_id" },
]
},
]
});
Association:
db.models.AccessUser.hasOne(db.models.AccessUserRoleLup, {
foreignKey: 'role_id',
as: 'role'
});
Use defaultScope for AccessUser. defaultScope is defined in a model definition and it is always applied (unless you removed inline).
const AccessUser = <AccessUserStatic>sequelize.define<AccessUserInstance>(
'AccessUser',
{
user_id: {
autoIncrement: true,
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true
},
...
}, {
tableName: 'access_user',
timestamps: false,
defaultScope: { // Add this
include: [{
model: AccessUserRoleLup,
as: 'role'
}]
},
...
});
With this model definition, all queries will include AccessUserRoleLup.
If you would like to remove for a certain query, use .unscoped().
// These will automatically add eager loading for role
DB.PORTALDB.models.AccessUser.findAll()
DB.PORTALDB.models.AccessUser.findOne()
// These won't fetch role
DB.PORTALDB.models.AccessUser.unscoped().findAll()
DB.PORTALDB.models.AccessUser.unscoped().findOne()
More detail about scope: https://sequelize.org/master/manual/scopes.html
My initial workaround was to create a utility function for querying the user like so:
export const getAccessUser = (where: WhereOptions, include?: IncludeOptions) => {
return new Promise(async (resolve, reject) => {
try {
const user = await DB.PORTALDB.models.AccessUser.findOne({
where: where,
include: [
{
model: DB.PORTALDB.models.AccessUserRoleLup,
as: 'role'
},
...[include]
]
});
resolve(user);
} catch (err) {
reject(err);
}
});
}
I wonder if my question above can be done in much simpler way.
I have two models Doctor and Degree with many to many relationship.
Doctor model:
const Doctor = _sequelize.define('doctor', {
fullname: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
gender: {
type: Sequelize.STRING
},
address: {
type: Sequelize.STRING
},
phone: {
type: Sequelize.STRING
},
avatarurl: {
type: Sequelize.STRING
},
active: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
license: {
type: Sequelize.STRING
}
},{
timestamps: false,
tableName: 'doctor'
})
Doctor.associate = (models) => {
Doctor.belongsToMany(model.degree, { through: 'doctor_degree', foreignKey: 'doctor_id',as: 'degree' })
}
Degree model:
const Degree = _sequelize.define('degree', {
name: {
type: Sequelize.STRING
}
},{
timestamps: false,
tableName: 'degree'
})
Degree.associate = (models) => {
Degree.belongsToMany(models.doctor, { through: 'doctor_degree', foreignKey: 'degree_id' })
};
Doctor_Degree model:
const Doctor_Degree = _sequelize.define('doctor_degree', {
doctor_id: {
type: Sequelize.STRING,
allowNull: false,
references: {
model: 'doctor',
key: 'id'
}
},
degree_id: {
type: Sequelize.STRING,
allowNull: false,
references: {
model: 'degree',
key: 'id'
}
}
},{
timestamps: false,
tableName: 'doctor_degree'
});
And I have a service to find all doctor with degree
var result;
try{
await doctorModel.findAll({
attributes: ['id','fullname', 'email','gender','address','phone','avatarurl','active','license'],
include: [{
model: degreeModel,
as: 'degree'
}]
}).then((doctors) => {
result = doctors
})
}
catch(err){
throw err
}
return result
But i got the following error:
[SequelizeEagerLoadingError]: degree is not associated to doctor!
Can anyone suggest what I am doing wrong?
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
I have the following problem:
I defined my tables (product and collection) like this:
module.exports = (sequelize, type) => {
return sequelize.define('product', {
id: {
type: type.UUID,
primaryKey: true,
defaultValue: type.UUIDV4,
},
title: {
type: type.STRING,
allowNull: false,
},
description: {
type: type.TEXT,
allowNull: false,
},
width: {
type: type.FLOAT,
allowNull: false,
},
height: {
type: type.FLOAT,
allowNull: false,
},
weight: {
type: type.FLOAT,
allowNull: false,
},
length: {
type: type.FLOAT,
allowNull: false,
},
vendor: {
type: type.STRING,
allowNull: false,
},
status: {
type: type.ENUM,
values: ['inactive', 'active'],
defaultValue: 'active',
allowNull: false,
},
})
}
module.exports = (sequelize, type) => {
return sequelize.define('collection', {
id: {
type: type.UUID,
primaryKey: true,
defaultValue: type.UUIDV4,
},
name: {
type: type.STRING,
allowNull: false,
},
image: {
type: type.STRING,
allowNull: true,
},
createdAt: {
type: type.DATE,
allowNull: false,
},
updatedAt: {
type: type.DATE,
allowNull: false,
},
status: {
type: type.ENUM,
values: ['inactive', 'active'],
defaultValue: 'active',
allowNull: false,
},
})
}
Then, I need associated the tables (product and collection) with belongsToMany association and i did it like this:
const ProductModel = require('../api/product/model')
const CategoryModel = require('../api/category/model')
const Product = ProductModel(sequelize, Sequelize)
const Collection = CollectionModel(sequelize, Sequelize)
Product.belongsToMany(Collection, {
through: ProductCollection,
foreignKey: 'productId',
otherKey: 'collectionId',
unique: false,
})
Collection.belongsToMany(Product, {
through: ProductCollection,
foreignKey: 'collectionId',
otherKey: 'productId',
unique: false,
})
Now, i want to get all the products of a collection given by the id sent from the body of the request, i have little time working with sequelize and i donĀ“t know how to do this kind of query.
Can you help me with that?
you can use something like this
let products = Collection.findAll({
where: {
id: collection.id,
},
attributes: ['id', 'name'],
include: [{
model: Product,
through: {
model: ProductCollection,
},
as: 'products',
attributes: ['id', 'title', 'description']
}],
});
return products
hope it helps
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']
}]
});