今回はTable Storageを使っていきます。
Table Storageは、NoSQLと呼ばれるデータベースの一種で、MySQLのようなRDBと違い、柔軟なデータを入れることができます。RDBなら列名がきちんと定義されていてそれ以外のものを入れることはできませんが、NoSQLは行ごとに列が違うデータも入れることができます。
その代わり、JOINなどRDBで使えていた操作ができなくなり、「Key-Value方式」と呼ばれる、キーを使ってその値を読み書きする、という使い方になります。
RDBで言うと、単一のテーブルで、主キーのみを使ってInsertやUpdateをする、というイメージでしょうか。
前回、Blob Storageを使った時と同じ手順で、UnityPackageをインポートして、サンプルを実行します。
接続文字列もBlobの時と同様になります。
今回のサンプルは、下記のものを作りました。
Gameビューをクリックする
・クリックした先に何もなければ、Cubeを作成して、作成された座標をTableに記録
・クリックした先に既にCubeがあれば、色を赤に変えて、記録されているTableを更新する
キーボードのQを押すと、既にTableに記録されている情報を元にCubeを作成する
前回と同じく、Azure Storage Explorerでデータを見ながら開発を行います。
シンプルにしたつもりがだいぶ長くなってしまいました。
シーンに置いた空のGameObjectにアタッチして、インスペクタでcubePrefabに何か適当なPrefabをセットしてください。
ConntectionStringも置き換えてください。
CreateTableAsync内の下記で行っています。
CloudTable table = TableClient.GetTableReference(tableName); await table.CreateIfNotExistsAsync();
これで、テーブルが存在しなければ作成、存在すれば何もしない、という処理になります。
RDBと違って、テーブル作成時に列は指定しません。
ただ、自動的に作成されるシステム用の列が存在します。
・PartitionKey
・RowKey
・Timestamp
の3つです。
PartitionKeyは、テーブルを物理的に格納する場所を指定します。PartitionKeyが同じなら同じ場所に格納されるので、検索などが高速になりますが、書き込みが集中するとパフォーマンスが低下するというデメリットもあります。今回のサンプルでは「CubeInfo」で固定にしています。
PartitionKeyとRowKeyで複合主キーになりますが、今回はPartitionKeyを固定にしたので、RowKeyをユニークに採番する必要があります。
今回は下記を参考にして、ユニークな値が採番されるようにしました。
public class CubeInfo : TableEntity { public double x { get; set; } public double y { get; set; } public double z { get; set; } public string ColorName { get; set; } ...
データの取り出しや書き込みは、全てクラスを通じて行います。
CubeInfoというクラスを、TableEntityを継承して作成しています。
TableEntity側に、必須項目のPartitionKeyやRowKeyが存在するので、それ以外の項目を追加しています。
x,y,zはVector3の要素を格納するつもりなので、Unity側ではfloatなのですが、Table Storageにはfloat型は存在しないので、double型を使っています。
何も無い場所をクリックされた時に、新規データを作成しています。
CubeInfo ci = new CubeInfo() { x = System.Convert.ToDouble(pos.x), y = System.Convert.ToDouble(pos.y), z = System.Convert.ToDouble(pos.z) };
コンストラクタでPartitionKeyとRowKeyの値が作成されるので、x,y,zの値を設定していますが、floatからdoubleに型変換しています。
InsertOrMergeEntityAsyncメソッドの下記で、「InsertOrMerge」操作を指定しています。
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
TableOperationには、
・Retrieve
・Insert
・Replace
・InsertOrReplace
・Merge
・InsertOrMerge
・Delete
などが存在します。
指定された行が存在しなければ作成、存在したら更新、が便利なので、InsertOrMergeを使用しています。
MergeとReplaceの違いですが、前述のように行はそれぞれ列を持つことができるので、既存の列を保持したまま操作するのがMerge、既存の列は無視して指定した列のみの情報にするのがReplaceになります(たぶん。。)
既に存在するCubeをクリックした場合、Cubeの名前をRowKeyとしてTableのデータを検索して書き換えています。
RetrieveEntityUsingPointQueryAsyncの中で、
TableOperation retrieveOperation = TableOperation.Retrieve<CubeInfo>(partitionKey, rowKey);
Retrieveは、主キーを指定して単一の行を取得する、という操作になります。
取得したデータのColorNameプロパティのみ書き換えて、InsertOrMergeEntityAsyncに送り込むことにより、既存のデータの更新が行われます。
Qキーを押すと既存のデータを取得して、それを元にCubeを生成しています。
データを取るのはPartitionScanAsyncメソッドですが、検索条件は下記のように書いています
TableQuery<CubeInfo> partitionScanQuery = new TableQuery<CubeInfo>().Where (TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
今回はPartitionKeyしか検索条件に指定しておらず、PartitioKeyは同一にしているので、全てのデータを取得する、という意味になっています。
もし他にも検索条件を追加したい場合には、Whereの後に追加していきます。SDK付属のサンプルスクリプトを参考にすると、複数条件をつける際には、TableOperators.AndやTableQuery.CombineFiltersを組み合わせていくようです。
結果となるCubeInfoのx,y,zはdouble型なので、Vector3用のfloatに変換するには、Convert.ToSingleを使用します。