sqlx将postgres数组扫描到结构中

时间:2019-01-04 07:51:36

标签: postgresql go sqlx

我正在尝试创建一个基本的注释API。我似乎无法弄清楚如何将postgresql数组扫描到结构中的结构数组中。我想我可能可以将Thread.Posts类型设置为jsonb,但这似乎不太雅致,因为我不得不解组它。

  

sql:列索引3上的扫描错误,名称“帖子”:不支持的扫描,   将driver.Value类型[] uint8存储为类型* [] models.Post

var threadSchema = `
CREATE TABLE IF NOT EXISTS thread (
  id         SERIAL PRIMARY KEY,
  name       VARCHAR(100) NOT NULL,
  profile_id INTEGER REFERENCES profile (id)
)`

var postSchema = `
CREATE TABLE IF NOT EXISTS post (
  id         SERIAL PRIMARY KEY,
  comment    TEXT,
  profile_id INTEGER REFERENCES profile (id),
  thread_id  INTEGER REFERENCES thread (id)
)`

type Post struct {
    Id        int    `db:"id" json:"id"`
    Comment   string `db:"comment" json:"comment" binding:"required" form:"comment"`
    ProfileId int   `db:"profile_id" json:"profile_id" binding:"required" form:"profile_id"`
    ThreadId  int    `db:"thread_id" json:"thread_id" binding:"required" form:"thread_id"`
}

type Thread struct {
    Id        int    `db:"id" json:"id"`
    Name      string `db:"name" json:"name" binding:"required" form:"name"`
    ProfileId int    `db:"profile_id" json:"profile_id" binding:"required" form:"profile_id"`
    Posts     []Post `db:"posts" json:"posts" form:"posts"`
}

func GetThreads(db *sqlx.DB, c *gin.Context) {
    threads := []Thread{}
    err := db.Select(&threads, `
    SELECT thread.id,thread.name,thread.profile_id,array_agg(post.id) AS posts
    FROM thread
    INNER JOIN post ON thread.id = post.thread_id
    GROUP BY thread.id;
  `)
    if err != nil {
        log.Fatal(err)
    }
    c.JSON(http.StatusOK, gin.H{"data": threads})
}

2 个答案:

答案 0 :(得分:1)

您可以定义您的类型:

type Posts []Post

// Scan implements the sql.Scanner interface.
func (a *Posts) Scan(src interface{}) error {
  // ...
}

// Value implements the driver.Valuer interface.
func (a Posts) Value() (driver.Value, error) {
  // ...
}

有关实现的更多信息,请参见例如here

答案 1 :(得分:0)

首先,you can't do this with sqlx,无论您是否使用Postgres数组。

第二,您的SQL查询仅是汇总帖子ID,而不是帖子的内容,因此无法获取所需的数据(使用Go或其他方式)。

因此,您可以执行以下操作:

  1. 使用匿名嵌入式结构,捕获SQL查询中的所有Post内容,然后合并重复的线程。

    type Post struct {
        Id        int    `db:"id" json:"id"`
        Comment   string `db:"comment" json:"comment" binding:"required" form:"comment"`
        ProfileId int   `db:"profile_id" json:"profile_id" binding:"required" form:"profile_id"`
        ThreadId  int    `db:"thread_id" json:"thread_id" binding:"required" form:"thread_id"`
    }
    
    type ThreadDb struct {
        Id        int    `db:"id" json:"id"`
        Name      string `db:"name" json:"name" binding:"required" form:"name"`
        ProfileId int    `db:"profile_id" json:"profile_id" binding:"required" form:"profile_id"`
        Post
    }
    
    type Thread struct {
        Id        int    `db:"id" json:"id"`
        Name      string `db:"name" json:"name" binding:"required" form:"name"`
        ProfileId int    `db:"profile_id" json:"profile_id" binding:"required" form:"profile_id"`
        Posts     []Post `db:"posts" json:"posts" form:"posts"`
    }
    
    
    func GetThreads(db *sqlx.DB, c *gin.Context) {
        threads := []ThreadDb{}
        err := db.Select(&threads, `
        SELECT thread.id,thread.name,thread.profile_id,post.id,post.comment,post.profile_id,post.thread_id
        FROM thread
        INNER JOIN post ON thread.id = post.thread_id
        GROUP BY post.id;
      `)
    
        thread_map := make(map[string]Thread)
    
        for i, thread := range threads {
            if _, ok := thread_map[thread.Id]; ok {
                thread_map[thread.Id].Posts = append(thread_map[thread.Id].Posts, thread.Post)
            } else {
                thread_map[thread.Id] = Thread{thread.Id, thread.Name, thread.ProfileId, []Post{thread.Post}}
            }
        }
    
        var threadSlice []string
    
        for k := range thread_map {
            threadSlice = append(threadSlice, k)
        }
    
        if err != nil {
            log.Fatal(err)
        }
        c.JSON(http.StatusOK, gin.H{"data": threadSlice})
    }
    
  2. 使用GROUP_CONCAT或类似名称。除非您打算每个线程最多发布100条帖子,否则我不建议这样做。