Hello guys please bear with me here. I’m using PostgreSQL, Sequelize, Express, and NodeJS to create a backend. I’m wondering if these lines of raw query code can be implemented using Sequelize Model findAll function.
First of all, what I am trying to do here is to calculate the total score of these students. Here are some tables and their relations.
Student Level
| student_id | name | level_id | | level_id | level_name |
|:----------:|:----------:|:--------:| |:--------:|:----------:|
| 1 | John | 1 | > | 1 | Rookie |
| 2 | Jane | 2 | | 2 | Expert |
v
StudentQuiz
| quiz_id | student_id | score |
|:----------:|:----------:|:--------:|
| 1 | 1 | 40 |
| 1 | 1 | 100 |
| 2 | 1 | 80 |
| 1 | 2 | 100 |
| 2 | 2 | 100 |
If I run line of codes below.
SELECT table2.student_id,
s.canvasser_name,
l.level_name,
table2.total_score
FROM (SELECT table1.student_id,
sum(table1.max_score) total_score
FROM (SELECT sq.student_id,
max(sq.score) max_score
FROM public.StudentQuiz sq
GROUP BY sq.quiz_id, sq.student_id) table1
GROUP BY table1.student_id) table2
INNER JOIN public.Student s
ON s.student_id = table2.student_id
INNER JOIN public.Level l
ON l.level_id = s.level_id
ORDER BY table2.total_score DESC
LIMIT 10;
I will get something like this.
| student_id | name | level | total_score |
|:----------:|:----------:|:--------:|:--------------:|
| 1 | John | Rookie | 180 |
| 2 | Jane | Expert | 200 |
Please note that I’m selecting the highest score if more than one quiz with the same id found.
Anyway, I want to implement it using sequelize built in function. What I’ve been trying to do is something like this.
const result = await StudentQuiz.findAll({
attributes: ['studentId', [sequelize.fn('sum', sequelize.fn('max', sequelize.col('score'))), 'totalPrice'], 'quizId'],
group: 'studentId',
include: [
{
model: Student,
include: [{
model: Level
}],
},
],
offset: 0,
limit: 10
});
The code above throws an error message which is “aggregate function calls cannot be nested”.
Any kind of help will be appreciated. Thank you.
P.S. I know i can use sequelize.query() function to use the first code block shown, but that’s not the point.
Advertisement
Answer
I find the solution without using any raw query, though I need to get two tables, that is StudentQuiz and Student that coupled by Level. Here is my answer.
// Finding maximum score and group it based on studentId and quizId
const maxScoreList = await StudentQuiz.findAll({
attributes: ['studentId', 'quizId', [sequelize.fn('max', sequelize.col('score')), 'maxScore']],
group: ['studentId', 'quizId'],
order: ['studentId', 'quizId'],
raw: true
});
// Calculating total score for the same student for each quiz recorded
const scoreArray = [maxScoreList.shift()];
let index = 0;
const unfilteredStudentId = maxScoreList.map((item) => {
if (scoreArray[index].studentId !== item.studentId) {
scoreArray.push(item);
index += 1;
}
scoreArray[index].maxScore += item.maxScore;
return item.studentId;
});
// Filtering studentId that show up more than one time
const extractedStudentId = [new Set(unfilteredStudentId)];
// Finding student based on studentId inside extractedStudentId array
const student = await Student.findAll({
where: { id: extractedStudentId },
attributes: ['id', 'canvasserId', 'canvasserName', 'canvasserImageUrl'],
include: {
model: Level,
attributes: [['level_name', 'level'], ['icon_url', 'level_image_url']]
},
order: ['id'],
raw: true,
nest: true
});
// Combining total score list to student list
const rankList = student.map((item, idx) => ({ item, totalScore: scoreArray[idx] }));
With this much complexity, I agree that using raw query by far the best approach for this case.