はじめに
DynamoidはAmazon DynamoDBをRailsアプリケーションで利用するためのORMです。
Dynamoidを用いることで、ActiveRecordに近い感覚でDynamoDBにアクセスできるようになります。
この記事ではRailsアプリケーションにDynamoidを導入する手順と主な使用方法をまとめました。
導入
Gemfileに以下を追記します。
gem 'dynamoid'
バージョンについて
AWS SDKとのバージョン互換性は次のようになっています。
Dynamoid version | AWS SDK version |
---|---|
0.x | 1.x |
1.x | 2.x |
2.x | 2.x |
3.x | 3.x |
AWSの接続設定をconfig/initializers/aws.rb
に記述します。
Aws.config.update({
region: 'your-region',
credentials: Aws::Credentials.new('ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')
})
アプリケーション全体のAWS接続設定とは別にDynamoidについてのみ接続設定を行いたい場合は、config/initializers/dynamoid.rb
に接続情報を記述します。
require 'dynamoid'
Dynamoid.configure do |config|
config.access_key = 'ACCESS_KEY_ID'
config.secret_key = 'SECRET_ACCESS_KEY'
config.region = 'your-region'
end
モデルファイルの準備
テーブルごとにモデルファイルを作成します。
class User
include Dynamoid::Document
table name: :user, key: :user_id
field :name, :string
field :hometown, :string, default: '東京都'
field :age, :integer
global_secondary_index hash_key: :hometown, range_key: :age,
projected_attributes: :all,
name: 'hometown-age-index'
end
class Purchase
include Dynamoid::Document
table name: :purchase, key: :purchase_id
range :purchase_datetime, :datetime
field :user_id, :string
field :product_id, :string
end
各モデルファイルにinclude Dynamoid::Document
の記述が必要です。
table name: :user, key: :user_id
テーブル名とパーティションキー(旧称: ハッシュキー)を指定します。
ここで指定したパーティションキー(user_id
)はstring型で扱われます。
string型以外のパーティションキーを設定したいとき
table name: :user, key: :user_id
attributes[:user_id][:type] = :integer
このように型を指定することでuser_id
をinteger型として扱うことができました。
また次のようにuser_id
をフィールドとして指定し直した場合もinteger型として扱うことができました。
table name: :user, key: :user_id
field :user_id, :integer
ただしこのときは次のようなWarningが出力されます。
# Method user_id generated for the field user_id overrides already existing method
# Method user_id= generated for the field user_id overrides already existing method
# Method user_id? generated for the field user_id overrides already existing method
range :purchase_datetime, :datetime
ソートキー(旧称: レンジキー)とその型を指定します。
DynamoDBにおいてuserテーブルのようにソートキーが設定されていない場合、パーティションキーを重複させることはできません。
一方purchaseテーブルのようにソートキーが設定されている場合は、ソートキーが異なるものに限りパーティションキーを重複させることができます。
field :name, :string
field :hometown, :string, default: '東京都'
field :age, :integer
各フィールドの名前とその型を指定します。
default
を用いてフィールドに初期値を設定することも可能です。
global_secondary_index hash_key: :hometown, range_key: :age,
projected_attributes: :all,
name: 'hometown-age-index'
必要に応じてグローバルセカンダリーインデックスを指定できます。
この指定はフィールドの記述の下で行う必要があります。
where句で暗黙的にグローバルセカンダリインデックスを用いた検索を行うためにはprojected_attributes: :all
の指定が必要です。
使用方法
オブジェクトの作成
次のようにcreateを使うことで作成できます。
User.create(
user_id: '1',
name: '山田太郎',
age: 23
)
またnewとsaveを使って作成することも可能です。
purchase = Purchase.new
purchase.purchase_id = '1'
purchase.purchase_datetime = Time.zone.parse('2022-04-01 00:00:00')
purchase.user_id = '1'
purchase.product_id = '2'
purchase.save
このときフィールド名はpurchase[:purchase_id]
のようにシンボルで指定することもできます。
データ取得
findによりパーティションキーやソートキーを指定してデータ1つを取得できます。
User.find('1')
# => #<DynamoDb::User:0x00...>
Purchase.find('1', range_key: Time.zone.parse('2022-04-01 00:00:00'))
# => #<DynamoDb::ActionLog:0x00...>
指定した条件に合うデータが存在しない場合はエラーとなります。
User.find('2')
# => Dynamoid::Errors::RecordNotFound
Purchase.find('1', range_key: Time.zone.parse('2021-05-31 12:30:00'))
# => Dynamoid::Errors::RecordNotFound
find_by_idを用いて特定のデータ1つを取得することもできます。
find_by_idでは指定した条件に合うデータが存在しない場合nilが返されます。
User.find_by_id('1')
# => #<DynamoDb::User:0x00...>
Purchase.find_by_id('1', range_key: Time.zone.parse('2022-04-01 00:00:00'))
# => #<DynamoDb::ActionLog:0x00...>
User.find_by_id('2')
# => nil
Purchase.find_by_id('1', range_key: Time.zone.parse('2021-05-31 12:30:00'))
# => nil
whereを使うと指定した条件に合うデータをまとめて取得できます。
User.where(user_id: '1').all
# => #<Enumerator::Lazy: ...>
ソートキーに対して不等号条件などを指定することもできます。
ソートキー以外に対しては、これらの条件を指定することでクエリ結果の絞り込みが行われます。
Purchase.where(purchase_datetime: Time.zone.parse('2022-04-01 00:00:00'))
Purchase.where('purchase_datetime.lt': Time.zone.parse('2022-04-01 00:00:00'))
Purchase.where('purchase_datetime.lte': Time.zone.parse('2022-04-01 00:00:00'))
Purchase.where('purchase_datetime.gt': Time.zone.parse('2022-04-01 00:00:00'))
Purchase.where('purchase_datetime.gte': Time.zone.parse('2022-04-01 00:00:00'))
Purchase.where('purchase_datetime.between': [Time.zone.parse('2022-04-01 00:00:00'), Time.zone.parse('2022-05-01 00:00:00')])
User.where('hometown.begins_with': '東')
※簡略化のためにパーティションキーの指定を省いて例示していますが、この場合スキャン(全件走査)が実行され処理効率が落ちることにご注意ください。
更新
updateを使うことでデータの更新が可能です。
# ユーザー1の年齢を30に更新
User.update('1', age: 30)
# 購入1の商品IDを5に更新
Purchase.update('1', Time.zone.parse('2022-04-01 00:00:00'), product_id: '5')
update_attributesを使って書くこともできます。
User.find('1').update_attributes(age: 30)
削除
deleteやdelete_allを使うことでデータを削除できます。
User.find('1').delete
User.where(hometown: '東京都').delete_all
その他
日時データの保存について
モデルファイルのフィールド型を:date
や:datetime
で指定したとき、DynamoDBには数値(UNIX時間)で保存されます。
オプションにstore_as_string: true
を指定するとiso8601形式の文字列として保存されるようになります。
タイムゾーンはconfig/initializers/dynamoid.rb
で設定できます。
require 'dynamoid'
Dynamoid.configure do |config|
# タイムゾーン設定
config.application_timezone = 'Asia/Tokyo'
config.dynamodb_timezone = 'Asia/Tokyo'
end
created_at/updated_atカラムについて
Dynamoidを使用すると各テーブルにcreated_atとupdate_atの2つのフィールドが自動で追加されます。
これを無効にしたい場合はモデルファイルにtimestamps
の設定を追加します。
table name: :user, key: :user_id, timestamps: false
またconfig/initializers/dynamoid.rb
に設定を記述することで、全てのテーブルに対して設定を適用できます。
require 'dynamoid'
Dynamoid.configure do |config|
# created_atとupdate_atを追加しない
config.timestamps = false
end