簡體   English   中英

將 PostgreSQL 字段(ARRAY 類型)掃描到 Go 結構的切片中

[英]Scan a PostgreSQL field (of ARRAY type) into a slice of Go structs

假設我有:

type User struct {
    ID int64 `json:"id"
    Posts []Post `json:"posts"
}

type Post struct {
    ID int64 `json:"id"
    Text string `json:"text"
}

SQL 查詢:

WITH temp AS (SELECT u.id AS user_id, p.id AS post_id, p.text AS post_text FROM users u JOIN posts p ON u.id=p.user_id)

SELECT user_id, ARRAY_AGG(ARRAY[post_id::text, post_text]) 
FROM temp
GROUP BY user_id
)

我想要的是從上面的查詢中掃描行到用戶對象的切片中:

 import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v4/pgxpool"
    "github.com/lib/pq"
 )

 var out []User

    rows, _ := client.Query(context.Background(), query) // No error handling for brevity

    for rows.Next() {

        var u User

        if err := rows.Scan(&u.ID, pq.Array(&u.Posts)); err != nil {
            return
        }

        out = append(out, u)

    }

幾乎可以預料,上面的代碼失敗了:

pq: cannot convert ARRAY[4][2] to StringArray

這是有道理的,但是有沒有辦法將 SQL output 讀入我的用戶群?

lib/pq不支持掃描任意類型的多維 arrays,如結構。 如果您想掃描這樣的數組,您必須在自定義sql.Scanner實現中自己解析和解碼它。

例如:

type PostList []Post

func (ls *PostList) Scan(src any) error {
    var data []byte
    switch v := src.(type) {
    case string:
        data = []byte(v)
    case []byte:
        data = v
    }

    // The data var holds the multi-dimensional array value,
    // something like: {{"1","foo"}, {"2","bar"}, ...}
    // The above example is easy to parse but too simplistic,
    // the array is likely to be more complex and therefore
    // harder to parse, but not at all impossible if that's
    // what you want.

    return nil
}

如果您想了解有關 PostgreSQL 數組表示語法的更多信息,請參閱:


An approach that does not require you to implement a parser for PostgreSQL arrays would be to build and pass JSON objects, instead of PostgreSQL arrays, to array_agg . 其結果將是一個以jsonb作為元素類型的一維數組。

SELECT user_id, array_agg(jsonb_build_object('id', post_id, 'text', post_text)) 
FROM temp
GROUP BY user_id

然后自定義sql.Scanner的實現只需要委托給lib/pq.GenericArray和另一個特定於元素的sql.Scanner ,將委托給encoding/json

type PostList []Post

func (ls *PostList) Scan(src any) error {
    return pq.GenericArray{ls}.Scan(src)
}

func (p *Post) Scan(src any) error {
    var data []byte
    switch v := src.(type) {
    case string:
        data = []byte(v)
    case []byte:
        data = v
    }
    return json.Unmarshal(data, p)
}

type User struct {
    ID    int64    `json:"id"`
    Posts PostList `json:"posts"`
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM