DynamoDBのドキュメントAPI、JSON対応したから簡単なサンプルで紹介する part2


前回の振り返り
-

前回はDynamoDBのTable/Itemを使って、テーブル作成、データのPUT、データのGETまでやってみました。今回はQueryでデータを条件を絞って取得してみます。

データは前回バルクで入れたものから、こんなデータを使ってみます。まあその辺は適当にいれてみてください。


![今回使うデータの概観](https://qiita-image-store.s3.amazonaws.com/0/1234/41291391-ec87-b0d6-5289-06516ab55ad5.png "Screen Shot 2014-11-27 at 22.44.41.png")


ハッシュキーを指定してレンジでデータを取得する
-

まあみりゃわかります。ちょっとだけ注意としては、ItemCollectionがジェネリクスでQueryOutcomeを受けているのに、拡張forではItemを受けるのがなんだか最初はとまどいました。クラス名みればわかる話なのですが、ねえ・・・

```java
		ItemCollection<QueryOutcome> itemCollection = table
				.query(new QuerySpec().withHashKey("gameId", 325528));
		for (Item item : itemCollection) {
			String json = item.toJSONPretty();
			System.out.println(json + ",");
		}

```


アウトプットは下記のような感じです。

```json
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201401220227",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 3483,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game16",
    "gameStatus" : "FAILED",
    "ranking" : 4
  }
},
...
```

ハッシュキーとレンジキーの条件の組み合わせでデータを取得する
-

次はハッシュキー指定とレンジキーに条件を入れて検索してみます。

そのためには<b>RangeKeyConditionクラス</b>を使います。レンジキー条件を追加するには、gt(Greater Than)/ge(Greater Than or Equal)/lt(Less Than)/le(Less Than or Equal)/eq(Equal)/between/beginWithなどで条件を指定します。

レンジキーは時間を文字列にしてそのまま突っ込んでいるので、下記の例では2014年3月1日以上のデータを取得してみます。

```java
		ItemCollection<QueryOutcome> itemCollection = table
				.query(new QuerySpec().withHashKey("gameId", 325528)
						.withRangeKeyCondition(
								new RangeKeyCondition("time")
										.gt("201403010000")));
		for (Item item : itemCollection) {
			String json = item.toJSONPretty();
			System.out.println(json + ",");
		}
```

結果はこんな感じです。

```json
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201403101010",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 1103,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game26",
    "gameStatus" : "COMPLETED",
    "ranking" : 4
  }
},
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201404290502",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 6244,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game32",
    "gameStatus" : "FAILED",
    "ranking" : 2
  }
},
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201411080701",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 1958,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game29",
    "gameStatus" : "COMPLETED",
    "ranking" : 4
  }
},
```


また別の例としてレンジキーをbetween条件で絞って、2014年3-4月の2ヶ月のみにしてみます。


```java
		ItemCollection<QueryOutcome> itemCollection = table
				.query(new QuerySpec().withHashKey("gameId", 325528)
						.withRangeKeyCondition(
								new RangeKeyCondition("time")
								.between("201403010000", "201404300000")));
```

結果は言わずもがなな感じですがちゃんと絞れていますね・・・


```json
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201403101010",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 1103,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game26",
    "gameStatus" : "COMPLETED",
    "ranking" : 4
  }
},
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201404290502",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 6244,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game32",
    "gameStatus" : "FAILED",
    "ranking" : 2
  }
},
```

ハッシュキーとレンジキーの条件の組み合わせでデータを取得する
-

次はレンジキーではなく、<b>アトリビュートの方で条件を絞ってみます。scoreアトリビュートで2000点よりハイスコア出したレコードのみ出してみますか</b>。


```java
		ItemCollection<QueryOutcome> itemCollection = table
				.query(new QuerySpec().withHashKey("gameId", 325528)
						.withQueryFilters(new QueryFilter("score").gt(2000)));
		for (Item item : itemCollection) {
			System.out.println(item.toJSONPretty() + ",");
		}
```

まあなんだかコードより、結果の方が長いですが結果もはっときますね・・・
とはいえ、このままだとJSONの中の細かい条件が指定できないので、もう少し使い勝手がほしいですよね。というわけで、<b>そういう場合に次のfilter expressionを使います</b>。

```json
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201401220227",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 3483,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game16",
    "gameStatus" : "FAILED",
    "ranking" : 4
  }
},
{
  "teamFriendId" : [ "foo1", "foo2", "foo3" ],
  "time" : "201404290502",
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 6244,
  "additionalInfo" : {
    "aaa1" : "bbb1",
    "aaa2" : "bbb2"
  },
  "metadata" : {
    "gameName" : "game32",
    "gameStatus" : "FAILED",
    "ranking" : 2
  }
},
```


ハッシュキーとfilter expressionの組み合わせでデータを取得する
-


というわけで、最近このfilter expressionのところが使いやすくなったはずです（多分）。JSON形式のデータをより受け入れやすくなったのも手伝って、もう少しデータ構造の中をみるようなクエリーが書きやすくなっています。しかし、前述のQuery Filterと併用ができないので、どちらかを使う形になります。


コードは下記のような感じになります。順を追って説明すると、
<b>* ハッシュキーのところは今までと変わらず
* projection expressionで、取得すべきアトリビュートを指定します。下記の場合だと、gameId, playerId, scoreと、metadataというJSON構造の中にあるgameStatusを取り出す対象としています
* fileter expressionで、条件式を書いています。ここは汎用的に記載しており、playerIdとgameStatusはある特定の条件でscoreはある一定以上としています。
* filter expressionの具体的な値は、ValueMapで与えます。この場合だと、:playerId、:score、:statusに値を与えています</b>

SQLっぽく記述するなら、"SELECT gameId, playerId, score, metadata.gameStatus from Game where playerId = "hoge11" and score > 1000 and metadata.gameStatus = "COMPLETED""て所だと思います。


```java
		ItemCollection<QueryOutcome> itemCollection = table
				.query(new QuerySpec()
						.withHashKey("gameId", 325528)
						.withProjectionExpression(
								"gameId, playerId, score, metadata.gameStatus")
						.withFilterExpression(
								"playerId = :playerId AND score > :score AND metadata.gameStatus = :status")
						.withValueMap(
								new ValueMap()
										.withString(":playerId", "hoge11")
										.withInt(":score", 1000)
										.withString(":status", "COMPLETED")

						));
```

結果としては、こんな感じで帰ってきます。

```json
{
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 1197,
  "metadata" : {
    "gameStatus" : "COMPLETED"
  }
},
{
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 1103,
  "metadata" : {
    "gameStatus" : "COMPLETED"
  }
},
{
  "playerId" : "hoge11",
  "gameId" : 325528,
  "score" : 1958,
  "metadata" : {
    "gameStatus" : "COMPLETED"
  }
},
```

ちょっとまだ出来なそうなのが、このfilter expressionでIN/CONTAINSみたいなのが使えなそうなのと、<b>現時点だとJSON形式の構造のトップレベル以外をドリルダウンしての指定はできないっぽい</b>です。


まとめ
-

今回はQueryを中心に書いてみました。調べてみたらこの辺の記法とかのドキュメントが意外と少なかったので、復習もあわせて書いてみた感じですがいかがでしょうか。

次は、Conditional UpdateとScanあたりでも書いてみようかなと思います。


免責事項
こちらは個人の意見で、所属する企業や団体は関係ありません。