'Nested select is missing required properties when LEFT JOIN
I have 4 database tables:
- Questionnaires
- Sections
- Questions
- Answers
I'm trying to get the questionnaire with nested values where Sections, Questions, and Answers are joined to each other. The query itself works but the problem is that the result only contains questions that has at least one answer.
The goal is to have a list of questions with all the answers - whether or not the answer is present.
| Questionnaire | |
|---|---|
| id | PrimaryKey |
| name | String |
| Sections | |
|---|---|
| id | PrimaryKey |
| name | String |
| questionnaire_id | ForeignKey |
| Questions | |
|---|---|
| id | PrimaryKey |
| text | String |
| section_id | ForeignKey |
| Answers | |
|---|---|
| id | PrimaryKey |
| text | String |
| user_id | Number |
| question_id | ForeignKey |
This is the query I'm trying to run:
@Injectable()
class QuestionnaireService {
findOne(id: number, user_id: string) {
return this.questionnaire
.createQueryBuilder('questionnaire')
.leftJoinAndSelect('questionnaire.sections', 'sections')
.leftJoinAndSelect('sections.questions', 'questions')
.leftJoinAndSelect('questions.answers', 'answers')
.where('questionnaire.id = :id', { id })
.andWhere('answers.user_id = :user_id', { user_id })
.getOne();
}
}
At the moment I have 14 questions in the database. And only 2 answers so the result is this:
Questionnaire {
id: 1,
name: 'Default DRI Questionnaire',
sections: [
Section {
id: 1,
name: 'Your security threat concerns',
questions: [ [Question] ]
},
Section {
id: 2,
name: 'About your organization',
questions: [ [Question] ]
}
]
}
So, I'm expecting the 14 questions to be present despite the fact that most of them do not have answers at all.
In Sequelize I've done this with include property but in TypeORM couldn't find the way to do it. 😕
This is the query TypeORM is running:
SELECT "questionnaire"."id" AS "questionnaire_id",
"questionnaire"."name" AS "questionnaire_name",
"sections"."id" AS "sections_id",
"sections"."name" AS "sections_name",
"sections"."questionnaire_id" AS "sections_questionnaire_id",
"questions"."id" AS "questions_id",
"questions"."text" AS "questions_text",
"questions"."section_id" AS "questions_section_id",
"answers"."id" AS "answers_id",
"answers"."user_id" AS "answers_user_id",
"answers"."question_id" AS "answers_question_id",
"answers"."questionnaire_id" AS "answers_questionnaire_id",
FROM "questionnaires" "questionnaire"
LEFT JOIN "sections" "sections" ON "sections"."questionnaire_id" = "questionnaire"."id"
LEFT JOIN "questions" "questions" ON "questions"."section_id" = "sections"."id"
LEFT JOIN "answers" "answers" ON "answers"."question_id" = "questions"."id"
WHERE "questionnaire"."id" = 1
AND "answers"."user_id" = 1
Solution
Thanks to @Andrew
I had to use condition check alongside leftJoinAndSelect.
@Injectable()
class QuestionnaireService {
findOne(id: number, user_id: string) {
return this.questionnaire
.createQueryBuilder('questionnaire')
.leftJoinAndSelect('questionnaire.sections', 'sections')
.leftJoinAndSelect('sections.questions', 'questions')
.leftJoinAndSelect('questions.answers', 'answers', 'answers.user_id = :user_id', { user_id })
.where('questionnaire.id = :id', { id })
.getOne();
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
