Amazon SimpleDBってなんじゃ?(その1 Scratchpad編)で、SimpleDBをscratchpadから使用してみましたが、実際には、ほとんどがプログラムからSDKを通じて使用するケースと思うので、今回はPHPのSDKを使用してアイテム(レコード)のCRUD(作成、読込、更新、削除)をしてみます。

○準備
今回はPHPのSDKを使用するので、AWS SDK for PHPからSDKをダウンロードしてロードしやすい場所に配置します。

$ mkdir src
$ cd src
$ wget http://pear.amazonwebservices.com/get/sdk-latest.zip
$ unzip sdk-latest.zip
$ cd sdk-1.3.4
$ mkdir /var/www/html/api
$ mv sdk-1.3.4 /var/www/html/api/
$ cd /var/www/html/api
$ ln -s sdk-1.3.4 sdk

SDKのドキュメンテーションを参考にいろいろなクエリを試して見ます。

○ドメイン(テーブル)の新規作成
パラメータは、Amazon SimpleDBってなんじゃ?(その1 Scratchpad編)で、ドメイン(テーブル)を作成した際と同様ドメイン名です。


  //ダウンロードしたSDKディレクトリのsdl.class.phpをロードします。
  require_once '../../sdk/sdk.class.php';
  //アクセス用のキーを定義します。
  $access_key = "xxxxxxxxxxxxxxxxxx";
  $secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  //キーをもとにSDKのSDBアクセサクラスを初期化します。
  $sdb = new AmazonSDB($access_key, $secret_key);
  //ドメイン作成します。
  $data = $sdb->createDomain("member");
  //戻り値から成否を判断し、成功なら戻り値のダンプを返します。
  if($data->isOK()){
    print_r($data);
  }
  else{
    print_r("失敗~");
  }
?>

成功した場合、戻り値のオブジェクトのisOK()がtrueで返ります。
戻り値のダンプは以下のとおりです。

CFResponse Object
(
    [header] => Array
        (
            [content-type] => text/xml
            [transfer-encoding] => chunked
            [content-encoding] => gzip
            [vary] => Accept-Encoding
            [date] => Mon, 20 Jun 2011 08:09:44 GMT
            [server] => Amazon SimpleDB
            [_info] => Array
                (
                    [url] => https://sdb.amazonaws.com/
                    [content_type] => text/xml
                    [http_code] => 200
                    [header_size] => 205
                    [request_size] => 691
                    [filetime] => -1
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 2.144966
                    [namelookup_time] => 0.208893
                    [connect_time] => 0.403431
                    [pretransfer_time] => 0.804195
                    [size_upload] => 0
                    [size_download] => 193
                    [speed_download] => 89
                    [speed_upload] => 0
                    [download_content_length] => 0
                    [upload_content_length] => 0
                    [starttransfer_time] => 2.144638
                    [redirect_time] => 0
                    [method] => POST
                )

            [x-aws-stringtosign] => 略
            [x-aws-request-headers] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded; charset=utf-8
                )

            [x-aws-body] =略
        )

    [body] => CFSimpleXML Object
        (
            [@attributes] => Array
                (
                    [ns] => http://sdb.amazonaws.com/doc/2009-04-15/
                )

            [ResponseMetadata] => CFSimpleXML Object
                (
                    [RequestId] => 673f1239-14be-ff54-9115-8ede692b7dc9
                    [BoxUsage] => 0.0055590278
                )

        )

    [status] => 200
)

上記のようにアクションの戻り値は、ヘッダ、ボディ、ステータス部に分かれて構成されたXMLのオブジェクトとして返され、実際のレスポンスデータはボディ部に含まれます。
このアクションは、ドメイン作成でデータの取得ではないため、返却値はなくメタデータのみが返されます。
また、戻り値のisOKメソッドでアクションの成否の判断ができます。

○アイテム(レコード)の追加
SimpleDBでは、アイテムの追加と更新は同じメソッドを利用し、アイテムの追加は、ドメイン名(テーブル名)とアイテム名(プライマリキー)と各属性(カラム名)とその値の連想配列をパラメータとします。
戻り値はドメインの作成と同様に更新アクションなので、返却値はなくメタデータのみになります。


  require_once '../../sdk/sdk.class.php';

  $access_key = "xxxxxxxxxxxxxxxxxx";
  $secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

  $sdb = new AmazonSDB($access_key, $secret_key);

  //1件ずつのインサート
  $data1 = $sdb->put_attributes('member',//ドメイン名
                                     'memorycraft',//アイテム名(プライマリキー)
                                     array(//各属性名と値の連想配列
                                       'country' => 'Japan',
                                       'location' => 'Tokyo',
                                     )
                                );

  /*
   * 複数件同時にインサート
   * ・ドメイン名
   * ・アイテム名(プライマリキー)をキーにした属性と値の連想配列
  */
  $data2 = $sdb->batchPutAttributes('shop', //ドメイン名
                                      array('David' => array('country' => 'USA',
                                                         'location' => 'Seattle'
                                                        ),
                                           'Satoru' => array('country' => 'Japan',
                                                        'location' => 'Saitama'
                                                        )
                                      )//アイテム名をキーにした属性と値の連想配列
                                   );

    if($data1->isOK() && $data2->isOK()){
      print_r($data1);
      print_r($data2);
    }
    else{
      print_r("失敗~");
    }

?>

値のセットは、1件の場合はput_attributeメソッドで、複数アイテム同時にセットする場合は、batchPutAttributesメソッドを使用します。

最後の引数としてreplace引数をtrue/falseで追加すると、指定したアイテム名が該当するアイテムが既に存在した場合の挙動をコントロールできます。
replace引数のデフォルトはfalseです。

//普通のインサート
$data1 = $sdb->put_attributes('member',
                                     'memorycraft',
                                     array(
                                       'country' => 'Japan',
                                       'location' => 'Tokyo',
                                     )
                                );
//値を変えて再投入
$data2 = $sdb->put_attributes('member',
                                     'memorycraft',
                                     array(
                                       'country' => 'Japan',
                                       'location' => 'Chiba',
                                     )
                                );

上記場合、2回目のput_attributeの時点では既にmemorycraftというアイテムは存在しているので、location属性はTokyoという値を上書きせずに、更にChibaという値も持つことになります。
SELECTするとlocation属性の値としてTokyo, Chibaの2つが返ることがわかります。

また、最初から複数値をセットすることもできます。

$data = $sdb->put_attributes('member',
                                     'memorycraft',
                                     array(
                                       'country' => 'Japan',
                                       'location' => array('Tokyo', 'Chiba'),
                                     )
                                );

このようにデフォルトの状態では、既存アイテムに対して複数値のセットになってしまうことを避けたい場合、
put_attributeの第4引数であるreplace引数をtrueにセットします。

//普通のインサート
$data1 = $sdb->put_attributes('member',
                                     'memorycraft',
                                     array(
                                       'country' => 'Japan',
                                       'location' => 'Tokyo',
                                     )
                                );
//値を変えて再投入
$data2 = $sdb->put_attributes('member',
                                     'memorycraft',
                                     array(
                                       'country' => 'Japan',
                                       'location' => 'Chiba',
                                     ),
                                     true//replace引数をtrueに
                                );

この場合、locationは複数値にならず、TokyoがChibaに変更されます。

○アイテム(レコード)の検索
アイテムの検索は、RDBでいうところのSELECTにあたります。
戻り値には検索結果としての返却値が含まれています。

>?php
  require_once '../../sdk/sdk.class.php';

  $access_key = "xxxxxxxxxxxxxxxxxx";
  $secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";

  $sdb = new AmazonSDB($access_key, $secret_key);
  $sql = "select * from member where country = 'Japan'";//country属性がJapanのものを検索
  $data = $sdb->select($sql, array("ConsistentRead" => "true"));

    if($data->isOK()){
      print_r($data);
    }
    else{
      print_r("失敗~");
    }

?

この場合、戻り値$dataのダンプは以下のようになります。

CFResponse Object
(
    [header] => Array
        (
            [content-type] => text/xml
            [transfer-encoding] => chunked
            [content-encoding] => gzip
            [vary] => Accept-Encoding
            [date] => Mon, 20 Jun 2011 11:19:32 GMT
            [server] => Amazon SimpleDB
            [_info] => Array
                (
                    [url] => https://sdb.amazonaws.com/
                    [content_type] => text/xml
                    [http_code] => 200
                    [header_size] => 205
                    [request_size] => 753
                    [filetime] => -1
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.808396
                    [namelookup_time] => 0.002231
                    [connect_time] => 0.195101
                    [pretransfer_time] => 0.595389
                    [size_upload] => 0
                    [size_download] => 285
                    [speed_download] => 352
                    [speed_upload] => 0
                    [download_content_length] => 0
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.808132
                    [redirect_time] => 0
                    [method] => POST
                )

            [x-aws-stringtosign] => 略
            [x-aws-request-headers] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded; charset=utf-8
                )

            [x-aws-body] => 略
        )

    [body] => CFSimpleXML Object
        (
            [@attributes] => Array
                (
                    [ns] => http://sdb.amazonaws.com/doc/2009-04-15/
                )

            [SelectResult] => CFSimpleXML Object
                (
                    [Item] => Array
                        (
                            [0] => CFSimpleXML Object
                                (
                                    [Name] => memorycraft
                                    [Attribute] => Array
                                        (
                                            [0] => CFSimpleXML Object
                                                (
                                                    [Name] => location
                                                    [Value] => Tokyo
                                                )

                                            [1] => CFSimpleXML Object
                                                (
                                                    [Name] => country
                                                    [Value] => Japan
                                                )

                                        )

                                )

                            [1] => CFSimpleXML Object
                                (
                                    [Name] => Satoru
                                    [Attribute] => Array
                                        (
                                            [0] => CFSimpleXML Object
                                                (
                                                    [Name] => location
                                                    [Value] => Saitama
                                                )

                                            [1] => CFSimpleXML Object
                                                (
                                                    [Name] => country
                                                    [Value] => Japan
                                                )

                                        )

                                )

                        )

                )

            [ResponseMetadata] => CFSimpleXML Object
                (
                    [RequestId] => d2c6f796-5d0d-cc2f-3b63-e461e108d38e
                    [BoxUsage] => 0.0000320033
                )

        )

    [status] => 200
)

上記のように、body要素のSearchResultの中に検索結果が出力されます。
この場合は、アイテム(レコード)が2件取得されたので、Item要素はCFSimpleXML Objectの配列になりますが、1件の場合は配列ではなくCFSimpleXML Objectになります。

これらはXMLオブジェクトなどを含んでいるため、結果セットのパースや操作が少し面倒です。
そのため、amazonwebservices / aws-sdk-for-phpのサンプルのようにレスポンスを連想配列セットに再構築する汎用メソッドやライブラリなどを用意しておくと便利です。

$data = $sdb->select($sql, array("ConsistentRead" => "true"));//クエリ実行
$data = reorganize_data($data->body->Item());//戻り値を再構築
print_r($data["rows"][0]["country"]);//1番目のアイテムの国を出力

また、SimpleDBはデフォルトでは非アトミックなため、SELECTなどでその前の更新内容の伝播が遅れていても
読み込まれる場合があります。
それを防ぐためには、下記のサンプルのように第2引数のオプション配列で、ConsistentReadを有効にするように指定します。
ポイントはTRUEを文字列として渡すことです。ブール値で渡すとエラーが発生します。

$data = $sdb->select($sql, array("ConsistentRead" => true));//エラーが発生
$data = $sdb->select($sql, array("ConsistentRead" => "true"));//正しい指定のしかた

アイテム名(プライマリキー)を直接参照、指定する際はitemName()という関数を使用します。

$sql = "select * from member where itemName() = 'memorycraft'";

そのほかSELECTで使用できるクエリ構文に関しては、SimpleDBのデベロッパーガイドが参考になります。

こちらの記事はなかの人(memorycraft)監修のもと掲載しています。
元記事は、こちら