LoginSignup
10
3

More than 1 year has passed since last update.

【Rails+DynamoDB】Dynamoidの主な使用方法まとめ

Last updated at Posted at 2022-06-03

はじめに

DynamoidAmazon DynamoDBをRailsアプリケーションで利用するためのORMです。
Dynamoidを用いることで、ActiveRecordに近い感覚でDynamoDBにアクセスできるようになります。
この記事ではRailsアプリケーションにDynamoidを導入する手順と主な使用方法をまとめました。

導入

Gemfileに以下を追記します。

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に記述します。

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に接続情報を記述します。

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

モデルファイルの準備

テーブルごとにモデルファイルを作成します。

app/models/user.rb
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
app/models/purchase.rb
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で設定できます。

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に設定を記述することで、全てのテーブルに対して設定を適用できます。

config/initializers/dynamoid.rb
require 'dynamoid'
Dynamoid.configure do |config|
  # created_atとupdate_atを追加しない
  config.timestamps = false
end
10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3