go-gorm postgres dialect: управление структурами для вставки и поиска jsonb для правильного использования тегов json

Я много искал, и хотя я могу найти кучу хороших статей, которые объясняют, как работать с пакетом pq напрямую. Я не могу работать в контексте go-gorm и диалекта postgresql.

Если в Checks.go я использую ChecksMap, он не позволяет мне вставлять, но позволяет найти. Если я использую postgres.jsonb, он позволяет мне вставлять и запрашивать, но найденные записи будут jsonb.

Горм использует структуру указателя для определения таблицы и схемы БД. Это вызывает головную боль при использовании универсальной утилиты searchHandler, которая возвращает ответ json от API. Для любых типов, отличных от jsonb, gorm работает с соответствующими структурами и использует теги json, но для jsonb, поскольку он не имеет ссылки на «структуру» jsonb, он не может использовать теги json. Это приводит к тому, что возвращаемый API json имеет ключи с заглавной буквы.

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

Есть ли элегантный способ справиться с такими вещами, чтобы столбец результатов jsonb имел правильную структуру и использовал теги json в нижнем регистре? Я просто пытаюсь делать то, чего не следует делать в контексте гурманов?

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

person Jeff    schedule 03.09.2019    source источник
comment
Попробуйте реализовать интерфейс driver.Valuer на приемнике значения, а не на указателе. То есть поменять func (cm *ChecksMap) Value() ... на func (cm ChecksMap) Value() ..., вставлять все равно не удастся, если вы это сделаете? И если он по-прежнему не работает, можете ли вы показать, в чем ошибка, и если ошибки нет, можете ли вы показать, как вы реализовали метод Value()?   -  person mkopriva    schedule 03.09.2019
comment
@mkopriva Это сработало. Вы просто находка, и я начинаю развивать саморефлексию, которая включает в себя постановку глупых вопросов с действительно глупыми ответами или опечатками. Изучение Go было проблемой, и ТОЛЬКО нашел информацию в документации gorm, которая намекает на это ... gorm.io/docs/query.html#Struct-amp-Map, но не содержит подробностей. Если вы напишете краткий ответ о реализации driver.valuer, я считаю, что это подходящий ответ на вопрос, несмотря на мой неправильный код. Тогда я смогу проголосовать за. Но твой звонок. Спасибо еще раз.   -  person Jeff    schedule 03.09.2019


Ответы (1)


Вы можете использовать настраиваемый тип 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 справится с этим делом)

person mkopriva    schedule 03.09.2019