go-gorm postgres 方言:管理 jsonb 插入和查找的结构以正确使用 json 标签

go-gorm postgres dialect: managing structs for jsonb insert and find to properly use json tags

进行了大量搜索,虽然我可以找到一堆解释如何直接使用 pq 包的好文章。我在 go-gorm 和 postgresql 方言的上下文中不知所措。

如果在 checks.go 中我使用 ChecksMap 它不允许我插入但会让我找到。如果我使用 postgres.jsonb 它允许我插入和查询,但找到的记录将是 jsonb.

Gorm 使用指针的结构来确定数据库 table 和模式。这在使用 return 是来自 API 的 json 响应的通用 searchHandler 实用程序时会引起头痛。对于任何非 jsonb 类型,gorm 使用适当的结构并使用 json 标签,但对于 jsonb,因为它没有对 jsonb 的引用"struct" 它不能使用 json 标签。这导致 return API json 具有大写键。

{
   results: {
      id: "123",
      someId: "456",
      results: [
         {
            Description: "foobar"
         }
      ]
   }
}

是否有一种优雅的方法来处理此类事情,以便 jsonb 结果列具有正确的结构并使用小写的 json 标签?我只是想做一些在 go-gorm 环境下不应该做的事情吗?

POSTGRESQL DDL

CREATE TABLE checks (
   id        text,
   some_id   text,
   results   jsonb
);

checks.go

type CheckRules struct {
   Description   string `json:"description"`
}

type ChecksMap   map[string]CheckRules

type Checks struct {
   ID            string           `gorm: "primary_key", json:"id"`
   SomeID        *string          `json:"someId"`
   Results       postgres.jsonb   `json:"results"`                   // <-- this
   // results    ChecksMap        `gorm:"type:jsonb" json:"results"` // <-- or this
}

// func (cm *ChecksMap) Value() (driver.Value, error) {...}
// func (cm *ChecksMap) Scan(val interface{}) error {...}

insertChecks.go

var resultsVal = getResultsValue() // simplified
resJson, _ := json.Marshal(resultsVal)

checks := Checks{
   SomeID: "123",
   Results: postgres.Jsonb{ RawMessage: json.RawMessage(resJson) }
}

err := db.Create(&checks).Error
// ... some error handling

getChecks.go

var checks Checks

err := db.Find(&checks).Error
// ... some error handling

searchHandler.go

func SearchHandler(db *gorm.DB, model, results interface{}) func(c echo.Context) error {
   return func(c echo.Context) error {
      err := db.Find(results).Error
      // ... some error handling

      jsnRes, _ := json.Marshal(results) // <-- uppercase "keys"

      return c.JSON(http.StatusOK, struct {
         Results interface{} `json:"results"`
      }{
         Results: string(jsnRes),
      })
   }
}

您可以使用自定义 ChecksMap 类型,但在其 值接收器 而不是指针接收器上实现 driver.Valuer 接口。

所以,而不是:

func (cm *ChecksMap) Value() (driver.Value, error) { ...

你会这样写:

func (cm ChecksMap) Value() (driver.Value, error) {
    if cm == nil {
        return nil, nil
    }
    return json.Marshal(cm)
}

或者,您可以使它与指针实现一起使用,但是您必须将字段转换为指针,例如:

type Checks struct {
   ID      string     `gorm: "primary_key", json:"id"`
   SomeID  *string    `json:"someId"`
   Results *ChecksMap `json:"results"`
}

(虽然我没有测试过所以我不是 100% 确定 gorm 将如何处理这种情况)