2
0

More than 1 year has passed since last update.

DynamoDBの基本的な操作備忘録(AWS SDK for Ruby版)

Posted at

扱える型

スカラー型

  • 数値
    • BigDecimal型で返されるため、必要に応じてto_iメソッドでInteger型に直す
  • 文字列
  • バイナリ
  • ブール
  • null

ドキュメント型

  • リスト
    • 複数の型を要素に持つことができる
  • マップ

セット型

ユニークで順序の保証されないリスト

  • 数値セット
  • 文字列セット
  • バイナリセット

データ取得

  • scan
    • テーブルのすべての項目を取得する
    • 全てのカラムを条件に使用可能
  • query
    • パーティションキーの指定が必須
    • ソートキーを指定しない場合、条件に合致する全てのレコードが返される
  • get_item
    • 1件に絞ってレコードを取得

filter_expression

  • 取得したデータに対し、結果セットを返す前に返すレコードを絞り込む条件を記述する
    • 例:filter_expression: 'Age > :age',:ageexpression_attribute_valuesで指定する。
  • scan, queryで指定可能
  • ただしフィルタ式の適用は結果取得後のため、フィルタ式の有無にかかわらずキャパシティーの消費量は同じ

projection_expression

  • 取得するカラムをカンマ区切りで指定する
    • 例:projection_expression: 'Name, Age',
  • scan, query, get_itemで指定可能

example_table1

Id(主キー) Name Age Email
1 Name1 21 one@example.com
2 Name2 18 two@example.com
3 Name3 30 three@example.com

example_table2

K1(パーティションキー) K2(ソートキー) Code
1 1 '1_1'
1 2 '1_2'
2 1 '2_1'

スキャン

  • 主キーやパーティションキー・ソートキー以外を条件に指定可能
  • filter_expression で条件を指定する。指定しない場合全件を取得する
client.scan({
  table_name: 'example_table1',
  filter_expression: 'Age >= :age',
  expression_attribute_values: {
    ':age' => 20,
  }
})

結果

Id Name Age Email
1 Name1 21 one@example.com
3 Name3 30 three@example.com

ページング

  • スキャンでは、1回の実行で最大1MBのデータを取得できる
    • ※フィルタ式を適用する前で1MB
  • 1MBを超えるデータを取得する場合、複数回のスキャン実行が必要
    • 続きがある場合、実行結果に last_evaluated_key というパラメータが含まれている。同じ条件でスキャンを実行する際に、exclusive_start_keylast_evaluated_key の値を指定しておくことで、続きの結果を取得できる。
scan_condition = {
  table_name: 'table_name',
  filter_expression: 'Age = :threshold',
  expression_attribute_values: {
    ':threshold' => 20,
  },
}

loop do
  result = client.scan(scan_condition)
  puts "items: #{result[:items]}"

  last_evaluated_key = result[:last_evaluated_key]
  puts "last_evaluated_key: #{last_evaluated_key}"

  # `last_evaluated_key` がnilの場合は次の結果が無いので終了する
  break if last_evaluated_key.nil?

  # `exclusive_start_key` に last_evaluated_key の値を指定する
  scan_condition[:exclusive_start_key] = last_evaluated_key
end

クエリ

  • query メソッドは主キーついての条件が必須
  • パーティションキーのみの場合は必然的に最大1件になる
  • パーティションキー+ソートキーの場合、パーティションキーのみ条件に指定すると複数件の結果が返る
  • filter_expression にパーティションキー・ソートキーを含めることはできない。key_condition_expression で指定する。

パーティションキーのみ

client.query({
  table_name: 'example_table1',
  key_condition_expression: 'Id = :id',
  expression_attribute_values: {
    ':id' => 1
  }
})

結果

Id Name Age Email
1 Name1 21 one@example.com

パーティションキー+ソートキー

client.query({
  table_name: 'example_table2',
  key_condition_expression: 'K1 = :v1 AND K2 = :v2',
  expression_attribute_values: {
    ':v1' => 1,
    ':v2' => 1,
  }
})

結果

K1 K2 code
1 1 '1_1'

パーティションキー+ソートキー(パーティションキーのみ指定)

client.query({
  table_name: 'example_table2',
  key_condition_expression: 'K1 = :v1',
  expression_attribute_values: {
    ':v1' => 1,
  }
})

結果

K1 K2 code
1 1 '1_1'
1 2 '1_2'

get_item

queryscan と異なり、1件のみを取得する

result = client.get_item({
  table_name: 'example_table1',
  key: {
    id: 1,
  },
})
puts result[:item]
# => {"Id"=>0.1e1, "Name"=>"Name1", "Age"=>0.21e2, "Email"=>"one@example.com"}

項目の更新

  • SET
  • ADD
  • REMOVE
  • DELETE

SET

  • 値の更新(上書き)を行う
  • 存在しないカラムを指定した場合、新たにカラムが追加されそこに値がセットされる

値の更新

Id Val
1 101
2 102
3 100
client.update_item({
  table_name: 'example_table3',
  key: {
    Id: 3,
  },
  update_expression: 'SET Val = :new_val',
  expression_attribute_values: {
    ':new_val' => 103,
  }
})

結果

Id Val
1 101
2 102
3 103

データの追加

client.update_item({
  table_name: 'example_table3',
  key: {
    Id: 4, # 存在しない値を指定
  },
  update_expression: 'SET Val = :val',
  expression_attribute_values: {
    ':val' => 104,
  }
})

結果

Id Val
1 101
2 102
3 103
4 104

カラムの追加

client.update_item({
  table_name: 'example_table3',
  key: {
    Id: 1,
  },
  update_expression: 'SET Val2 = :val', # 存在しないカラムを指定
  expression_attribute_values: {
    ':val' => 1001,
  }
})

結果

Id Val Val2
1 101 1001
2 102
3 103
4 104

複数の更新

例:値の更新とカラム追加を同時に行う

client.update_item({
  table_name: 'example_table3',
  key: {
    Id: 2,
  },
  update_expression: 'SET Val = :val, Val3 = :val3', # カンマ区切りで式を並べる
  expression_attribute_values: {
    ':val' => 202,
    ':val3' => 10002,
  }
})

結果

Id Val Val2 Val3
1 101 1001
2 202 10002
3 103
4 104

ADD

  • 値の追加を行う
  • 数値(Integer)型、リスト型、セット型で挙動が異なる
  • SETと同じく、存在しないカラムを指定した場合、カラムを作成し値をセットする

各型のカラムを用意

client.update_item({
  table_name: 'example_table4',
  key: {
    Id: 1,
  },
  # カンマ区切りで複数カラムに操作
  update_expression: 'ADD IntType :int, IntSetType :intset, StrSetType :strset, BinSetType :binset',
  expression_attribute_values: {
    ':int' => 1, # 数値型
    ':intset' => Set.new([1,2]), # 数値セット型
    ':strset' => Set.new(['one', 'two']), # 文字列セット型
    ':binset' => Set.new([StringIO.new('abc123')]), # バイナリセット型
  }
})

結果

Id IntType IntSetType StrSetType BinSetType
1 1 {2, 1} {"one", "two"} { YWJjMTIz }

数値

  • 数値型での ADD は単に足し算を行う
  • マイナスの値を指定すれば引き算される(マイナスを足す)
client.update_item({
  table_name: 'example_table4',
  key: {
    Id: 1,
  },
  update_expression: 'ADD IntType :int',
  expression_attribute_values: {
    ':int' => 100, # 1 + 100
  }
})

結果

Id IntType IntSetType StrSetType BinSetType
1 101 {2, 1} {"one", "two"} { YWJjMTIz }

セット型

セット型の ADD は、互いの要素を合わせたものから重複を取り除いたセット型になる

client.update_item({
  table_name: 'example_table4',
  key: {
    Id: 1,
  },
  update_expression: 'ADD IntSetType :intset, StrSetType :strset, BinSetType :binset',
  expression_attribute_values: {
    ':intset' => Set.new([2,3]), # 2は重複
    ':strset' => Set.new(['two', 'three']), # 'two'は重複
    ':binset' => Set.new([StringIO.new('abc123'), StringIO.new('xyz999')]), # StringIO.new('abc123')は重複
  }
})

結果

Id IntType IntSetType StrSetType BinSetType
1 101 {3, 2, 1} {'three', 'two', 'one'} { YWJjMTIz, eHl6OTk5 }

配列で要素の追加をしたい場合、ADDではなくSETを用いる

  • list_append(list1, list2)
    • 2つのリストを受け取り結合したものを返す
Id ListType
1 [{"N": "1"}, {"N": "2"}]
client.update_item({
  table_name: 'example_table5',
  key: {
    Id: 1,
  },
  update_expression: 'SET AnyList = list_append(AnyList, :l)',
  expression_attribute_values: {
    ':l' => [2, '3'], # 注:配列を渡す
  },
})

結果

Id ListType
1 [{"N": "1"}, {"N": "2"}, {"N": "2"}, {"S": "3"}]

ちなみに AnyList というカラムがまだない場合や、存在しないキー(レコードの作成を期待)を指定するとエラーが発生する

The provided expression refers to an attribute that does not exist in the item (Aws::DynamoDB::Errors::ValidationException)

カラムが存在しない場合の代わりの値を渡すことで解決することができる

  • if_not_exists(Column, default)
    • 第一引数に指定したカラムが存在する場合、第一引数の値を返す
    • 第一引数に指定したカラムが存在しない場合、第二引数の値を返す
client.update_item({
  table_name: 'example_table5',
  key: {
    Id: 1,
  },
  update_expression: 'SET AnyList2 = list_append(if_not_exists(AnyList2, :empty), :l)',
  expression_attribute_values: {
    ':l' => [1, '2', 3],
    ':empty' => [], # もちろん空でない配列でもOK
  }
})

REMOVE

すべての型

  • あるレコードの指定したカラムの値を空にする
  • DynamoDBの性質上、この操作によりすべてのレコードがそのカラムに値を持たなくなった場合、カラム自体も削除される
  • 存在しないカラムを指定してもエラーにはならない
Id Column1 Column2 Column3 Column4
1 col1 col2 col3
2 col1 col2 col4
client.update_item({
  table_name: 'example_table6',
  key: {
    Id: 1,
  },
  update_expression: 'REMOVE Column2, Column3, Column4, Column5', # カンマ区切りで複数指定可能
}

結果

Id Column1 Column2 Column4
1 col1
2 col1 col2 col4

リスト型

  • REMOVE AnyList[0] のように、REMOVE + インデックスで要素指定を行うことで、リストの任意の位置の要素を削除できる
Id AnyList
1 [{"N": "1"}, {"N": "2"}, {"S": "3"}]
client.update_item({
  table_name: 'example_table5',
  key: {
    Id: 1,
  },
  update_expression: 'REMOVE AnyList[1]',
}

結果

Id AnyList
1 [{"N": "1"}, {"S": "3"}]

DELETE

  • セット型から要素を削除する
Id IntSet1 IntSet2
1 {1,2,3} {4,5}
client.update_item({
  table_name: 'example_table6',
  key: {
    Id: 1,
  },
  update_expression: 'DELETE IntSet1 :s1, IntSet2 :s2',
  expression_attribute_values: {
    ':s1' => Set.new([1,4]),
    ':s2' => Set.new([4,5]),
  },
})

結果

Id IntSet1
1 {2,3}

条件

  • 更新する際に条件を設けることも可能
  • 条件を満たさない場合更新は行われずエラーが返る
Id Val
1 100
client.update_item({
  table_name: 'example_table7',
  key: {
    id: 1,
  },
  update_expression: 'SET Val = Val + :v',
  condition_expression: 'Val > :threshold',
  expression_attribute_values: {
    ':threshold' => 100,
    ':v' => 100,
  }
})
# => The conditional request failed (Aws::DynamoDB::Errors::ConditionalCheckFailedException)
client.update_item({
  table_name: 'example_table7',
  key: {
    id: 1,
  },
  update_expression: 'SET Val = Val + :v',
  condition_expression: 'Val > :threshold',
  expression_attribute_values: {
    ':threshold' => 99,
    ':v' => 100,
  }
})

結果

Id Val
1 200

項目の削除

Id Val
1 10
2 20
3 30
client.delete_item({
  table_name: 'example_table8',
  key: {
    Id: 2,
  },
})

結果

Id Val
1 10
3 30

更新と同様条件を設定することも可能

client.delete_item({
  table_name: 'example_table8',
  key: {
    id: 1,
  },
  condition_expression: 'Val > :twenty',
  expression_attribute_values: {
    ':twenty' => 20
  }
})

条件(condition_expression)により削除できなかった場合はエラーが返るので注意

The conditional request failed (Aws::DynamoDB::Errors::ConditionalCheckFailedException)

戻り値

  • 更新や削除を実行する際、戻り値を指定することができる
  • return-values に文字列で指定する
キー 説明
ALL_OLD 更新前のすべての項目(カラム)
ALL_NEW 更新後のすべての項目
UPDATED_OLD 更新された項目のみ、以前の値
UPDATED_NEW 更新された項目のみ、更新後の値

ALL_OLD 以外は update_item でのみ指定可能

Id Val1 Val2 Val3
1 11 21 31
2 12 22 32
3 13 23 33
return_values = client.update_item({
  table_name: 'example_table9',
  key: {
    id: 1,
  },
  update_expression: 'SET Val1 = :zero',
  expression_attribute_values: {
    ':zero' => 0,
  },
  return_values: 'ALL_OLD'
})
puts return_values[:attributes]
# => {"Id"=>1, "Val1"=>0.11e2, "Val2"=>0.21e2, "Val3"=>0.31e2}

return_values = client.update_item({
  table_name: 'example_table9',
  key: {
    id: 2,
  },
  update_expression: 'SET Val1 = :zero, Val2 = :zero',
  expression_attribute_values: {
    ':zero' => 0,
  },
  return_values: 'UPDATED_NEW'
})
puts return_values[:attributes]
# => {"Val1"=>0.0, "Val2"=>0.0}

expression_attribute_names

  • update_expression 等にカラム名を記述する際、プレースホルダーを使用しなければならない場合がある。
    • 数字始まり
    • スペースを含む
    • 特殊文字を含む
    • 予約語である
  • プレースホルダーは # で始める必要がある
Id Col umn Value
1 "スペースを含むカラム名" "予約語のカラム名"
client.update_item({
  table_name: 'example_table3',
  key: {
    Id: 1,
  },
  update_expression: 'SET #Col1 = :v1, #Col2 = :v2',
  expression_attribute_names: {
    '#Col1' => 'Col umn',
    '#Col2' => 'Value',
  },
  expression_attribute_values: {
    ':v1' => 'aaa',
    ':v2' => 'bbb',
  }
})
2
0
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
2
0