Ruby — обновите настройку столбца массива jsonb с помощью jsonb_accessor

#ruby-on-rails #ruby #postgresql #jsonb

#ruby-on-rails #ruby #postgresql #jsonb

Вопрос:

У меня есть это поле, data которое было настроено с jsonb_accessor помощью ruby gem следующим образом:

 jsonb_accessor :data,
  line_items: [:jsonb, array: true, default: []]
  

Это в моей Contract модели. И когда a Contract создается, это пустой массив, подобный этому:

 data: {"line_items"=>[]},
  

Я хочу добавить несколько хэшей в этот пустой массив. Например, это:

 {user: "Joe"}
  

Затем позже я могу добавить хэш:

 {user: "Jack"}
  

Я пробовал много вещей, но безуспешно, в том числе:

 new = [{user: "Joe"}].to_json
Contract.last.update_all(["line_items = line_items || ?::jsonb", new])
##
Contract.last.update(["line_items = line_items || ?::jsonb", new])
  

Затем я попытался push

 Contract.last.line_items.push("line_items" => {user: "Todd"})
  

Затем я попытался merge

 Contract.last.line_items = Contract.last.line_items.merge(new)
  

Ничего не сработало.

Ответ №1:

Одним из вариантов было бы просто установить атрибут с нужными данными, а затем сохранить его с #save помощью (или #save! )

 contract = Contract.create!
=> #<Contract id: 1, data: {"line_items"=>[]}, line_items: []>

contract.line_items = [{foo: "bar"}]
contract.save!

contract
=> #<Contract id: 1, data: {"line_items"=>[{"foo"=>"bar"}]}, line_items: [{"foo"=>"bar"}]>
  

В моем тестировании я смог использовать метод #update, просто передав сам объект (вместо того, чтобы сначала преобразовать его в JSON):

 Contract.update(1, :line_items => [{foo: "bar"}])
=> #<Contract id: 1, data: {"line_items"=>[{"foo"=>"bar"}]}, line_items: [{"foo"=>"bar"}]>
  

Редактировать:
Похоже, вы пытаетесь добавить к массиву с помощью одного запроса, что представляется возможным с помощью

 Contract.where(id: 1).update_all("data=jsonb_set(data,'{line_items}', data->'line_items' || '{"foo": "bar"}')")
  

Хотя я уверен, что есть лучший способ добиться той же функциональности…

Ответ №2:

jsonb_accessor означает, что вам не нужно вручную возиться с JSON для простых операций доступа. Если вы хотите назначить для line_items использования update! как обычно.

 contract.update!(line_items: [{user: "Joe"}])
  

Если вы хотите добавить к одному контракту line_items , добавьте в массив в Ruby и обновите.

 contract = Contract.last
line_items = contract.line_items   [{user: "Joe"}]
contract.update!(line_items: line_items)
  

Если вы хотите сделать все это в SQL jsonb_accessor , вам не поможет. Вы должны написать SQL самостоятельно. Чтобы добавить в массив, используйте jsonb_insert .

 Contract
  .where(...)
  .update_all(
    q[jsonb_insert(data, '{"line_items", -1}', '{user: "Joe"}', true)]
  )
  

'{"line_items", -1}' говорит вставить в последний элемент ключа «line_items» и true должен вставить после этой точки.