Sequelize – where with count - javascript

I'm having three models that extend Sequelize.Model and have migrations generated. The associations look like this:
Foo
Foo.belongsToMany(Bar, {
as: 'bars',
through: 'FooBar',
foreignKey: 'foo_id',
});
Bar
Bar.belongsToMany(Foo, {
as: 'bars',
through: 'FooBar',
foreignKey: 'bar_id',
});
FooBar
FooBar.belongsTo(Foo, {
as: 'foo',
foreignKey: 'foo_id',
});
FooBar.belongsTo(Bar, {
as: 'bar',
foreignKey: 'bar_id',
});
I'm trying to query Foo like this:
const foos = await Foo.findAll({
include: [{
model: Bar,
as: 'bars',
}],
});
The code works as expected and I get the array of bars for each of my foo.
How can I now query foos that only have bars count that is more than e.g. 2?

There is no direct way to do this in sequelize.
Two ways you can do it:
First way: (2 queries)
Find all the foo ids that have 2 bars in 1 query (probably raw query)
Add those foo ids as a filter in where of your findAll
Second way: (1 query)
Create a sequelize literal of a subquery that returns foo ids that have more than 2 bars and add that literal to where: { id: <subquery literal> }
Showing Second way:
subquery literal e.g.:
sequelize.literal(`(
SELECT
id
FROM foo
LEFT JOIN bar ON bar.foo_id = foo.id
WHERE COUNT(bar.id) > 2
GROUP BY foo.id
)`)
Final findAll:
const foos = await Foo.findAll({
where: {
id: sequelize.literal(`...`),
},
include: [{
model: Bar,
as: 'bars',
}],
});

Related

Use array for filtering Prisma nextjs

having an array with unique names, how can i update the elemets of those names using prisma(in nextjs).
I mean something Like:
const arr=['Joe', 'Mark'];
const update=await prisma.user.updateMany({
where: {
name: //What should i put to update both Joe and Mark?
},
data: {
notifications: {push: "Hello"}
}
});
I'm using PSQL and i don't know if it matters.
Try this:
const arr=['Joe', 'Mark'];
const update=await prisma.user.updateMany({
where: {
name: {in: arr}
},
data: {
notifications: {push: "Hello"}
}
});
"in" will check if name matches anything in that array.
Here is the list of other filters: https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#filter-conditions-and-operators

Joining to the same table multiple times with Sequelize

I am trying to write a query with that would roughly do:
SELECT Challenge.id, Challenge.name, count(AcceptedChallenge.userId) AS attempts, count(a.met) AS met, count(b.active) AS active, count(c.id) AS WHOLE
FROM Challange
LEFT JOIN AcceptedChallenge ON Challenge.id = AcceptedChallenge.challengeId,
LEFT JOIN AcceptedChallenge AS a ON Challenge.id = a.challengeId
LEFT JOIN AcceptedChallenge AS b ON Challenge.id = b.challengeId
LEFT JOIN AcceptedChallenge AS c ON Challenge.id = c.challengeId
WHERE a.met = true
AND b.userId = id and b.active = true
AND c.userId = id;
Tried multiple versions, including the below:
const Sequelize = require('sequelize');
const ChallengeController = async ({ user: { id } }) => {
const challenges = await Challenge
.findAll({
attributes: {
include: [[Sequelize.fn('count', Sequelize.col('AcceptedChallenges.userId')), 'attempts'],
[Sequelize.fn('count', Sequelize.col('a.met')), 'met']]
},
include: [{
model: AcceptedChallenge, attributes: [],
required: false,
}, {
model: AcceptedChallenge, attributes: [],
as: 'a',
where: { userId: id, met: true },
required: false,
}],
group: ['Challenge.id']
})
.catch((e) => {
console.log("error:", e);
throw new HTTP404Error('Challenges not found');
});
return challenges;
};
It is not recognizing my associations. Please advise. The version here results in: SequelizeEagerLoadingError: AcceptedChallenge is associated to Challenge using an alias. You've included an alias (a), but it does not match the alias(es) defined in your association (AcceptedChallenges).
When including the AcceptedChallenge model just once, it calculates attempts just fine. I am perplexed as to how I could do the include/JOIN multiple times to get the result, which I need from a single SQL request.
This works for me when the association is repeated, once for each alias needed in your query (example below, but obviously your association may be different).
ChallengeController.hasMany(AcceptedChallenge, {as: 'a', foreignKey: 'challengeId'});
ChallengeController.hasMany(AcceptedChallenge, {as: 'b', foreignKey: 'challengeId'});
ChallengeController.hasMany(AcceptedChallenge, {as: 'c', foreignKey: 'challengeId'});
Are you doing something similar?

How I can change name in including field in sequelize?

How I can change name in request object, if I use sequelize.
I have two models, and Person model include Name model.
Table in db is Names, and my field name is Names.
How I can . change it?
Person.findByPk(
id,
include: [{
model: Name
}]
});
Name.Model.js
sequelize.define('Name', {
id: {
type: type.INTEGER,
primaryKey: true
},
name: {
type: type.STRING
}
});
and I get object Person, where one filed is also object
Person = {
...
Names: {
id: 1,
name: Piter
}
}
I want
Person = {
...
name: {
id: 1,
name: Piter
}
}
You should be able to use the as property in your association wherever you defined it. For example your Person Model could look like this.
const Person = sequelize.define('Person',
{
.
. //whatever other properties your person has.
.
},{});
Person.associate = function(models){
Person.hasOne(models.Name, {
as: 'name'
});
};
Edit: More information regarding this subject can be found here: http://docs.sequelizejs.com/manual/associations.html#naming-strategy

Sequelize.js: get association of many-to-many junction model

I have some application. There is an Character, which can belong to many group. A group can have many characters and also many ranks. Each Character have specified rank. Ranks differs across the groups.
Character model:
module.exports = (sequelize, DataTypes) => {
let Character = sequelize.define('Character', {
/* attributes */
}, {});
Character.associate = (models) => {
Character.hasMany(models.outfit, { foreignKey: 'owner' });
Character.belongsToMany(models.group, { through: models.groupmember });
};
return Character;
};
Group model:
module.exports = (sequelize, DataTypes) => {
let Group = sequelize.define('Group', {
/* attributes */
}, {});
Group.associate = function (models) {
Group.belongsToMany(models.character, { through: models.groupmember, as: 'members' });
Group.hasMany(models.grouprank);
};
return Group;
};
GroupMember (junction table) model:
module.exports = (sequelize, DataTypes) => {
let GroupMember = sequelize.define('GroupMember', {
/* groupId, characterId are generated by sequelize */
rankId: DataTypes.INTEGER
}, {});
GroupMember.associate = function (models) {
GroupMember.belongsTo(models.grouprank, { foreignKey: 'rankId', targetKey: 'id' });
};
return GroupMember;
};
Group rank model: (doesn't matter much in my question)
module.exports = (sequelize, DataTypes) => {
let GroupRank = sequelize.define('GroupRank', {
// atributes
}, {});
GroupRank.associate = function (models) {
GroupRank.belongsTo(models.group);
GroupRank.hasMany(models.groupmember, { foreignKey: 'rankId' });
};
return GroupRank;
};
I have added rankId column to junction table and I have problem to retrieve Character with all its groups included and also the rank which it have.
My current code, which returns the Character, the groups to which he belongst to, and also ranks, but the ALL ranks which belongs to the group. And I want just the rank with ID which is specified by rankId.
database.character.findById(characterId, {
include: [{
model: database.group,
through: database.groupmember,
include: {
model: database.grouprank,
/* where: { id: '$GroupMember.rankId$' } */
}
}]
}).then(result => {
});
Yep. I know it looks weird little, it lacks CamelCase but however my problem is about something else. I tried to query with where attribute, but Sequelize is parsing the string, so I can't put there column name. I hope the question is understable enough. Thank you for your time!
I think below code finally worked for me. http://docs.sequelizejs.com/class/lib/sequelize.js~Sequelize.html#static-method-literal
database.character.findById(characterId, {
include: [{
model: database.group,
through: database.groupmember,
include: {
model: database.grouprank,
where: database.Sequelize.literal('`Groups->GroupRanks`.`id` = `Groups->GroupMember`.`rankId`')
}
}]
})
edit: Finally I decided to rewrite models as stated here -> FindAll with includes involving a complicated many-to-(many-to-many) relationship (sequelizejs)

Sequelize How to customize your our ON condition in a join?

I have these three tables:
A: B: C:
AId BId CId
b_id b_id
I want to do this:
SELECT A.*, B.*, C.*
FROM A
JOIN B
ON A.b_id = B.BId
JOIN C
ON B.BId = C.b_id #1
I have tried to do this with sequelize like:
AModel.findOne({
attributes: [],
where: { Aid: idByParameter },
include: [{
model: BModel,
through: {
// as: 'B', Not necessary
attributes: []
},
include: [{
model: CModel,
// on: 'B.BId = C.b_id', #2
through: {
attributes: []
}
}]
}]
}).then((res) => {
return res;
})
The first join, AModel with BModel works correctly because I have set the relation between A and B.
The problem is when I try to make a join between BModel and CModel because the relation is defined in the other side.
Are there any possibility to implement this joint with sequelize??
Sequelize is trying to execute this query:
SELECT A.*, B.*, C.*
FROM A
JOIN B
ON A.b_id = B.BId
JOIN C
ON B.c_id = C.CId #3
But I want to change #3 to #1 with something similar to #2.
Is this possible??

Categories