ScalaからDynamoDBを扱うサンプル

ScalaからDynamoDB Java SDKを扱う際のサンプルです。足りないAPIは必要に応じて増やしていきます。
各APIを扱う書き方は複数あるので特定のパターンのみ記述しています。

フルのソースコードはこちら

https://github.com/Peranikov/dynamodb-scala-sample/blob/master/src/main/scala/LowLevelApiSample.scala

# はじめに

`aws-java-sdk-dynamodb`を`libraryDependencies`に含めておきます。

```scala:build.sbt
libraryDependencies += "com.amazonaws" % "aws-java-sdk-dynamodb" % "1.11.29"
```

dynamodb-localは既に立ち上がってる前提で進めます。
AWS-CLIでサンプルとして使うDynamoDBのテーブルも先に作っておきましょう。

```bash
#!/usr/bin/env bash

export AWS_ACCESS_KEY_ID="dummy"
export AWS_SECRET_ACCESS_KEY="dummy"

aws dynamodb --endpoint-url "http://localhost:8000" create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
```

AmazonDynamoDBClientとmodelをimportしておきます。
~~`collection.JavaConversions._`もimportしておくとJavaとScala間でコレクションを自動的に変換してくれるので便利です。~~ [参考](http://docs.scala-lang.org/ja/overviews/collections/conversions-between-java-and-scala-collections)

`collection.JavaConversions._`は非推奨になる可能性があるので、今後はJavaConvertersを使うほうが良いそうです。 [参考](http://d.hatena.ne.jp/xuwei/20160327/1459108797)

```scala
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient
import com.amazonaws.services.dynamodbv2.model._

// import collection.JavaConversions._
import collection.JavaConverters._
```

# クライアント生成

```scala
val client: AmazonDynamoDBClient = (new AmazonDynamoDBClient())
    .withEndpoint("http://localhost:8000")
```

# テーブル一覧


```scala
println(client.listTables)
```

# テーブル情報

```scala
println(client.describeTable("Music"))
```

# Batch Write

テーブルに複数のアイテムを一度に書き込みます。

```scala
  val batchWriteItems: java.util.List[WriteRequest] = Seq(
    new WriteRequest(new PutRequest(
      Map(
        "Artist"    -> new AttributeValue("Rainbow"),
        "SongTitle" -> new AttributeValue("Kill the King"),
        "Member"    -> new AttributeValue(Seq(
          "Ronnie James Dio",
          "Ritchie Blackmore"
        ).asJava)
      ).asJava
    )),
    new WriteRequest(new PutRequest(
      Map(
        "Artist"    -> new AttributeValue("Rainbow"),
        "SongTitle" -> new AttributeValue("All Night Long"),
        "Member"    -> new AttributeValue(Seq(
          "Graham Bonnet",
          "Ritchie Blackmore"
        ).asJava)
      ).asJava
    )),
    new WriteRequest(new PutRequest(
      Map(
        "Artist"    -> new AttributeValue("Metallica"),
        "SongTitle" -> new AttributeValue("Enter Sandman"),
        "Member"    -> new AttributeValue(Seq(
          "James Hetfield",
          "Kirk Hammett"
        ).asJava)
      ).asJava
    )),
    new WriteRequest(new PutRequest(
      Map(
        "Artist"    -> new AttributeValue("Deep Purple"),
        "SongTitle" -> new AttributeValue("Highway Star"),
        "Member"    -> new AttributeValue(Seq(
          "Ian Gillan",
          "Ritchie Blackmore"
        ).asJava)
      ).asJava
    ))
  ).asJava

  println(
    client.batchWriteItem(
      (new BatchWriteItemRequest())
        .withRequestItems(Map("Music" -> batchWriteItems).asJava)
    )
  )
```

# ハッシュキーでクエリ

ハッシュキーを指定してクエリします(prettyPrintメソッドはMapを拡張して追加しています。詳しくはコード)

```scala
val hashCondition = new Condition()
    .withComparisonOperator(ComparisonOperator.EQ)
    .withAttributeValueList(new AttributeValue("Rainbow")
  )

  println(
    client.query(
      (new QueryRequest("Music"))
        .withKeyConditions(Map("Artist" -> hashCondition).asJava)
    ).getItems.asScala.map(_.asScala.toMap.prettyPrint)
  )
```

# クエリ + フィルター

フィルター条件を加えてクエリします

```scala
val filterCondition = new Condition()
    .withComparisonOperator(ComparisonOperator.EQ)
    .withAttributeValueList(new AttributeValue("Ronnie James Dio")
  )

  println(
    client.query(
      (new QueryRequest("Music"))
        .withKeyConditions(Map("Artist" -> hashCondition).asJava)
        .withQueryFilter(Map("Vocal" -> filterCondition).asJava)
    ).getItems.asScala.map(_.asScala.toMap.prettyPrint)
  )
```

# クエリ + リミット、 さらに続きをクエリ

リミットを掛けてクエリします

```scala
val limitQueryResult = client.query(
    (new QueryRequest("Music"))
      .withKeyConditions(Map("Artist" -> hashCondition).asJava)
      .withLimit(1)
  )
  println(
    limitQueryResult.getItems.asScala.map(_.asScala.toMap.prettyPrint)
  )
```

続きがある場合、LastEvaluatedKeyが取得できるのでExclusiveStartKeyに指定して続きから取得します。

```scala
  println(
    client.query(
      (new QueryRequest("Music"))
        .withKeyConditions(Map("Artist" -> hashCondition).asJava)
        .withExclusiveStartKey(limitQueryResult.getLastEvaluatedKey)
    ).getItems.asScala.map(_.asScala.toMap.prettyPrint)
  )
```

# スキャン

全てのアイテムを取得します

```scala
println(
    client.scan(
      new ScanRequest("Music")
    ).getItems.asScala.map(_.asScala.toMap.prettyPrint)
  )
```