#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
должен вставить после этой точки.