AlibabaCloudのtableStoreを試してみた

# はじめに
AlibabaCloudを触れるようになったので、とりあえずNoSQLでデータを入れたり削除したりしてみる。
これができればIoTっぽいことができる...はず多分。
今回はpythonですが、nodeやJava、GoなんかもSDKがあるので同じことはできるっぽい。

# SDKのインストール
普通にpipでインストールできるっぽい。
今回必要なのはtablestore（テーブルいじったりするやつ）とots2（AlibabaCloudにアカウントを紐づけるやつ）

```python:コンソール
pip install ots2
pip install tablestore
```

# RAMアクセスキーの取得
アリクラウドのコンソールを開いて、RAMを選択し、ユーザーのAccessKeyから「AccessKeyを取得」を押す。
CSVをダウンロードでき、アクセスキー、シークレットキーが記載されているので無くさず、流出しないようにする。
これが流出しちゃうと悪意のあるユーザーに色々悪いことされるので絶対に流出させないようにましょう。

ぶっちゃけAWSのIAMと全く同じです


# Table Storeの作成
どうもインスタンスを立てて、そこにNoSQLのテーブルとかを設定する形っぽい。
まずはデフォルトリージョンが中国になっているので、左上の国旗からリージョンを日本に変更する。
そのあと、Table Storeからインスタンスを作成を押して適当にインスタンス立ててみる。
<img width="962" alt="スクリーンショット 2018-09-28 15.32.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/148269/9552cd2f-bad9-6208-442f-808287cbef65.png">

インスタンスを選んで、テーブルを作成してみる。
プライマリーキーは最大で４つまで設定できるっぽい。今回はテストだし１個だけ設定してみる。
<img width="963" alt="スクリーンショット 2018-09-28 15.37.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/148269/0a5807a9-2c64-0ca2-e2ba-1dc8ab564cf7.png">

テーブルを作るとこんな感じになる。
このとき、左上にあるインターネット：の部分のURLをメモっておきましょう。
画像だと「 https://testins.ap-northeast-1.ots.aliyuncs.com 」の部分です。
<img width="960" alt="スクリーンショット 2018-09-28 15.47.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/148269/1822a7e9-569a-2045-56d8-9b580fc7022b.png">

# pythonからテーブルを色々やってみる
## テーブル一覧を取得する
とりあえず一番簡単そうなテーブルの取得から
以後のpythonと共通で設定値は以下の通りに
ENDPOINT：table storeに記載されている、インターネットの値
ACCESSID：CSVに記載されているアクセスキー
ACCESSKEY：CSVに記載されているシークレットキー
INSTANCENAME：作成したインスタンスの名前

```python:tableList
# -- coding: utf8 --
from ots2 import *

ENDPOINT = "https://testins.ap-northeast-1.ots.aliyuncs.com"
ACCESSID = ""
ACCESSKEY = ""
INSTANCENAME = "testins"

ots_client = OTSClient(ENDPOINT, ACCESSID, ACCESSKEY, INSTANCENAME)
list_response = ots_client.list_table()
for table_name in list_response:
    print table_name
```

OTSClientでインスタンスと紐付け、そこから各種コマンドで色々やるってやり方みたい。
出力がこちら。

```python:コンソール
python tablesotre.py 
poke
```

きちんと作成したテーブルが出てこればオッケー。

## データを挿入してみる
やり方はリスト取得と基本的には同じ。

```python:put_row
# -- coding: utf8 --
from ots2 import *
from tablestore import *

ENDPOINT = "https://testins.ap-northeast-1.ots.aliyuncs.com"
ACCESSID = ""
ACCESSKEY = ""
INSTANCENAME = "testins"

primary_key = [('name','Latios')]
attribute_columns = [('cp',3644), ('no',381)]
row = Row(primary_key, attribute_columns)

condition = Condition(RowExistenceExpectation.EXPECT_NOT_EXIST)
ots_client = OTSClient(ENDPOINT, ACCESSID, ACCESSKEY, INSTANCENAME)
list_response = ots_client.put_row("poke", row, condition)
```

Conditionでデータの上書きとかを指定できる。
RowExistenceExpectation.IGNORE：プライマリキーが同じだろうが新規だろうが問答無用でPUTする
RowExistenceExpectation.EXPECT_EXIST：プライマリキーがすでに存在してる場合のみPUTで上書きする
RowExistenceExpectation.EXPECT_NOT_EXIST：指定行が存在しない場合のみ挿入
この辺りはAWSより融通が利きそう。
ちなみに、Rowはtablestoreをimportすれば使えるAPIでした。それがわからずに苦労しました...

![スクリーンショット 2018-10-01 16.29.11.png](https://qiita-image-store.s3.amazonaws.com/0/148269/4e27727c-2167-f3ce-8808-11221fc7096d.png)
コンソールから確認、ちゃんとデータ入ってますね。

## データを取得してみる
こちらも基本的にはやり方は同じ。
scanは存在しないみたいなので、全範囲を指定して取得するようにしてみる。

```python:get_range
# -- coding: utf8 --
from ots2 import *
from tablestore import *

ENDPOINT = "https://testins.ap-northeast-1.ots.aliyuncs.com"
ACCESSID = ""
ACCESSKEY = ""
INSTANCENAME = "testins"

start_primary_key = [('name',INF_MIN)]
end_primary_key = [('name',INF_MAX)]
columns_to_get = []
limit = 10

ots_client = OTSClient(ENDPOINT, ACCESSID, ACCESSKEY, INSTANCENAME)

consumed, next_start_primary_key, row_list, next_token  = ots_client.get_range(
                "poke",
                Direction.FORWARD, 
                start_primary_key,
                end_primary_key,
                columns_to_get, 
                limit)

for row in row_list:
    print row.primary_key, row.attribute_columns
```

get_rangeの帰り値は４つほどあるみたいだが、今回必要なのはrow_listのみ。
row_listの中に取得したデータがあるのでprintなりで表示してあげればOK

```python:コンソール
python get_range.py 
[(u'name', u'Latios')] [(u'cp', 3644, 1538374251039), (u'no', 381, 1538374251039)]
[(u'name', u'Metagross')] [(u'cp', 3644, 1538374586327), (u'no', 376, 1538374586327)]
[(u'name', u'Tranitar')] [(u'cp', 3670, 1538374608754), (u'no', 248, 1538374608754)]
```

無事出力されている...のはいいんだが、1538374251039とかの数字って何だろう？
１行毎に設定されているのかな？
ただとりあえず、データの取得が問題なくいけてますね。

# やってみたよかった点


## 複数プライマリーキーが設定できる
dynamoDBで非常に大変だった部分なのでこれはありがたい。
４つもあれば、データ保持とかもだいぶ楽になるんじゃないかな


# やってみた不満点
## ACCESSID、ACCESSKEYをコードに記載しないといけない。
なにこのセキュリティ事故発生しまくりそうな仕様...。
一応CLIでアカウント設定はできるんだけど、pythonからその情報を取る手段を調べてる限りなさげ。
これはなんとかどうにかして欲しいなぁ。

## 検索が不便
get_rangeで取得して見ましたが、なんかまだ色々私自身理解できてないせいなのか、
なんか複数リターンがあったり、スキャンやお手軽クエリができなかったりなんか微妙。
１行だけ取得とかそういうのはあったんですが、この辺りは勉強不足の可能性もある？

## ググっても出てこない
みんなもっと使ってあげて。
英語はまぁいいとしても中国語とか出てきてもさっぱりわかんないよ...

# まとめ
NoSQLというだけでdynamoDBとか異なる何かっぽい奴でしたが、なんとかうまくいけました。
インスタンス立てたりしないといけないのはめんどくさいですが、プライマリキーを４つまで
設定できるのは非常に魅力的。
ただコードにアクセスキー埋め込まないとダメなのはいけてない。絶対事故るよね。

# 今後やりたいこと
本当はDBに直接書き込みじゃなくてAWSでいうKinesisみたいなやつを挟みたいなぁ
IoTPlatformからmqttで送信とかはできそうなのでこっちでやってみるのも手かな