Go SQL映射1:M关系json数组到切片

问题描述 投票:0回答:2

我有一个

product-image (1:M)
关系,基本上我想将这些图像映射到我的结构中的一个切片。我正在使用 sqlx 库来使其更容易一些。

我搜索了一段时间,也许最好的答案就在这个帖子中:Efficiently地将一对多多对多数据库映射到Golang中的结构。 创建视图并将所有内容作为 json 返回的答案是可行的,但感觉有点hacky。

理想情况下,我想要做的是使用 postgres

json_agg
返回图像列中与我的类型匹配的 json 数组,以便我可以扫描它。

我用nodejs做了几次,但这里我不知道怎么做。我是否需要实现自定义

Scan
Value
方法,或者是否有更简单的方法。我没有展示这一点,但我也有一个 1:1 的类别,我可以将该类别嵌入产品中并执行左连接,它适用于 sqlx,但不适用于图像类型。

简化模型

type Image struct {
  ID int,
  URL string,
  ProductID int
}

type ImageList []*Image

type Product struct {
  ID int `db:"id"`
  Name string `db:"name"`
  Images ImageList `db:"images"`
}

数据库表

create table product (
  id int generated always as identity primary key,
  name varchar(255),
);

create table product_image (
  id int generated always as identity primary key,
  url text not null,
  product_id int references product(id)
);

我现在正在尝试这样的事情:

q := `SELECT 
      p.*,
      COALESCE(JSON_AGG(img.*) FILTER (WHERE img.product_id IS NOT NULL), '[]'::json) AS images
      FROM product p
      LEFT JOIN product_image img ON p.id = img.product_id
      WHERE p.id = 1
      GROUP BY p.id`

var p Product
if err := sqlxdb.Get(&p, q); err != nil {
    fmt.Printf("Error: %v\n", err)
}

我收到此错误:

sql: Scan error on column index 26, name "images": unsupported Scan, storing driver.Value type []uint8 into type *model.ImageList

这似乎是一个非常常见的场景,但我找不到任何例子...... 或者最后我什至愚蠢地这样做,因为我可以在 JS 中执行 forEach 并执行 50 个查询来获取每个产品的所有图像。

postgresql go struct sqlx
2个回答
1
投票

可以使用此映射库来完成获取项目列表的一种解决方案:carta

q := `SELECT p.*, img.id AS img_id, img.url AS img_url 
      FROM public.product p
      LEFT JOIN product_image img ON p.id = img.product_id`

    rows, err := sqlxdb.Query(q)
    if err != nil {
        fmt.Println(err)
    }

    var products []*model.Product
    carta.Map(rows, &products)

对于 img 结构,我将使用

db:"img_id
” 前缀等,因为我使用别名进行选择...


0
投票

你的问题是你正在返回一个 json 数据的 postgres 数组。对于标准扫描代码来说,这看起来像一个 []uint8。这并不像评论中提到的将扫描仪接口添加到数组那么简单。 Postgres 有一个特定的文本格式来传递需要正确解析的数组。

查看此代码以了解如何正确解析 Postgres 数组列。 https://github.com/lib/pq/blob/master/array.go

这是我整理的一个示例(基于我最近使用的一些代码)来演示解决您的问题的一种可能方法。我确信它不是最佳的,但它在本地测试中有效。

type PqArrayOfJson[T any] []T

func (a *PqArrayOfJson[T]) Scan(src interface{}) error {
    raw := &pq.ByteaArray{}
    if err := raw.Scan(src); err != nil {
        return err
    }

    parsed := make(PqArrayOfJson[T], len(*raw))
    for i, v := range *raw {
        nv := *new(T)
        if err := json.Unmarshal(v, &nv); err != nil {
            return err
        }
        parsed[i] = nv
    }
    *a = parsed

    return nil
}

func (a PqArrayOfJson[T]) Value() (driver.Value, error) {
    mapped := make([][]byte, len(a))
    for i, v := range a {
        data, err := json.Marshal(&v)
        if err != nil {
            return nil, err
        }
        mapped[i] = data
    }
    return pq.ByteaArray(mapped).Value()
}

然后你会使用这样的东西

type ImageList = PqArrayOfJson[*Image]

这是使用 pq lib 将传入的 json 数据数组解析为

[][]byte
,然后使用泛型类型
json.Unmarshal
将其中的每个元素输入
T
,以创建一个要解组的新变量。

如果您将此作为一个想法,您可以创建专用的

ImageList
类型并忽略代码的所有通用方面。

© www.soinside.com 2019 - 2024. All rights reserved.