DynamoDB テーブルとクライアントのクラスをマッピングする Aws::Record を使用して DynamoDB を Ruby on Rails から操作する方法をチュートリアル風にメモっとく。
Java や C# 向けには高レベルプログラミングインターフェイスが公式に用意されている。しかし、公式な Ruby 向けの高レベルプログラミングインターフェイスの Aws::Record は2016年8月にリリースされたばかりで、開発者ガイドにも詳細な記載はない。(AWS SDK for Ruby バージョン1には似たような機能が搭載されていたが、AWS SDK for Ruby バージョン2には搭載されていない。)この記事では Aws::Record と Ruby on Rails を使用して Amazon DynamoDB 入門ガイドの Ruby および DynamoDB のチュートリアルを再実装してみることにする。
事前準備
Ruby と Ruby on Rails をインストールしておくこと。この記事では Ruby 2.3.1 と Ruby on Rails 5.0.0.1 を対象とする。
DynamoDB Local もインストールして起動しておく。OS X であれば OS X に DynamoDB Local を Homebrew でインストールして brew services で起動するを参考にしてほしい。
Rails プロジェクトを作成する
Rails プロジェクトを適当な名前で作成する。以降はプロジェクトディレクトリを作業ディレクトリとする。
$ rails new dynamodb-tutorial
$ cd dynamodb-tutorial
Rails 用の AWS SDK for Ruby と Aws::Record をインストールする
Gemfile に aws-sdk-rails
と aws-record
を追加する。
@@ -26,6 +26,10 @@ gem 'jbuilder', '~> 2.5'
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
+# Use AWS SDK for Ruby
+gem 'aws-sdk-rails'
+# Use Aws::Record as a data mapping abstraction for Amazon DynamoDB
+gem 'aws-record'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
bundle
を実行してインストールする。
$ bundle
AWS SDK for Ruby を設定する
AWS SDK for Ruby 用に環境別設定ファイルを記述する。なお、アクセスキー ID が接続先データベースに1対1に対応するので、DynamoDB JavaScript Shell などで DynamoDB を閲覧するときはアクセスキー ID を一致させる。シークレットアクセスキーとリージョンの設定も必須。DynamoDB JavaScript Shell から閲覧するのであればリージョンは us-west-2 に設定する。また、開発やテストでは DynamoDB Local を使用するのでエンドポイントを http://localhost:8000
に設定する。
development:
access_key_id: DynamodbTutorialDevelopment
secret_access_key: DynamodbTutorialDevelopmentSecret
region: us-west-2
endpoint: http://localhost:8000
test:
access_key_id: DynamodbTutorialTest
secret_access_key: DynamodbTutorialTestSecret
region: us-west-2
endpoint: http://localhost:8000
production:
access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
region: <%= ENV["AWS_REGION"] %>
# Be sure to restart your server when you modify this file.
# Configure AWS SDK for Ruby.
Aws.config.update(Rails.application.config_for(:aws).symbolize_keys)
この設定の詳細は Rails で AWS SDK for Ruby の Aws.config を環境別に設定するを見てほしい。
Step 1: テーブルを作成する
ここからは Amazon DynamoDB 入門ガイドの Ruby および DynamoDB のチュートリアルに沿って実装していく。
まずは Movies という名前のテーブルを作成する。この記事では Rails のマイグレーションを使用してテーブルを作成することにする。
Movie モデルを作成する
まずは下記のように Movie モデルを作成する。Aws::Record はモデルからマイグレーションに必要な情報を生成するためだ。
class Movie
include Aws::Record
integer_attr :year, hash_key: true
string_attr :title, range_key: true
map_attr :info
end
マイグレーションファイルを作成する
次にマイグレーションファイルを生成する。
$ rails g migration create_movies
ファイルの内容を下記のように書き換える。provisioned_throughput
は DynamoDB Local では無視される。本番環境の DynamoDB のマイグレーションでも使用することを考えると最小値に設定しておくのが無難かな。
マイグレーションのロールバックはできないように down
メソッドで ActiveRecord::IrreversibleMigration
例外を投げるようにしている。テーブルの削除については記事の終わりの方を見てほしい。
class CreateMovies < ActiveRecord::Migration[5.0]
def up
migration = Aws::Record::TableMigration.new(Movie)
migration.create!(
provisioned_throughput: {
read_capacity_units: 1,
write_capacity_units: 1
}
)
migration.wait_until_available
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
マイグレーションを実行する
マイグレーションのコマンドを実行する。
$ rails db:migrate
なお、このプロジェクトでは Rails のマイグレーションを管理しているテーブルは SQLite データベースに作成される。DynamoDB のみをデータベースとして使用するプロジェクトの場合は Rails のマイグレーションを管理しているテーブルをどうするか考えなければならない。Rails のマイグレーションを使用しないというのもひとつの解決策かと思う。
Movie テーブルの存在を確認する
Rails コンソールで Movie テーブルの存在を確認する。
$ rails c
> Movie.table_exists?
[Aws::DynamoDB::Client 200 0.211896 0 retries] describe_table(table_name:"Movie")
=> true
> exit
Step 2: サンプルデータをロードする
映画のサンプルデータを DynamoDB にロードする。この記事では Rails のシード機能を使用する。
サンプルデータファイルをダウンロードする
サンプルデータをダウンロードして db/moviedata.json
として保存する。
$ curl -O http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/gettingstartedguide/samples/moviedata.zip
$ unzip moviedata.zip
$ mv moviedata.json db
$ rm moviedata.zip
Movie テーブルにサンプルデータをロードする
db/moviedata.json
の内容を DynamoDB に保存するコードをシードファイルに記述する。
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first)
# Load movies
file = File.read('db/moviedata.json')
movies = JSON.parse(file)
movies.each do |movie|
Movie.new(movie).save!
end
シードファイルを実行してサンプルデータをロードする。この実行には数十秒程度の時間がかかるので少し待とう。
$ rails db:seed
サンプルデータのロードを確認する
Rails コンソールでサンプルデータの存在を確認する。
$ rails c
> Movie.find(year: 2013, title: 'Rush')
[Aws::DynamoDB::Client 200 0.051161 0 retries] get_item(table_name:"Movie",key:{"year"=>{n:"2013"},"title"=>{s:"Rush"}})
=> #<Movie:0x007fe09d5a1268 @data=#<Aws::Record::ItemData:0x007fe09d58add8 @data={:year=>#<BigDecimal:7fe09d5a2410,'0.2013E4',9(18)>, :title=>"Rush", :info=>{"actors"=>["Daniel Bruhl", "Chris Hemsworth", "Olivia Wilde"], "release_date"=>"2013-09-02T00:00:00Z", "plot"=>"A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.", "genres"=>["Action", "Biography", "Drama", "Sport"], "image_url"=>"http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg", "directors"=>["Ron Howard"], "rating"=>#<BigDecimal:7fe09d5a16f0,'0.83E1',18(18)>, "rank"=>#<BigDecimal:7fe09d5a15b0,'0.2E1',9(18)>, "running_time_secs"=>#<BigDecimal:7fe09d5a1470,'0.738E4',9(18)>}}, @clean_copies={:year=>2013, :title=>"Rush", :info=>{"actors"=>["Daniel Bruhl", "Chris Hemsworth", "Olivia Wilde"], "release_date"=>"2013-09-02T00:00:00Z", "plot"=>"A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.", "genres"=>["Action", "Biography", "Drama", "Sport"], "image_url"=>"http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg", "directors"=>["Ron Howard"], "rating"=>#<BigDecimal:7fe09d589e60,'0.83E1',18(18)>, "rank"=>#<BigDecimal:7fe09d589d98,'0.2E1',9(18)>, "running_time_secs"=>#<BigDecimal:7fe09d589cd0,'0.738E4',9(18)>}}, @dirty_flags={}, @model_attributes=#<Aws::Record::ModelAttributes:0x007fe09ac7f3d0 @model_class=Aws::Record::Attributes, @attributes={:year=>#<Aws::Record::Attribute:0x007fe09ac4de98 @name=:year, @database_name="year", @dynamodb_type="N", @marshaler=#<Aws::Record::Marshalers::IntegerMarshaler:0x007fe09ac572b8>, @persist_nil=nil>, :title=>#<Aws::Record::Attribute:0x007fe09ac45e28 @name=:title, @database_name="title", @dynamodb_type="S", @marshaler=#<Aws::Record::Marshalers::StringMarshaler:0x007fe09ac45ec8>, @persist_nil=nil>, :info=>#<Aws::Record::Attribute:0x007fe09d2c8de8 @name=:info, @database_name="info", @dynamodb_type="M", @marshaler=#<Aws::Record::Marshalers::MapMarshaler:0x007fe09d2c8ed8>, @persist_nil=nil>}, @storage_attributes={"year"=>:year, "title"=>:title, "info"=>:info}>, @track_mutations=true>>
Step 3: 項目を作成・取得・更新・削除する
Aws::Record を使用して項目の作成・読み込み・更新・削除を Rails コンソールから実行してみる。Rails でスキャフォルドした Web インターフェイスから操作できるようにしようかと考えたのだけど、複合プライマリキーだったり、全件表示するようなデータでもなかったり、info が複雑だったりするので諦めた。元のチュートリアルもそこまでやってないしね。
新しい項目を作成する
新しい項目は ActiveRecord のように作成できる。
$ rails c
> movie = Movie.new(year: 2015, title: 'The Big New Movie', info: { plot: 'Nothing happens at all.', rating: 0 })
> movie.save!
[Aws::DynamoDB::Client 200 0.18404 0 retries] put_item(table_name:"Movie",item:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"},"info"=>{m:{"plot"=>{s:"Nothing happens at all."},"rating"=>{n:"0"}}}},condition_expression:"attribute_not_exists(#H) and attribute_not_exists(#R)",expression_attribute_names:{"#H"=>"year","#R"=>"title"})
=> #<struct Aws::DynamoDB::Types::PutItemOutput attributes=nil, consumed_capacity=nil, item_collection_metrics=nil>
項目を取得する
項目の取得ではプライマリキーをハッシュ形式で指定する。Movie モデルのように複合プライマリキーの場合はパーティションキー(ハッシュキー)とソートキー(レンジキー)の両方を指定する必要がある。
$ rails c
> Movie.find(year: 2015, title: 'The Big New Movie')
[Aws::DynamoDB::Client 200 0.081038 0 retries] get_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}})
=> #<Movie:0x007fe09d4726a8 @data=#<Aws::Record::ItemData:0x007fe09d4725e0 @data={:year=>#<BigDecimal:7fe09d473210,'0.2015E4',9(18)>, :title=>"The Big New Movie", :info=>{"rating"=>#<BigDecimal:7fe09d472bf8,'0.0',9(18)>, "plot"=>"Nothing happens at all."}}, @clean_copies={:year=>2015, :title=>"The Big New Movie", :info=>{"rating"=>#<BigDecimal:7fe09d472018,'0.0',9(18)>, "plot"=>"Nothing happens at all."}}, @dirty_flags={}, @model_attributes=#<Aws::Record::ModelAttributes:0x007fe09d2632b8 @model_class=Aws::Record::Attributes, @attributes={:year=>#<Aws::Record::Attribute:0x007fe09d2390a8 @name=:year, @database_name="year", @dynamodb_type="N", @marshaler=#<Aws::Record::Marshalers::IntegerMarshaler:0x007fe09d242b58>, @persist_nil=nil>, :title=>#<Aws::Record::Attribute:0x007fe09ab7c618 @name=:title, @database_name="title", @dynamodb_type="S", @marshaler=#<Aws::Record::Marshalers::StringMarshaler:0x007fe09ab7c6b8>, @persist_nil=nil>, :info=>#<Aws::Record::Attribute:0x007fe09ab6e8d8 @name=:info, @database_name="info", @dynamodb_type="M", @marshaler=#<Aws::Record::Marshalers::MapMarshaler:0x007fe09ab6e978>, @persist_nil=nil>}, @storage_attributes={"year"=>:year, "title"=>:title, "info"=>:info}>, @track_mutations=true>>
項目を更新する
項目の更新ではプライマリキーと変更する属性をハッシュ形式で指定する。
$ rails c
> Movie.update(year: 2015, title: 'The Big New Movie', info: { plot: 'Everything happens all at once.', rating: 5.5, actors: ["Larry", "Moe", "Curly"] })
[Aws::DynamoDB::Client 200 0.042996 0 retries] update_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}},update_expression:"SET #UE_A = :ue_a",expression_attribute_names:{"#UE_A"=>"info"},expression_attribute_values:{":ue_a"=>{m:{"plot"=>{s:"Everything happens all at once."},"rating"=>{n:"5.5"},"actors"=>{l:[{s:"Larry"},{s:"Moe"},{s:"Curly"}]}}}})
=> #<struct Aws::DynamoDB::Types::UpdateItemOutput attributes=nil, consumed_capacity=nil, item_collection_metrics=nil>
また下記のように項目を取得してから編集して保存してもいい。
$ rails c
> movie = Movie.find(year: 2015, title: 'The Big New Movie')
[Aws::DynamoDB::Client 200 0.102433 0 retries] get_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}})
=> #<Movie:0x007fe09ac256a0 @data=#<Aws::Record::ItemData:0x007fe09ac25628 @data={:year=>#<BigDecimal:7fe09ac262d0,'0.2015E4',9(18)>, :title=>"The Big New Movie", :info=>{"rating"=>#<BigDecimal:7fe09ac25f10,'0.55E1',18(18)>, "plot"=>"Everything happens all at once.", "actors"=>["Larry", "Moe", "Curly"]}}, @clean_copies={:year=>2015, :title=>"The Big New Movie", :info=>{"rating"=>#<BigDecimal:7fe09ac24f70,'0.55E1',18(18)>, "plot"=>"Everything happens all at once.", "actors"=>["Larry", "Moe", "Curly"]}}, @dirty_flags={}, @model_attributes=#<Aws::Record::ModelAttributes:0x007fe09d2632b8 @model_class=Aws::Record::Attributes, @attributes={:year=>#<Aws::Record::Attribute:0x007fe09d2390a8 @name=:year, @database_name="year", @dynamodb_type="N", @marshaler=#<Aws::Record::Marshalers::IntegerMarshaler:0x007fe09d242b58>, @persist_nil=nil>, :title=>#<Aws::Record::Attribute:0x007fe09ab7c618 @name=:title, @database_name="title", @dynamodb_type="S", @marshaler=#<Aws::Record::Marshalers::StringMarshaler:0x007fe09ab7c6b8>, @persist_nil=nil>, :info=>#<Aws::Record::Attribute:0x007fe09ab6e8d8 @name=:info, @database_name="info", @dynamodb_type="M", @marshaler=#<Aws::Record::Marshalers::MapMarshaler:0x007fe09ab6e978>, @persist_nil=nil>}, @storage_attributes={"year"=>:year, "title"=>:title, "info"=>:info}>, @track_mutations=true>>
> movie.info['rating'] += 1
=> #<BigDecimal:7fe09ab3baa0,'0.65E1',18(27)>
> movie.save!
[Aws::DynamoDB::Client 200 0.029221 0 retries] update_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}},update_expression:"SET #UE_A = :ue_a",expression_attribute_names:{"#UE_A"=>"info"},expression_attribute_values:{":ue_a"=>{m:{"rating"=>{n:"6.5"},"plot"=>{s:"Everything happens all at once."},"actors"=>{l:[{s:"Larry"},{s:"Moe"},{s:"Curly"}]}}}})
=> #<struct Aws::DynamoDB::Types::UpdateItemOutput attributes=nil, consumed_capacity=nil, item_collection_metrics=nil>
項目を削除する
項目の削除ではプライマリキーを指定した項目を作成して削除する。削除対象のプライマリキーが判明していればこの方法でいい。
> movie = Movie.new(year: 2015, title: 'The Big New Movie')
=> #<Movie:0x007fd6a23a9eb0 @data=#<Aws::Record::ItemData:0x007fd6a23a9e60 @data={:year=>2015, :title=>"The Big New Movie"}, @clean_copies={}, @dirty_flags={}, @model_attributes=#<Aws::Record::ModelAttributes:0x007fd6a217a1a8 @model_class=Aws::Record::Attributes, @attributes={:year=>#<Aws::Record::Attribute:0x007fd6a20d35b0 @name=:year, @database_name="year", @dynamodb_type="N", @marshaler=#<Aws::Record::Marshalers::IntegerMarshaler:0x007fd69fe628a0>, @persist_nil=nil>, :title=>#<Aws::Record::Attribute:0x007fd6a20c10b8 @name=:title, @database_name="title", @dynamodb_type="S", @marshaler=#<Aws::Record::Marshalers::StringMarshaler:0x007fd6a20c1158>, @persist_nil=nil>, :info=>#<Aws::Record::Attribute:0x007fd6a20b09c0 @name=:info, @database_name="info", @dynamodb_type="M", @marshaler=#<Aws::Record::Marshalers::MapMarshaler:0x007fd6a20b0a60>, @persist_nil=nil>}, @storage_attributes={"year"=>:year, "title"=>:title, "info"=>:info}>, @track_mutations=true>>
> movie.delete!
[Aws::DynamoDB::Client 200 0.032924 0 retries] delete_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}})
=> true
また下記のように項目を取得してから削除してもいい。読み取りと書き込みの2つのリクエストが発生するので前述の方法より無駄がある。
$ rails c
> movie = Movie.find(year: 2015, title: 'The Big New Movie')
[Aws::DynamoDB::Client 200 0.062304 0 retries] get_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}})
=> #<Movie:0x007fe09d041980 @data=#<Aws::Record::ItemData:0x007fe09d041868 @data={:year=>#<BigDecimal:7fe09d042b78,'0.2015E4',9(18)>, :title=>"The Big New Movie", :info=>{"rating"=>#<BigDecimal:7fe09d042498,'0.65E1',18(18)>, "plot"=>"Everything happens all at once.", "actors"=>["Larry", "Moe", "Curly"]}}, @clean_copies={:year=>2015, :title=>"The Big New Movie", :info=>{"rating"=>#<BigDecimal:7fe09d040c88,'0.65E1',18(18)>, "plot"=>"Everything happens all at once.", "actors"=>["Larry", "Moe", "Curly"]}}, @dirty_flags={}, @model_attributes=#<Aws::Record::ModelAttributes:0x007fe09d2632b8 @model_class=Aws::Record::Attributes, @attributes={:year=>#<Aws::Record::Attribute:0x007fe09d2390a8 @name=:year, @database_name="year", @dynamodb_type="N", @marshaler=#<Aws::Record::Marshalers::IntegerMarshaler:0x007fe09d242b58>, @persist_nil=nil>, :title=>#<Aws::Record::Attribute:0x007fe09ab7c618 @name=:title, @database_name="title", @dynamodb_type="S", @marshaler=#<Aws::Record::Marshalers::StringMarshaler:0x007fe09ab7c6b8>, @persist_nil=nil>, :info=>#<Aws::Record::Attribute:0x007fe09ab6e8d8 @name=:info, @database_name="info", @dynamodb_type="M", @marshaler=#<Aws::Record::Marshalers::MapMarshaler:0x007fe09ab6e978>, @persist_nil=nil>}, @storage_attributes={"year"=>:year, "title"=>:title, "info"=>:info}>, @track_mutations=true>>
> movie.delete!
[Aws::DynamoDB::Client 200 0.027339 0 retries] delete_item(table_name:"Movie",key:{"year"=>{n:"2015"},"title"=>{s:"The Big New Movie"}})
=> true
Step 4: データをクエリおよびスキャンする
DynamoDB からデータを取得するときに使用するクエリとスキャンを Rails コンソールから実行してみる。
クエリで1年間にリリースされたすべての映画を取得する
1985年にリリースされたすべての映画を取得してみる。年を表現するパーティションキーが1985である項目をクエリで取得する。現時点での Aws::Record のクエリは AWS SDK for Ruby のクエリをテーブル名を補完して呼び出しているだけなので引数は同じフォーマットになる。
$ rails c
> movies = Movie.query(
key_condition_expression: '#yr = :yyyy',
expression_attribute_names: { '#yr' => 'year' },
expression_attribute_values: { ':yyyy' => 1985 })
=> #<Aws::Record::ItemCollection:0x007fe09ae542f0 @search_method=:query, @search_params={:key_condition_expression=>"#yr = :yyyy", :expression_attribute_names=>{"#yr"=>"year"}, :expression_attribute_values=>{":yyyy"=>1985}, :table_name=>"Movie"}, @model=Movie, @client=#<Aws::DynamoDB::Client>>
> movies.each do |movie|
puts "#{movie.year.to_i} #{movie.title}"
end
[Aws::DynamoDB::Client 200 0.134007 0 retries] query(key_condition_expression:"#yr = :yyyy",expression_attribute_names:{"#yr"=>"year"},expression_attribute_values:{":yyyy"=>{n:"1985"}},table_name:"Movie")
1985 A Nightmare on Elm Street Part 2: Freddy's Revenge
1985 A Room with a View
1985 A View to a Kill
.
.
.
1985 The Return of the Living Dead
1985 Weird Science
1985 Witness
=> nil
クエリで1年間にリリースされた特定のタイトルを持つすべての映画を取得する
1992年にリリースされたすべての映画から、文字 A から文字 L までで始まる映画を取得してみる。題名を表現するソートキーで範囲指定している。
$ rails c
> movies = Movie.query(
projection_expression: '#yr, title, info.genres, info.actors[0]',
key_condition_expression: '#yr = :yyyy and title between :letter1 and :letter2',
expression_attribute_names: { '#yr' => 'year' },
expression_attribute_values: { ':yyyy' => 1992, ':letter1' => 'A', ':letter2' => 'L' })
> movies.each do |movie|
print "#{movie.year.to_i}: #{movie.title} ... "
movie.info['genres'].each do |gen|
print gen + ' '
end
print " ... #{movie.info['actors'][0]}\n"
end
1992: A Few Good Men ... Crime Drama Mystery Thriller ... Tom Cruise
1992: A League of Their Own ... Comedy Drama Sport ... Tom Hanks
1992: A River Runs Through It ... Drama ... Craig Sheffer
.
.
.
1992: Howards End ... Drama Romance ... Anthony Hopkins
1992: Jennifer Eight ... Crime Drama Mystery Thriller ... Andy Garcia
1992: Juice ... Crime Drama Thriller ... Omar Epps
=> nil
スキャンとフィルターで1950年代の映画を取得する
1950年代、つまり1950年から1959年までにリリースされた映画を取得してみる。パーティションキーについての範囲指定になるためクエリが使えないのでスキャンとフィルターを使用している。また、このスキャンの結果セットは 1 MB を超えるためページ分割が発生するのだが、次のページを取得する処理を Aws::Record が自動的にしてくれている。
$ rails c
> movies = Movie.scan(
projection_expression: '#yr, title, info.rating',
filter_expression: '#yr between :start_yr and :end_yr',
expression_attribute_names: { '#yr' => 'year' },
expression_attribute_values: { ':start_yr' => 1950, ':end_yr' => 1959 })
=> #<Aws::Record::ItemCollection:0x007fd69ff708a0 @search_method=:scan, @search_params={:projection_expression=>"#yr, title, info.rating", :filter_expression=>"#yr between :start_yr and :end_yr", :expression_attribute_names=>{"#yr"=>"year"}, :expression_attribute_values=>{":start_yr"=>1950, ":end_yr"=>1959}, :table_name=>"Movie"}, @model=Movie, @client=#<Aws::DynamoDB::Client>>
> movies.each do |movie|
puts "#{movie.year.to_i}: #{movie.title} ... #{movie.info['rating'].to_f}"
end
[Aws::DynamoDB::Client 200 0.296593 0 retries] scan(projection_expression:"#yr, title, info.rating",filter_expression:"#yr between :start_yr and :end_yr",expression_attribute_names:{"#yr"=>"year"},expression_attribute_values:{":start_yr"=>{n:"1950"},":end_yr"=>{n:"1959"}},table_name:"Movie")
1952: High Noon ... 8.2
1952: Singin' in the Rain ... 8.4
1952: The Member of the Wedding ... 6.8
.
.
.
1951: The Day the Earth Stood Still ... 7.9
[Aws::DynamoDB::Client 200 0.124166 0 retries] scan(projection_expression:"#yr, title, info.rating",filter_expression:"#yr between :start_yr and :end_yr",expression_attribute_names:{"#yr"=>"year"},expression_attribute_values:{":start_yr"=>{n:"1950"},":end_yr"=>{n:"1959"}},table_name:"Movie",exclusive_start_key:{"title"=>{s:"Iron Man 2"},"year"=>{n:"2010.0"}})
1955: East of Eden ... 8.0
.
.
.
1950: Rashomon ... 8.4
1950: Sunset Blvd. ... 8.6
1950: Tea for Two ... 6.4
=> nil
Step 5: テーブルを削除する
最後に Movies テーブルを削除する。これは Rails のマイグレーションのロールバックを使用して実装する。
マイグレーションファイルを修正する
down
メソッドの内容を下記のように修正する。
@@ -11,6 +11,7 @@ class CreateMovies < ActiveRecord::Migration[5.0]
end
def down
- raise ActiveRecord::IrreversibleMigration
+ migration = Aws::Record::TableMigration.new(Movie)
+ migration.delete!
end
end
マイグレーションのロールバックを実行する
マイグレーションのロールバックを実行する。
$ rails db:rollback
Movie テーブルが存在しないことを確認する
Rails コンソールで Movie テーブルが存在しないことを確認する。
$ rails c
> Movie.table_exists?
[Aws::DynamoDB::Client 400 0.06534 0 retries] describe_table(table_name:"Movie") Aws::DynamoDB::Errors::ResourceNotFoundException Cannot do operations on a non-existent table
=> false
> exit
Aws::Record を使用して DynamoDB のテーブルを Ruby on Rails から操作する方法をチュートリアル風に説明した。AWS SDK for Ruby で DynamoDB を操作するよりは便利なんじゃないかな。ぜひ DynamoDB を使用した Rails アプリケーションに挑戦してみてね。
関連記事
OS X に DynamoDB Local を Homebrew でインストールして brew services で起動する
Rails で AWS SDK for Ruby の Aws.config を環境別に設定する
参考文献
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/gettingstartedguide/GettingStarted.Ruby.html
https://aws.amazon.com/jp/blogs/developer/general-availability-release-of-the-aws-record-gem/
http://docs.aws.amazon.com/awssdkrubyrecord/api/index.html