I have cases with nested results in JSON. here's the sample result,
{
"total_result" : 25,
"questions" : [
{
"id" : 1,
"text" : "the question of user 1 here",
"user" : {
"id" : 5,
"name" : "user 5",
},
"answers" : [
{
"id" : 5,
"text" : "first answer to user 1 question",
"user" : {
"id" : 10,
"name" : "user 10",
}
},
{
"id" : 6,
"text" : "second answer to user 1 question",
"user" : {
"id" : 11,
"name" : "user 11",
}
},
{
"id" : 10,
"text" : "third answer to user 1 question",
"user" : {
"id" : 12,
"name" : "user 12",
}
}
]
},
{
"id" : 2,
"text" : "the question by user 2 here",
"user" : {
"id" : 6,
"name" : "user 6",
},
"answers" : [
{
"id" : 5,
"text" : "first answer to user 2 question",
"user" : {
"id" : 30,
"name" : "user 30",
}
},
{
"id" : 6,
"text" : "second answer to user 2 question",
"user" : {
"id" : 20,
"name" : "user 20",
}
},
{
"id" : 10,
"text" : "third answer to user 2 question",
"user" : {
"id" : 1,
"name" : "user 1",
}
}
]
},
]
}
my struct goes here,
type Question struct {
Id int
Text string ...
User User ...
Answer []Answer ...
}
type User struct {
Id int ...
Name string ...
}
type Answer struct {
Id int ...
Text string ...
User User ...
}
here's the query to get questions
and user.Detail
query := "select text, user_id from questions limit 10 offset 10"
rows, err, := db.QueryCtx(ctx, query)
//handel error
var questions []Question
var userIds []string
for rows.Next() {
var q Question
var userId string
//scan to `question` and `userId`
questions = append(questions, q)
userIds = append(questions, userId)
}
here's the query to get the user to the question
query = "select name from users where id = any($1)"
userRows, err := db.QueryCtx(ctx, query, pq.Array(userIds))
//handle error
var users []User
//scan and store to users
here's the query to get answers to the question
query = "select answers.id, answers.text, u.id, u.name from answers join questions as q on q.id=answers.question_id join users as u on u.id=answers.user_id where answers.questions_id=$1"
for i := 0; i < len(questions); i++{
rowAnswer, err := db.QueryCtx(ctx, query, questions[i].Id)
//handle error
var answers []Answer
for rowAnswer.Next(){
var answer Answer
//scan to answer
append = (answers, answer)
}
questions[i].User.Id = users[i].Id
questions[i].User.Name = users[i].Name
questions[i].Answer = answers
}
users
table
id | name |
---|---|
1 | name |
questions
table
id | text | user_id |
---|---|---|
1 | text | 1 |
answers
table
id | text | question_id | user_id |
---|---|---|---|
1 | text | 1 | 1 |
the result is good, nothing wrong with the code and the result. but, I'm thinking about the n+query
case, because I do looping to get the answers. my question is, is it reasonable to do that, or is any good advice for my code?
In get questions, you need var questionIds
and mapQuestionIdx
where in
so it look like this
query := "select id, text, user_id from questions limit 10 offset 10"
rows, err, := db.QueryCtx(ctx, query)
//handel error
var questions []Question
var userIds []string
questionIds := make([]int, 0, 10) // because limit is 10
mapQuestionIdx := make(map[int]int)
idx := 0
for rows.Next() {
var q Question
var userId string
//scan to `question` and `userId`
questions = append(questions, q)
userIds = append(questions, userId)
questionIds = append(questionIds, q.ID)
mapQuestionIdx[q.ID] = idx
idx++
}
On query to get answers to the question
question_id
// add select question_id
query = "select q.id question_id, answers.id, answers.text, u.id, u.name from answers join questions as q on q.id=answers.question_id join users as u on u.id=answers.user_id where answers.questions_id in ($1)"
rowAnswer, err := db.QueryCtx(ctx, query, questionIds) // questionIds from above
//handle error
for rowAnswer.Next(){
var answer Answer
var question_id int
//scan to answer and question_id
i := mapQuestionIdx[question_id]
questions[i].User.Id = users[i].Id
questions[i].User.Name = users[i].Name
if questions[i].Answer == nil {
questions[i].Answer = make([]answer, 0)
}
questions[i].Answer = append(questions[i].Answer, answer)
}