How to set complex join conditions in sequelize? - node.js

I'm trying to encode a query to a database using sequelize. This is exactly the sentence I want to transcript:
SELECT matches.uid_prop, matches.uid_player, matches.porcentaje, users.uid, profile_users.name, profile_users.profile, profile_users.birthday, profile_users.location
FROM (matches JOIN users ON (((matches.uid_prop = users.uid) AND (matches.uid_player = 11)) OR ((matches.uid_player = users.uid) AND (matches.uid_prop = 11)))) JOIN profile_users USING(uid)
WHERE (matches.uid_player = 11) OR (matches.uid_prop = 11);
I don't know if I should change the associations in order to build the join condition as I want
Matches.belongsTo(User, {as: 'match_prop', foreignKey: 'uid_prop'});
User.hasMany(Matches, {as: 'user_prop', foreignKey: 'uid_prop'});
Matches.belongsTo(User, {as: 'match_player', foreignKey: 'uid_player'});
User.hasMany(Matches, {as: 'user_player', foreignKey: 'uid_player'});
or add a where and specify the relation at the include between matches and user. If that's the case, how could I make reference to a field of matches from a where statement in the include made with user so I could compare, e.g., User.uid and Matches.uid_prop.
I've tried this notation but it didn't work: $Matches.uid_prop$
Matches.findAll({
attributes: ['porcentaje'],
where: {
$or: [
{uid_prop: uid},
{uid_player: uid}
]
},
include: [{
model: User,
attributes: ['uid'],
include: [{
model: Profile_user,
attributes: ['name', 'profile', 'birthday', 'location']
}]
}]
})

I think all you need is required : true ( INNER JOIN ) like :
Matches.findAll({
attributes: ['porcentaje'],
where: {
$or: [
{uid_prop: uid},
{uid_player: uid}
]
},
include: [{
model: User,
attributes: ['uid'],
required : true , //<-------- HERE -------
include: [{
model: Profile_user,
attributes: ['name', 'profile', 'birthday', 'location']
}]
}]
})
Your query is making the LEFT JOIN , to make it INNER JOIN force fully , you can use required : true.

Related

Sequelize get all records if value exists on N:N table

I want to achieve this query with sequelize
SELECT U.username, G.groupName
FROM [user] U INNER JOIN [userGroup] UG ON (U.id = UG.userId) INNER JOIN [group] G ON (G.id = UG.groupId)
WHERE U.profileId = #profile AND EXISTS (SELECT groupId FROM [userGroup] WHERE groupId = #group AND userId = U.id)
the expected result are all the users that match with the profile and all theirs groups in which some group should be present
this is my relevant code of my associations layer
db.users.belongsToMany(db.group, {
as: 'Groups',
through: UserGroup,
foreignKey: 'userId',
otherKey: 'groupId',
});
db.group.belongsToMany(db.users, {
as: 'Users',
through: UserGroup,
foreignKey: 'groupId',
otherKey: 'userId',
});
and the one from the controller
User.findAll({
where: {profileId = Number(profileType)},
include: [{
model: Group,
as: 'Groups',
attributes: ['groupName'],
where: [
{
[Op.and]: sequelize.literal(
`EXISTS (SELECT [UG].groupId FROM [userGroup] AS [UG] WHERE [UG].groupId = ${group})`
),
};
]
required: true,
}]
My principal problem is how to filter by userId in the sequelize subquery like i did in the SQL query example from the begining
You need to move this subquery condition to the User's conditions because you have AND userId = U.id. Something like this:
User.findAll({
where: {
[Op.and]: [{
profileId = Number(profileType)
},
sequelize.literal(
`EXISTS (SELECT [UG].groupId FROM [userGroup] AS [UG] WHERE [UG].groupId = ${group} AND [UG].userId=[User].id)`
)]
},
include: [{
model: Group,
as: 'Groups',
attributes: ['groupName'],
required: true,
}]
You should look at generated SQL query and correct the [User] table alias if needed in AND [UG].userId=[User].id

How do I eager load multiple foreign keys including an or query from the same table, in Sequelize?

I'm searching for the captain.entry_date but I'm not able to create the query in a sequelize model.
My problem is that for any ship exists a captain but the ship_captain.captain_id sometimes is null.
For this cases the captain can be found about the route_id.
4 Tables :
ship, attributes:[id,name],
captain, attributes: [id, name, route_id, route_date]
ship_captain, attributes: [id, ship_id, route_id, captain_id]
route, attributes: [id, name]
select ship.name, c.name, c.entry_date
from ship left join ship_captain sc on ship.id = sc.ship_id
left join captain c on c.id = sc.captain_id or c.route_id = sc.route_id
What I've try so far is this but I can't give an OR operator into the last join
Ship.hasMany(ShipCaptain, {foreignKey: "ship_id"});
ShipCaptain.belongsTo(Ship, {foreignKey: "ship_id"});
Captain.hasMany(ShipCaptain, {as: "ship_captain_by_id", foreignKey: "captain_id"});
ShipCaptain.belongsTo(Captain, {as: "ship_captain_by_route", foreignKey: "captain_id"});
Captain.hasMany(ShipCaptain, {as: "ship_captain_by_route", foreignKey: "route_id"});
ShipCaptain.belongsTo(Captain, {as: "ship_captain_by_route", foreignKey: "route_id"});
const options = {
attributes: ["name"],
include: [
{
model: Captain,
as: 'ship_captain_per_id',
required: false,
attributes: ["name","route_date"],
},
{
model: Captain,
as: 'ship_captain_per_route',
required: false,
attributes: ["name","route_date"],
}
],
}
const elements = await Ship.findAll(options);
This is only an example code, may be that you want to rearrange the db attributes
but I tried to give my best to clarify the problem. I can't change the customers database.
If you really want to use only one association to get a captain by captain_id or route_id and not to use two associations and map them yourself then you need to define only one association hasOne (instead of hasMany) and always use the on option to join ShipCaptain with Captain by OR:
Ship.hasMany(ShipCaptain, {foreignKey: "ship_id"});
ShipCaptain.belongsTo(Ship, {foreignKey: "ship_id"});
ShipCaptain.belongsTo(Captain, {as: "captain_by_captain", foreignKey: "captain_id"});
...
const options = {
attributes: ["name"],
include: [
{
model: ShipCaptain,
required: false,
include: [{
model: Captain,
required: false,
as: 'captain_by_captain',
attributes: ["name","route_date"],
on: {
[Op.or]: [{
id: Sequelize.col('ship_captain.captain_id'
}, {
id: Sequelize.col('ship_captain.route_id'
}]
}
}
]
},
],
}
const elements = await Ship.findAll(options);

How do you join 3 tables in Sequelize that aren't all linked

I have 4 tables Properties, Units, Leases, LeaseRates.
Units
Leases
LeaseRates
propertyId
unitId
leaseId
they are all connected with the id of the last one.
I have these relationships setup like this
Properties.hasMany(Units);
Units.belongsTo(Properties);
Units.hasMany(Leases);
Leases.belongsTo(Units);
Leases.hasMany(LeaseRates);
LeaseRates.belongsTo(Leases);
Is there an easy way in Sequelize for me to query LeaseRates via propertyId?
<pre>
await properties.findOne({
where: { id },
include: [{
model: model.Unit, as: 'unit',
attributes: ['id'],
include: [{
model: model.Leases, as: 'leases',
attribute: ['id'],
include: [{ model: model.LeaseRates, as: 'leaserates' }]
}]
}]
})
</pre>

Multiple Sequelize Associations

I have a few associations between models like so:
Patient.hasMany(Transaction, {
as: 'transactions',
foreignKey: 'patient_id',
sourceKey: 'id'
});
Transaction.belongsTo(Patient, {
as: 'patient',
foreignKey: 'patient_id',
targetKey: 'id'
});
Transaction.hasMany(Transaction, {
as: 'transactions',
foreignKey: 'item_to_pay',
sourceKey: 'id'
});
I'm writing a query to get a list of transactions that belong to a patient and include the models associated with them:
Transaction.findAll({
where: {
patient_id: 1
},
include: [{
model: Patient,
as: 'patient',
attributes: ['first_name']
}, {
model: Transaction,
as: 'transactions',
include: [{
model: Patient,
as: 'patient',
attributes: ['first_name']
}]
}],
});
However when the result returns, it does not include the second nested patient model as I expect it to.
I've also tried writing a different query to see if I can get the desired result:
Transaction.findAll({
where: {
patient_id: 1
},
include: [{
model: Transaction,
as: 'transactions',
include: [{
model: Patient,
as: 'patient',
attributes: ['first_name']
}]
}],
});
But this query errors out and returns 'Unknown column 'patient.id' in field list'.
Does anyone see something wrong with my query or associations? Is there something I'm not understanding about the associations here?
I ended up changing my original query since I couldn't figure it out at the time. I stumbled into the same issue recently and realized the issue came from how I named my associations. I named the transaction.hasMany association as transactions which ended up confusing MySQL with the query that was generated because one of the tables is named transactions.
TLDR, don't name your associations the same name as any of your table names.

Multiple tables join issue in sequelize in node js

I have 6 tables
Employees,Salary,Deduction,DeductionTypes,Allowance,AllowancesTypes
Here is the table relations with a diagram
I can't understand how to create a join query
Employee.belongsTo(Salary, { foreignKey: 'employee_id' });
Salary.belongsToMany(Deduction, {foreignKey: 'salary_id' });
Deduction.belongsToMany(DeductionType, {foreignKey: 'deduction_type_id' });
Salary.belongsToMany(Allowance, {foreignKey: 'salary_id' });
Allowance.belongsToMany(AllowanceType, {foreignKey: 'allowance_type_id' });
Here is my find query
const payRunData = Employee.findAll({
include: [{
model: Salary, attributes: ['salary_id', 'basic_salary'],
include: [
{
model: Deduction, attributes: ['amount'],
include: [{
model: DeductionType, attributes: ['epf_enable', 'type', 'pre_tax', 'pay_run_editable'],
}]
},
{
model: Allowance, attributes: ['amount'],
include: [{
model: AllowanceType, attributes: ['epf_enable', 'type', 'payee_enable', 'pay_run_editable'],
}]
}
]
}
],
attributes: ['employee_id', 'given_names'],
});
Here is the sql join query what I'm expecting
select s.salary_id , e.employee_id, e.given_names, d.amount as 'deduction_amount',
dty.`type` as 'deduction_type', dty.epf_enable as 'deductType_epf_enable', dty.pre_tax, dty.pay_run_editable as 'deductType_pay_run_editable',
a.amount as 'allowance_amount', aty.`type`as 'allowanceType_type', aty.epf_enable as 'allowanceType_epf_enable',
aty.payee_enable as 'allowanceType_payee', aty.pay_run_editable as 'allowanceType_pay_run_editable'
from employees as e inner join salary as s on e.employee_id = s.salary_id
inner join deductions as d on s.salary_id = d.salary_id
inner join deduction_types as dty on d.deduction_type_id = dty.deduction_type_id
inner join allowances as a on s.salary_id = a.salary_id
inner join allowance_types as aty on a.allowance_type_id = aty.allowance_type_id
When I execute this,it's returning this error
salary.belongsToMany(deduction) requires through option, pass either a
string or a model"
please help me to solve this

Resources