Class: Dynamoid::Transactions::Mutation
- Inherits:
-
Object
- Object
- Dynamoid::Transactions::Mutation
- Defined in:
- lib/dynamoid/transactions/mutation.rb,
lib/dynamoid/transactions/mutation/inc.rb,
lib/dynamoid/transactions/mutation/base.rb,
lib/dynamoid/transactions/mutation/save.rb,
lib/dynamoid/transactions/mutation/touch.rb,
lib/dynamoid/transactions/mutation/create.rb,
lib/dynamoid/transactions/mutation/import.rb,
lib/dynamoid/transactions/mutation/upsert.rb,
lib/dynamoid/transactions/mutation/destroy.rb,
lib/dynamoid/transactions/mutation/increment.rb,
lib/dynamoid/transactions/mutation/update_fields.rb,
lib/dynamoid/transactions/mutation/update_attributes.rb,
lib/dynamoid/transactions/mutation/delete_with_instance.rb,
lib/dynamoid/transactions/mutation/delete_with_primary_key.rb,
lib/dynamoid/transactions/mutation/builders/delete_request_builder.rb,
lib/dynamoid/transactions/mutation/builders/update_request_builder.rb
Overview
The Mutation class provides a way to perform multiple modifying operations
atomically in a transaction—either all operations succeed, or all fail.
The persistence methods are designed to mirror their non-transactional
counterparts like .create, #save, and #delete:
user = User.new(name: 'John')
payment = Payment.find(1)
Dynamoid::Transactions::Mutation.execute do |t|
t.save! user
t.create! Account, name: 'A'
t.delete payment
end
The primary difference is that these methods are called on a transaction instance, and the model (or class) must be passed as an argument.
For example, user.save! becomes t.save!(user), Account.create!(name: 'A')
becomes t.create!(Account, name: 'A'), and payment.delete becomes
t.delete(payment).
Transactions can also be used without a block by manually instantiating and committing:
t = Dynamoid::Transactions::Mutation.new
t.save! user
t.create! Account, name: 'A'
t.delete payment
t.commit
Some persistence methods (like .update and .update!) are intentionally
unavailable because they perform multiple underlying operations and cannot
be implemented atomically.
DynamoDB Transactions
Unlike many databases, DynamoDB transactions are executed in batch.
In Dynamoid, no changes are persisted when a method like #save is called.
All changes are queued and sent to DynamoDB at the end of the transaction.
This uses the DynamoDB TransactWriteItems operation (see
documentation).
Callbacks
Transactional methods support before_, after_, and around_ callbacks
to the same extent as their non-transactional counterparts.
Important difference: Callbacks (even after_ ones) run immediately
when the method is called, before changes are actually persisted to
DynamoDB. Consequently, code in an after_ callback will not yet see the
updated data in DynamoDB.
If a callback aborts the operation or a model is invalid, that specific action is skipped, but the transaction itself may still commit successfully.
Transaction Rollback
A transaction is automatically rolled back on the DynamoDB side if:
- Another operation is currently updating the same item.
- Provisioned capacity is insufficient.
- Item or transaction size limits are exceeded (e.g., item > 400 KB or total > 4 MB).
- There is a user error (e.g., invalid data format).
Since no changes are persisted until the #commit call, a transaction can
be aborted by simply raising an exception within the block.
Raising Dynamoid::Errors::Rollback will interrupt the transaction without
propagating the exception further:
Dynamoid::Transactions::Mutation.execute do |t|
t.save! user
t.create! Account, name: 'A'
raise Dynamoid::Errors::Rollback if user.is_admin?
end
When a transaction is successfully committed or rolled back, the corresponding
#after_commit or #after_rollback callbacks are run for each involved model.
Class Method Summary collapse
Instance Method Summary collapse
-
#commit ⇒ Object
Persist all the changes.
-
#create(model_class, attributes = {}, &block) ⇒ Dynamoid::Document
Create a model.
-
#create!(model_class, attributes = {}, &block) ⇒ Dynamoid::Document
Create a model.
-
#decrement!(model, attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
-
#delete(model_or_model_class, hash_key = nil, range_key = nil) ⇒ Dynamoid::Document
Delete a model.
-
#destroy(model) ⇒ Dynamoid::Document
Delete a model.
-
#destroy!(model) ⇒ Dynamoid::Document|false
Delete a model.
-
#import(model_class, array_of_attributes) ⇒ Array<Dynamoid::Document>
Create multiple models from an array of attribute hashes.
-
#inc(model_class, hash_key, range_key = nil, counters = nil) ⇒ nil
Increment numeric attributes.
-
#increment!(model, attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
-
#initialize ⇒ Mutation
constructor
A new instance of Mutation.
- #rollback ⇒ Object
-
#save(model, **options) ⇒ true|false
Create new model or persist changes in already existing one.
-
#save!(model, **options) ⇒ true|false
Create new model or persist changes in already existing one.
-
#touch(model, *names, time: nil) ⇒ Dynamoid::Document
Update the
updated_attimestamp and optionally other specified attributes. -
#update_attribute(model, attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
-
#update_attribute!(model, attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
-
#update_attributes(model, attributes) ⇒ true|false
Update multiple attributes at once.
-
#update_attributes!(model, attributes) ⇒ Object
Update multiple attributes at once.
-
#update_fields(model_class, hash_key, range_key = nil, attributes = nil, &block) ⇒ nil
Update document.
-
#upsert(model_class, hash_key, range_key = nil, attributes) ⇒ nil
Update an existing document or create a new one.
Constructor Details
#initialize ⇒ Mutation
Returns a new instance of Mutation.
119 120 121 |
# File 'lib/dynamoid/transactions/mutation.rb', line 119 def initialize @actions = [] end |
Class Method Details
.execute ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/dynamoid/transactions/mutation.rb', line 103 def self.execute transaction = new begin yield transaction rescue StandardError => e transaction.rollback unless e.is_a?(Dynamoid::Errors::Rollback) raise e end else transaction.commit end end |
Instance Method Details
#commit ⇒ Object
Persist all the changes.
transaction = Dynamoid::Transactions::Mutation.new
# ...
transaction.commit
128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/dynamoid/transactions/mutation.rb', line 128 def commit actions_to_commit = @actions.reject(&:aborted?).reject(&:skipped?) return if actions_to_commit.empty? action_requests = actions_to_commit.flat_map(&:action_requests) Dynamoid.adapter.transact_write_items(action_requests) actions_to_commit.each(&:on_commit) nil rescue Aws::Errors::ServiceError run_on_rollback_callbacks raise end |
#create(model_class, attributes = {}, &block) ⇒ Dynamoid::Document
Create a model.
Dynamoid::Transactions::Mutation.execute do |t|
t.create(User, name: 'A')
end
Accepts both Hash and Array of Hashes and can create several models.
Dynamoid::Transactions::Mutation.execute do |t|
t.create(User, [{name: 'A'}, {name: 'B'}, {name: 'C'}])
end
Instantiates a model and pass it into an optional block to set other attributes.
Dynamoid::Transactions::Mutation.execute do |t|
t.create(User, name: 'A') do |user|
user.initialize_roles
end
end
Validates model and runs callbacks.
Raises Dynamoid::Errors::MissingRangeKey if a sort key is required but
not specified or has value nil.
There are the following differences between transactional and
non-transactional #create:
- transactional
#createdoesn't raiseDynamoid::Errors::RecordNotUniqueat saving new model when primary key is already used. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - a table isn't created lazily if it doesn't exist yet
395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/dynamoid/transactions/mutation.rb', line 395 def create(model_class, attributes = {}, &block) if attributes.is_a? Array attributes.map do |attr| action = Create.new(model_class, attr, raise_error: false, &block) register_action action end else action = Create.new(model_class, attributes, raise_error: false, &block) register_action action end end |
#create!(model_class, attributes = {}, &block) ⇒ Dynamoid::Document
Create a model.
Dynamoid::Transactions::Mutation.execute do |t|
t.create!(User, name: 'A')
end
Accepts both Hash and Array of Hashes and can create several models.
Dynamoid::Transactions::Mutation.execute do |t|
t.create!(User, [{name: 'A'}, {name: 'B'}, {name: 'C'}])
end
Instantiates a model and pass it into an optional block to set other attributes.
Dynamoid::Transactions::Mutation.execute do |t|
t.create!(User, name: 'A') do |user|
user.initialize_roles
end
end
Validates model and runs callbacks.
Raises Dynamoid::Errors::MissingRangeKey if a sort key is required but
not specified or has value nil.
There are the following differences between transactional and
non-transactional #create:
- transactional
#create!doesn't raiseDynamoid::Errors::RecordNotUniqueat saving new model when primary key is already used. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - a table isn't created lazily if it doesn't exist yet
340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/dynamoid/transactions/mutation.rb', line 340 def create!(model_class, attributes = {}, &block) if attributes.is_a? Array attributes.map do |attr| action = Create.new(model_class, attr, raise_error: true, &block) register_action action end else action = Create.new(model_class, attributes, raise_error: true, &block) register_action action end end |
#decrement!(model, attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
Initializes attribute to zero if nil and subtracts the specified value
(by default is 1). Only makes sense for number-based attributes.
Runs callbacks.
Dynamoid::Transactions::Mutation.execute do |t|
t.decrement!(user, :followers_count)
t.decrement!(user, :followers_count, 2)
end
Only attribute is saved. The model itself is not saved. So any other
modified attributes will still be dirty. Validations are skipped.
When :touch option is passed the timestamp columns are updating. If
attribute names are passed, they are updated along with updated_at
attribute:
t.decrement!(user, :followers_count, touch: true)
t.decrement!(user, :followers_count, touch: :viewed_at)
t.decrement!(user, :followers_count, touch: [:viewed_at, :accessed_at])
677 678 679 |
# File 'lib/dynamoid/transactions/mutation.rb', line 677 def decrement!(model, attribute, by = 1, touch: nil) increment!(model, attribute, -by, touch: touch) end |
#delete(model_or_model_class, hash_key = nil, range_key = nil) ⇒ Dynamoid::Document
Delete a model.
Can be called either with a model:
Dynamoid::Transactions::Mutation.execute do |t|
t.delete(user)
end
or with a primary key:
Dynamoid::Transactions::Mutation.execute do |t|
t.delete(User, user_id)
end
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
There are the following differences between transactional and
non-transactional #delete: TBD
- transactional
#deletedoesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
#deletedoesn't disassociate a model from associated ones if there is any
838 839 840 841 842 843 844 845 |
# File 'lib/dynamoid/transactions/mutation.rb', line 838 def delete(model_or_model_class, hash_key = nil, range_key = nil) action = if model_or_model_class.is_a? Class DeleteWithPrimaryKey.new(model_or_model_class, hash_key, range_key) else DeleteWithInstance.new(model_or_model_class) end register_action action end |
#destroy(model) ⇒ Dynamoid::Document
Delete a model.
Runs callbacks.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
There are the following differences between transactional and
non-transactional #destroy:
- transactional
#destroydoesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
#destroydoesn't disassociate a model from associated ones if there are association declared in the model class
895 896 897 898 |
# File 'lib/dynamoid/transactions/mutation.rb', line 895 def destroy(model) action = Destroy.new(model, raise_error: false) register_action action end |
#destroy!(model) ⇒ Dynamoid::Document|false
Delete a model.
Runs callbacks.
Raises Dynamoid::Errors::RecordNotDestroyed exception if model deleting
failed (e.g. aborted by a callback).
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
There are the following differences between transactional and
non-transactional #destroy!:
- transactional
#destroy!doesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
#destroy!doesn't disassociate a model from associated ones if there are association declared in the model class
870 871 872 873 |
# File 'lib/dynamoid/transactions/mutation.rb', line 870 def destroy!(model) action = Destroy.new(model, raise_error: true) register_action action end |
#import(model_class, array_of_attributes) ⇒ Array<Dynamoid::Document>
Create multiple models from an array of attribute hashes.
Validations and callbacks are skipped.
Dynamoid::Transactions::Mutation.execute do |t|
t.import(User, [{ name: 'A' }, { name: 'B' }])
end
Since DynamoDB limits the total number of actions per transaction,
each model created via #import consumes one action from this limit.
916 917 918 919 |
# File 'lib/dynamoid/transactions/mutation.rb', line 916 def import(model_class, array_of_attributes) action = Import.new(model_class, array_of_attributes) register_action action end |
#inc(model_class, hash_key, range_key = nil, counters = nil) ⇒ nil
Increment numeric attributes.
Doesn't run validations and callbacks.
Dynamoid::Transactions::Mutation.execute do |t|
t.inc(User, '1', age: 1)
end
If range key is declared for a model it should be passed as well:
Dynamoid::Transactions::Mutation.execute do |t|
t.inc(User, '1', 'Tylor', age: 1)
end
It also supports touch option to update updated_at attribute and
optionally other specified attributes.
Dynamoid::Transactions::Mutation.execute do |t|
t.inc(User, '1', age: 1, touch: true)
end
If attribute names are passed, they are updated along with updated_at attribute:
t.inc(User, '1', age: 2, touch: :viewed_at)
t.inc(User, '1', age: 2, touch: [:viewed_at, :accessed_at])
It's an atomic operation. So incrementing or decrementing a numeric field is atomic and does not interfere with other write requests.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not declared in the model class.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and Dynamoid::Errors::MissingRangeKey if a sort key is required
but has value nil.
603 604 605 606 607 608 609 610 611 |
# File 'lib/dynamoid/transactions/mutation.rb', line 603 def inc(model_class, hash_key, range_key = nil, counters = nil) if range_key.is_a?(Hash) && !counters counters = range_key range_key = nil end action = Inc.new(model_class, hash_key, range_key, counters) register_action action end |
#increment!(model, attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
Initializes attribute to zero if nil and adds the specified value (by
default is 1). Only makes sense for number-based attributes.
Dynamoid::Transactions::Mutation.execute do |t|
t.increment!(user, :followers_count)
t.increment!(user, :followers_count, 2)
end
Only attribute is saved. The model itself is not saved. So any other
modified attributes will still be dirty. Validations and callbacks are
skipped.
When :touch option is passed the timestamp columns are updating. If
attribute names are passed, they are updated along with updated_at
attribute:
t.increment!(user, :followers_count, touch: true)
t.increment!(user, :followers_count, touch: :viewed_at)
t.increment!(user, :followers_count, touch: [:viewed_at, :accessed_at])
642 643 644 645 |
# File 'lib/dynamoid/transactions/mutation.rb', line 642 def increment!(model, attribute, by = 1, touch: nil) action = Increment.new(model, attribute, by, touch: touch) register_action action end |
#rollback ⇒ Object
142 143 144 |
# File 'lib/dynamoid/transactions/mutation.rb', line 142 def rollback run_on_rollback_callbacks end |
#save(model, **options) ⇒ true|false
Create new model or persist changes in already existing one.
Run the validation and callbacks. Raise
Dynamoid::Errors::DocumentNotValid unless this object is valid.
user = User.new
Dynamoid::Transactions::Mutation.execute do |t|
t.save(user)
end
Validation can be skipped with validate: false option:
user = User.new(age: -1)
Dynamoid::Transactions::Mutation.execute do |t|
t.save(user, validate: false)
end
save by default sets timestamps attributes - created_at and
updated_at when creates new model and updates updated_at attribute
when updates already existing one.
If a model is new and hash key (+id+ by default) is not assigned yet it was assigned implicitly with random UUID value.
When a model is not persisted - its id should have unique value. Otherwise a transaction will be rolled back.
Raises Dynamoid::Errors::MissingHashKey if a model is already persisted
and a partition key has value nil and raises
Dynamoid::Errors::MissingRangeKey if a sort key is required but has
value nil.
There are the following differences between transactional and
non-transactional #save:
- transactional
#savedoesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
#savedoesn't raiseDynamoid::Errors::RecordNotUniqueat saving new model when primary key is already used. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
savedoesn't raiseDynamoid::Errors::StaleObjectErrorwhen a model that is being updated was concurrently deleted - a table isn't created lazily if it doesn't exist yet
292 293 294 295 |
# File 'lib/dynamoid/transactions/mutation.rb', line 292 def save(model, **) action = Save.new(model, **, raise_error: false) register_action action end |
#save!(model, **options) ⇒ true|false
Create new model or persist changes in already existing one.
Run the validation and callbacks. Returns true if saving is successful
and false otherwise.
user = User.new
Dynamoid::Transactions::Mutation.execute do |t|
t.save!(user)
end
Validation can be skipped with validate: false option:
user = User.new(age: -1)
Dynamoid::Transactions::Mutation.execute do |t|
t.save!(user, validate: false)
end
save! by default sets timestamps attributes - created_at and
updated_at when creates new model and updates updated_at attribute
when updates already existing one.
If a model is new and hash key (+id+ by default) is not assigned yet it was assigned implicitly with random UUID value.
When a model is not persisted - its id should have unique value. Otherwise a transaction will be rolled back.
Raises Dynamoid::Errors::MissingHashKey if a model is already persisted
and a partition key has value nil and raises
Dynamoid::Errors::MissingRangeKey if a sort key is required but has
value nil.
There are the following differences between transactional and
non-transactional #save!:
- transactional
#save!doesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
#save!doesn't raiseDynamoid::Errors::RecordNotUniqueat saving new model when primary key is already used. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
save!doesn't raiseDynamoid::Errors::StaleObjectErrorwhen a model that is being updated was concurrently deleted - a table isn't created lazily if it doesn't exist yet
232 233 234 235 |
# File 'lib/dynamoid/transactions/mutation.rb', line 232 def save!(model, **) action = Save.new(model, **, raise_error: true) register_action action end |
#touch(model, *names, time: nil) ⇒ Dynamoid::Document
Update the updated_at timestamp and optionally other specified
attributes.
Runs callbacks.
Dynamoid::Transactions::Mutation.execute do |t|
t.touch(user)
end
If attribute names are passed, they are updated along with updated_at attribute:
t.touch(user, :viewed_at)
t.touch(user, :viewed_at, :accessed_at)
t.touch(user, time: 2.days.ago)
Raises Dynamoid::Errors::Error if a model is new or destroyed.
171 172 173 174 |
# File 'lib/dynamoid/transactions/mutation.rb', line 171 def touch(model, *names, time: nil) action = Touch.new(model, *names, time: time) register_action action end |
#update_attribute(model, attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
Dynamoid::Transactions::Mutation.execute do |t|
t.update_attribute(user, :last_name, 'Tylor')
end
Validation is skipped.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not on the model
770 771 772 773 |
# File 'lib/dynamoid/transactions/mutation.rb', line 770 def update_attribute(model, attribute, value) model.write_attribute(attribute, value) save(model, validate: false) end |
#update_attribute!(model, attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
Dynamoid::Transactions::Mutation.execute do |t|
t.update_attribute!(user, :last_name, 'Tylor')
end
Validation is skipped.
If any of the before_* callbacks throws :abort the action is
cancelled and update_attribute! raises
Dynamoid::Errors::RecordNotSaved.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not on the model
796 797 798 799 800 |
# File 'lib/dynamoid/transactions/mutation.rb', line 796 def update_attribute!(model, attribute, value) model.write_attribute(attribute, value) save!(model, validate: false) model end |
#update_attributes(model, attributes) ⇒ true|false
Update multiple attributes at once.
Dynamoid::Transactions::Mutation.execute do |t|
t.update_attributes(user, age: 27, last_name: 'Tylor')
end
Returns true if saving is successful and false
otherwise.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
There are the following differences between transactional and
non-transactional #update_attributes:
- transactional
#update_attributesdoesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
update_attributesdoesn't raiseDynamoid::Errors::StaleObjectErrorwhen a model that is being updated was concurrently deleted - a table isn't created lazily if it doesn't exist yet
711 712 713 714 |
# File 'lib/dynamoid/transactions/mutation.rb', line 711 def update_attributes(model, attributes) action = UpdateAttributes.new(model, attributes, raise_error: false) register_action action end |
#update_attributes!(model, attributes) ⇒ Object
Update multiple attributes at once.
Returns true if saving is successful and false
otherwise.
Dynamoid::Transactions::Mutation.execute do |t|
t.update_attributes(user, age: 27, last_name: 'Tylor')
end
Raises a Dynamoid::Errors::DocumentNotValid exception if some vaidation
fails.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
There are the following differences between transactional and
non-transactional #update_attributes!:
- transactional
#update_attributes!doesn't raiseDynamoid::Errors::StaleObjectErrorwhen optimistic concurrency control detects a conflict. A genericAws::DynamoDB::Errors::TransactionCanceledExceptionis raised instead. - transactional
update_attributes!doesn't raiseDynamoid::Errors::StaleObjectErrorwhen a model that is being updated was concurrently deleted - a table isn't created lazily if it doesn't exist yet
748 749 750 751 |
# File 'lib/dynamoid/transactions/mutation.rb', line 748 def update_attributes!(model, attributes) action = UpdateAttributes.new(model, attributes, raise_error: true) register_action action end |
#update_fields(model_class, hash_key, range_key = nil, attributes = nil, &block) ⇒ nil
Update document.
Doesn't run validations and callbacks.
Dynamoid::Transactions::Mutation.execute do |t|
t.update_fields(User, '1', age: 26)
end
If range key is declared for a model it should be passed as well:
Dynamoid::Transactions::Mutation.execute do |t|
t.update_fields(User, '1', 'Tylor', age: 26)
end
Updates can also be performed in a block.
Dynamoid::Transactions::Mutation.execute do |t|
t.update_fields(User, 1) do |u|
u.add(article_count: 1)
u.delete(favorite_colors: 'green')
u.set(age: 27, last_name: 'Tylor')
end
end
Operation add just adds a value for numeric attributes and join
collections if attribute is a set.
t.update_fields(User, 1) do |u|
u.add(age: 1, followers_count: 5)
u.add(hobbies: ['skying', 'climbing'])
end
Operation delete is applied to collection attribute types and
substructs one collection from another.
t.update_fields(User, 1) do |u|
u.delete(hobbies: ['skying'])
end
Operation set just changes an attribute value:
t.update_fields(User, 1) do |u|
u.set(age: 21)
end
Operation remove removes one or more attributes from an item.
t.update_fields(User, 1) do |u|
u.remove(:age)
end
All the operations work like ADD, DELETE, REMOVE, and SET actions supported
by UpdateExpression
parameter
of UpdateItem operation.
It's atomic operations. So adding or deleting elements in a collection or incrementing or decrementing a numeric field is atomic and does not interfere with other write requests.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not declared in the model class.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and Dynamoid::Errors::MissingRangeKey if a sort key is required
but has value nil.
There are the following differences between transactional and
non-transactional #update_fields:
- transactional
#update_fieldsdoesn't support conditions (that'sifandunless_existsoptions) - transactional
#update_fieldsdoesn't return a document that was updated or created
544 545 546 547 548 549 550 551 552 553 |
# File 'lib/dynamoid/transactions/mutation.rb', line 544 def update_fields(model_class, hash_key, range_key = nil, attributes = nil, &block) # given no attributes, but there may be a block if range_key.is_a?(Hash) && !attributes attributes = range_key range_key = nil end action = UpdateFields.new(model_class, hash_key, range_key, attributes, &block) register_action action end |
#upsert(model_class, hash_key, range_key = nil, attributes) ⇒ nil
Update an existing document or create a new one.
If a document with specified hash and range keys doesn't exist it creates a new document with specified attributes. Doesn't run validations and callbacks.
Dynamoid::Transactions::Mutation.execute do |t|
t.upsert(User, '1', age: 26)
end
If range key is declared for a model it should be passed as well:
Dynamoid::Transactions::Mutation.execute do |t|
t.upsert(User, '1', 'Tylor', age: 26)
end
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not declared in the model class.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and Dynamoid::Errors::MissingRangeKey if a sort key is required
but has value nil.
There are the following differences between transactional and
non-transactional #upsert:
- transactional
#upsertdoesn't support conditions (that'sifandunless_existsoptions) - transactional
#upsertdoesn't return a document that was updated or created
446 447 448 449 |
# File 'lib/dynamoid/transactions/mutation.rb', line 446 def upsert(model_class, hash_key, range_key = nil, attributes) # rubocop:disable Style/OptionalArguments action = Upsert.new(model_class, hash_key, range_key, attributes) register_action action end |