DynamoDB PHP Sample Codes

AWS 工具組功能強大,而且功能還以火箭速度不斷開發,每年 AWS 的研發支出還力壓 Google。 不過要說 AWS 缺點的話也不是沒有…

一個是它是 Amazon WEB Service, 所以沒有網路的話就完全無用,這聽來是癈話,但是在 IoT 應用方面卻是一大問題,因為 IoT Device 的網路連線不可能是 100% 暢通的。所以 Greengrass 技術是十分重要的…. 我期待Greengrass 技術繼續發展下去。

另一個缺點是,因為功能出得太快太新了,線上文件也不是很好看,而且因為 AWS 同時支援數個語言,像是 DynamoDB 就有 Java 、JavaScript 、Node.js、.NET、PHP、Python、Ruby,還有iOS SDK、 Android SDK …等。
支援的程式語言很多是好事,但是也引發了一個問題:要嘛很難查到線上範例,要嘛勉強查到的範例卻不是自己使用的語言,雖然可以參考但是還是不是那麼方便。我自己就有無數次到 Stack Overflow 去查問題,結果要嘛只查到問題無人回答,要嘛竟然連問題都沒有的情況。

在這裡分享許多我用過的 PHP example codes,對於要使用 PHP 撰寫 DynamoDB 程式的開發者應該多少會有點幫助:

AWS helper function

function array_from_aws_result($awsArray)
{
	$data_table = array();
	foreach($awsArray as $k => $v){

		if(array_key_exists('S',$v)){
			$data_table[$k]=$v['S'];
		}
		if(array_key_exists('N',$v)){
			$data_table[$k]=floatval($v['N']);
		}
	}
	return $data_table;
}

Scan all records

do{
    $request = [
        'TableName' => 'IotecUser'
    ];

    if(isset($result) && isset($result['LastEvaluatedKey'])) {
        $request['ExclusiveStartKey'] = $result['LastEvaluatedKey'];
    }
    $result = $dynamoDbClient -> scan($request);

    foreach ($result['Items'] as $record) {
        $list_table = array_from_aws_result($record);
    }
}while(isset($result['LastEvaluatedKey']));
if(count($list_table) != 0){
	// process the list table
}else{
	// record not found
}

Scan table using query index. Single attribute index.

Partition Key: s_deviceId
Paramter: $deviceId

do{
    $request = [
        'TableName' => 'DeviceList',
        'IndexName' => 's_deviceId-index',                      
        'KeyConditionExpression' => 's_deviceId = :v_deviceId ',
        'ExpressionAttributeValues' =>
            [
                ':v_deviceId' => ['S' => $deviceId]
            ]
    ];

    if(isset($result) && isset($result['LastEvaluatedKey'])) {
        $request['ExclusiveStartKey'] = $result['LastEvaluatedKey'];
    }
    $result = $dynamoDbClient -> query($request);

    foreach ($result['Items'] as $record) {
        $list_table = array_from_aws_result($record);
    }
}while(isset($result['LastEvaluatedKey']));

if(count($list_table) != 0){
	// process the list table
}else{
	// record not found
}

Scan table using query index. Double attributes index.

Partition Key: n_certified

do{
    $request = [
        'TableName' => 'IotecPost',
        'IndexName' => 'n_certified-n_view_count-index',                      
        'KeyConditionExpression' => 'n_certified = :v_certified ',
        'ScanIndexForward' => false, // switch forward or backward
        'FilterExpression' => 's_country_code = :v_country_code',
        'ExpressionAttributeValues' =>
            [
                ':v_certified' => ['N' => '1'],
                ':v_country_code' => ['S' => 'TW']
            ]
    ];


    if(isset($result) && isset($result['LastEvaluatedKey'])) {
        $request['ExclusiveStartKey'] = $result['LastEvaluatedKey'];
    }
    $result = $dynamoDbClient -> query($request);

	$list_table[] = array();
    foreach ($result['Items'] as $record) {
        $item_table = array();
       
        foreach($record as $k=>$v){
            if(array_key_exists('S',$v)){
                $item_table[$k]=$v['S'];
            }
            if(array_key_exists('N',$v)){
                $item_table[$k]=floatval($v['N']);
            }
        }
        $list_table[] = $item_table;
    }
// if LastEvaluatedKey is set, means this is no the last record. Continue next query
}while(isset($result['LastEvaluatedKey'])); 

if(count($list_table) != 0){
	// process the list table
}else{
	// record not found
}

Get a dedicate record using getItem with Partition key.

Partition key: n_post_sn

$result = $dynamoDbClient->getItem(array(
    'ConsistentRead' => true,
    'TableName' => 'IotecPost',
    'Key'       => array(
        'n_post_sn'   => array('N' => '')
    )
));
$postTable = array_from_aws_result($result['Item']);
var_dump($postTable);

Get a dedicate record using getItem with Partition key and sort key.

Partition key: n_post_sn
Sort key: s_email

$result = $dynamoDbClient->getItem(array(
    'ConsistentRead' => true,
    'TableName' => 'IotecPost',
    'Key'       => array(
        's_email'   => array('S' => ''.$s_email),
        'n_post_sn' => array('N' => ''.$n_post_sn)
    )
));

Update record with updateItem

Partition key: n_post_sn
Sort key: s_email

Parameter: $userEmail,$postSn, $attributeName, $attributeType, $attributeValue

$dynamoDbClient->updateItem(array(
    'TableName' => 'IotecPost',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail),
       'n_post_sn'     => array('N' => ''.$postSn)
    ),
    "UpdateExpression" => "SET #key = :val",
    "ExpressionAttributeNames" => ['#key' => $attributeName],
    "ExpressionAttributeValues" => [':val' => [$attributeType => ''.$attributeValue]]
));

Delete record with deleteItem

Partition key: n_post_sn
Sort key: s_email

Parameter: $userEmail,$postSn

$dynamoDbClient->deleteItem(array(
    'TableName' => 'IotecPost',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail),
       'n_post_sn'     => array('N' => ''.$postSn)
    )
));

Remove attribute

Partition key: n_post_sn
Sort key: s_email

Parameter: $userEmail,$postSn

$result = $dynamoDbClient->updateItem(array(
    'TableName' => 'IotecPost',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail),
       'n_post_sn'     => array('N' => ''.$postSn)
    ),
    "UpdateExpression"    => "REMOVE s_address2"
));

DynamoDB PHP 最常見的坑

參數不接受空字串!參數不接受空字串!參數不接受空字串!

因為太重要了,要重覆三次

舉例如下:

Partition key: s_email
Parameter: $userName, $userEmail

$dynamoDbClient->updateItem(array(
    'TableName' => 'IotecUser',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail)
    ),
    "UpdateExpression" => "SET #key = :val",
    "ExpressionAttributeNames" => ['#key' => 's_name'],
    "ExpressionAttributeValues" => [':val' => ['S' => ''.$userName]]
));

上面的 code, 在 $userName = ” 的情況下,會發生錯誤

… One or more parameter values were invalid: An AttributeValue may not contain an empty string  …

只要看到上述這個錯誤訊息,八成又發生某個參數是空字串了