获取嵌套结果 JSON 的最佳实践方法

huangapple go评论136阅读模式
英文:

best practice to get nested result json

问题

我有一些包含嵌套结果的 JSON 案例。这是一个示例结果:

{
  "total_result" : 25,
  "questions" : [

    {
      "id" : 1,
      "text" : "用户1的问题在这里",
      "user" : {
        "id" : 5,
        "name" : "用户5"
      },
      "answers" : [
        {
          "id" : 5,
          "text" : "对用户1问题的第一个回答",
          "user" : {
            "id" : 10,
            "name" : "用户10"
          }
        },
        {
          "id" : 6,
          "text" : "对用户1问题的第二个回答",
          "user" : {
            "id" : 11,
            "name" : "用户11"
          }
        },
        {
          "id" : 10,
          "text" : "对用户1问题的第三个回答",
          "user" : {
            "id" : 12,
            "name" : "用户12"
          }
        }
      ]
    },

    {
      "id" : 2,
      "text" : "用户2的问题在这里",
      "user" : {
        "id" : 6,
        "name" : "用户6"
      },
      "answers" : [
        {
          "id" : 5,
          "text" : "对用户2问题的第一个回答",
          "user" : {
            "id" : 30,
            "name" : "用户30"
          }
        },
        {
          "id" : 6,
          "text" : "对用户2问题的第二个回答",
          "user" : {
            "id" : 20,
            "name" : "用户20"
          }
        },
        {
          "id" : 10,
          "text" : "对用户2问题的第三个回答",
          "user" : {
            "id" : 1,
            "name" : "用户1"
          }
        }
      ]
    }

  ]
}

这是我的结构体定义:

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 ...
}

这是获取 questionsuser.Detail 的查询:

query := "select text, user_id from questions limit 10 offset 10"
rows, err := db.QueryCtx(ctx, query)
// 处理错误

var questions []Question
var userIds []string
for rows.Next() {
  var q Question
  var userId string
  // 扫描到 `question` 和 `userId`
  questions = append(questions, q)
  userIds = append(questions, userId)
}

这是获取问题的用户的查询:

query = "select name from users where id = any($1)"
userRows, err := db.QueryCtx(ctx, query, pq.Array(userIds))
// 处理错误
var users []User
// 扫描并存储到 users

这是获取问题的答案的查询:

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)
  // 处理错误
  var answers []Answer
  for rowAnswer.Next(){
    var answer Answer
    // 扫描到 answer
    append = (answers, answer)
  }
  questions[i].User.Id = users[i].Id
  questions[i].User.Name = users[i].Name
  questions[i].Answer = answers
}

users 表格:

id name
1 名字

questions 表格:

id text user_id
1 文本 1

answers 表格:

id text question_id user_id
1 文本 1 1

代码和结果都是正确的,没有问题。但是,我在考虑 n+query 的情况,因为我需要循环获取答案。我的问题是,这样做是否合理,或者对我的代码有什么好的建议?

英文:

I have cases with nested results in JSON. here's the sample result,

{
  &quot;total_result&quot; : 25,
  &quot;questions&quot; : [

    {
      &quot;id&quot; : 1,
      &quot;text&quot; : &quot;the question of user 1 here&quot;,
      &quot;user&quot; : {
        &quot;id&quot; : 5,
        &quot;name&quot; : &quot;user 5&quot;,
      },
      &quot;answers&quot; : [
        {
          &quot;id&quot; : 5,
          &quot;text&quot; : &quot;first answer to user 1 question&quot;,
          &quot;user&quot; : {
            &quot;id&quot; : 10,
            &quot;name&quot; : &quot;user 10&quot;,
           }
        },
        {
          &quot;id&quot; : 6,
          &quot;text&quot; : &quot;second answer to user 1 question&quot;,
          &quot;user&quot; : {
            &quot;id&quot; : 11,
            &quot;name&quot; : &quot;user 11&quot;,
           }
        },
        {
          &quot;id&quot; : 10,
          &quot;text&quot; : &quot;third answer to user 1 question&quot;,
          &quot;user&quot; : {
            &quot;id&quot; : 12,
            &quot;name&quot; : &quot;user 12&quot;,
           }
        }
      ]
    },

    {
      &quot;id&quot; : 2,
      &quot;text&quot; : &quot;the question by user 2 here&quot;,
      &quot;user&quot; : {
        &quot;id&quot; : 6,
        &quot;name&quot; : &quot;user 6&quot;,
      },
      &quot;answers&quot; : [
        {
          &quot;id&quot; : 5,
          &quot;text&quot; : &quot;first answer to user 2 question&quot;,
          &quot;user&quot; : {
            &quot;id&quot; : 30,
            &quot;name&quot; : &quot;user 30&quot;,
           }
        },
        {
          &quot;id&quot; : 6,
          &quot;text&quot; : &quot;second answer to user 2 question&quot;,
          &quot;user&quot; : {
            &quot;id&quot; : 20,
            &quot;name&quot; : &quot;user 20&quot;,
           }
        },
        {
          &quot;id&quot; : 10,
          &quot;text&quot; : &quot;third answer to user 2 question&quot;,
          &quot;user&quot; : {
            &quot;id&quot; : 1,
            &quot;name&quot; : &quot;user 1&quot;,
           }
        }
      ]
    },

  ]
}

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 := &quot;select text, user_id from questions limit 10 offset 10&quot;
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 = &quot;select name from users where id = any($1)&quot;
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 = &quot;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&quot;
for i := 0; i &lt; 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?

答案1

得分: 1

在获取问题时,你需要使用变量questionIdsmapQuestionIdx

  • 添加选择id
  • questionIds用于获取问题id,这样你就可以查询where in
  • mapQuestionIdx用于保存问题id和切片中的索引。注意问题id是键,索引是值

所以它看起来像这样

query := "select id, text, user_id from questions limit 10 offset 10"
rows, err := db.QueryCtx(ctx, query)
//处理错误

var questions []Question
var userIds []string

questionIds := make([]int, 0, 10) //因为限制是10
mapQuestionIdx := make(map[int]int)
idx := 0

for rows.Next() {
  var q Question
  var userId string
  //扫描到`question`和`userId`
  questions = append(questions, q)
  userIds = append(questions, userId)

  questionIds = append(questionIds, q.ID)
  mapQuestionIdx[q.ID] = idx
  idx++
}

关于查询获取问题的答案

  • 添加选择question_id
//添加选择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
//处理错误
for rowAnswer.Next(){
  var answer Answer
  var question_id int
  //扫描到answer和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)
}
英文:

In get questions, you need var questionIds and mapQuestionIdx

  • add select id
  • questionIds is for get question id, so you can query where in
  • mapQuestionIdx is for save question id and index in slice. Note question_id is key and index is value.

so it look like this

query := &quot;select id, text, user_id from questions limit 10 offset 10&quot;
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

  • add select question_id
// add select question_id
query = &quot;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)&quot;

  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)
  }

答案2

得分: 0

你可以利用结构标签和encoding/json的组合来正确地解组你的结果。

以下是你的用例示例:

重新声明你的类型:

type Question struct {
  Id int `json:"id"`
  Text string `json:"text"`
  User User `json:"user"`
  Answer []Answer `json:"answer"`

  // 填写其余字段
  // ...
}

进行解组操作:

someDummyQuestionsResult := []byte{
  // 一些示例数据
}

var questions []Question
err := json.Unmarshal(someDummyQuestionsResult, &questions)

fmt.Println(questions)

以上是你要翻译的内容。

英文:

You can make use of a combination of struct tag and encoding/json to un-marshal your result properly.

Example for your use case:

Redeclare your type as

type Question struct {
Id int `json:&quot;id&quot;`
Text string `json:&quot;text&quot;`
User User `json:&quot;user&quot;`
Answer []Answer `json:&quot;answer&quot;`
// Fill the rest yourself
...
}

To unmarshal simply:


someDummyQuestionsResult := []byte{
{
// some example
}
}
var questions []Question
err := json.Unmarshal(someDummyQuestionsResult , &amp;questions)
fmt.Println(questions) 

huangapple
  • 本文由 发表于 2022年8月26日 17:34:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/73498953.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定