Dynamoid / dynamoid

Ruby ORM for Amazon's DynamoDB.
MIT License
576 stars 197 forks source link

Conditional update array element #684

Open mzientkowski opened 11 months ago

mzientkowski commented 11 months ago

Hi, i'm using latest version (v 3.9.0) and I try to update element in array with condition. Here is example:

class Test
  include Dynamoid::Document

  table name: :test, key: :id, capacity_mode: :on_demand

  field :test_array, :array, of: :string
end
Test.find(id).update(if: { 'test_array[0]': 'test' }) do |t|
  t.set('test_array[0]': 'test_pass')
end

it doesn't work and ends with an error:

[Aws::DynamoDB::Client 400 0.211219 0 retries] update_item(table_name:"test",key:{"id"=>{s:"1"}},attribute_updates:{"updated_at"=>{action:"PUT",value:{s:"2023-09-25T07:19:01+00:00"}},"test_array[0]"=>{action:"PUT",value:{s:"test_pass"}}},expected:{"test_array[0]"=>{value:{s:"test"}}},return_values:"ALL_NEW") Aws::DynamoDB::Errors::ConditionalCheckFailedException The conditional request failed

I managed to achieve this using low-level methods

Dynamoid.adapter.client.update_item(
  {
    table_name: 'test',
    key: {
      "id": id
    },
    expression_attribute_values: {
      ":new_array_elem" => 'test_pass',
      ":prev_array_elem" => 'test',
    },
    condition_expression: "test_array[0] = :prev_array_elem",
    update_expression: "SET test_array[0] = :new_array_elem",
    return_values: "ALL_NEW"
  }
)

And it works:

[Aws::DynamoDB::Client 200 0.158401 0 retries] update_item(table_name:"test",key:{"id"=>{s:"1"}},expression_attribute_values:{":new_array_elem"=>{s:"test_pass"},":prev_array_elem"=>{s:"test"}},condition_expression:"test_array[0] = :prev_array_elem",update_expression:"SET test_array[0] = :new_array_elem",return_values:"ALL_NEW")

it would be nice if it also worked from Dynamioid level. Or maybe i'm doing something wrong, so please provide working example ;) Thanks

andrykonchin commented 11 months ago

Dynamoid doesn't support yet conditions on/updating of nested attributes or array elements.

But migration from deprecated attributes to the new expression-like ones (e.g. mentioned in the example above condition_expression and update_expression) is in progress. I've stated with #where method (https://github.com/Dynamoid/dynamoid/pull/655) and hopefully will migrate all the public methods till the next release.