百名山 API を開発しました | mountix APICLICK !

AWS SDK for RustでDynamoDBのデータをQuery検索する

  • URLをコピーしました!

AWS SDK for Rust の Developer Preview が発表されました。

examplesにDynamoDB操作関連のソースコードが載っているので
いくつか触ってみたいと思います。

今回は Query による検索を試します。

AWS SDK for Rust を使ってみる

  1. query のサンプルを実行
  2. query + GSI のサンプルを実行
  3. query による検索結果のレスポンスを解析

Developer Preview のため、本番環境での使用は避けたほうが無難でしょう。

目次

Queryで絞り込み条件を指定してデータを取得する

検証環境

  • macOS:Big Sur 11.6
  • Rust:1.57.0
  • AWS SDK for Rust:v0.2.0

環境変数の設定

認証情報の取得は環境変数からおこなうため、以下の環境変数を設定します。

  • AWS_DEFAULT_REGION
  • AWS_SECRET_ACCESS_KEY
  • AWS_ACCESS_KEY_ID

詳しくは、READMEで確認してください。

テーブルの作成

DynamoDBに Users テーブルを作成し、スラムダンク 湘北高校のメンバーを登録します。

  • テーブル名:Users
  • パーティションキー:Id(Number)
  • ソートキー:なし
{
    "users": [
        {
            "Id": 1,
            "Name": "桜木花道",
            "Grade": 1,
            "Position": "PF",
            "Number": 10,
            "Height": 189,
            "Weight": 83
        },
        {
            "Id": 2,
            "Name": "流川楓",
            "Grade": 1,
            "Position": "SF",
            "Number": 11,
            "Height": 187,
            "Weight": 75
        },
        {
            "Id": 3,
            "Name": "赤木剛憲",
            "Grade": 3,
            "Position": "C",
            "Number": 4,
            "Height": 197,
            "Weight": 93
        },
        {
            "Id": 4,
            "Name": "宮城リョータ",
            "Grade": 2,
            "Position": "PG",
            "Number": 7,
            "Height": 168,
            "Weight": 59
        },
        {
            "Id": 5,
            "Name": "三井寿",
            "Grade": 3,
            "Position": "SG",
            "Number": 14,
            "Height": 184,
            "Weight": 70
        },
        {
            "Id": 6,
            "Name": "木暮公延",
            "Grade": 3,
            "Position": "SF",
            "Number": 5,
            "Height": 178,
            "Weight": 62
        }
    ]
}

Queryでデータを取得する

Cargo.toml

依存クレートを設定します。

[dependencies]
aws-config = "0.2.0"
aws-sdk-dynamodb = "0.2.0"
tokio = { version = "1", features = ["full"] }

main.rs

use aws_sdk_dynamodb::{Client};
use aws_sdk_dynamodb::model::{AttributeValue};

#[tokio::main]
async fn main() {
    let config = aws_config::load_from_env().await;
    let client = Client::new(&config);

    let resp = client
        .query()
        .table_name("Users")
        .key_condition_expression("#key = :value".to_string())
        .expression_attribute_names("#key".to_string(), "Id".to_string())
        .expression_attribute_values(":value".to_string(), AttributeValue::N("1".to_string()))
        .send()
        .await;
    match resp {
        Ok(query_output) => {
            if let Some(items) = query_output.items {
                for item in items {
                    println!("{:?}", item);

                    // Idを取り出す
                    if let Some(attr_val) = item.get("Id") {
                        if let Ok(id_val) = attr_val.as_n() {
                            if let Ok(id) = id_val.parse::<u32>() {
                                println!("{}", id);
                            }
                        }
                    }

                    // Nameを取り出す
                    if let Some(attr_val) = item.get("Name") {
                        if let Ok(name) = attr_val.as_s() {
                            println!("{}", name.to_string());
                        }
                    }
                }
            }
        }
        Err(e) => {
            println!("{}", e);
        }
    }
}

環境変数に設定した認証情報を取得するため、configを生成します。
その後、DynamoDBのクライアントを生成します。

.table_name()でテーブル名を指定し、.send()でリクエストを送信します。

以下を使用することで、Queryで絞り込むための条件を構成します。

  • .key_condition_expression()
  • .expression_attribute_names()
  • .expression_attribute_values()

上記ソースコードでは、Id = {N: 1}で検索しています。

非同期メソッドで実装されているため、.awaitが必要です。

Idの取得では.as_n()、Nameの取得では.as_s()を使用しています。

取得結果のId は&Stringであるため、
数値として扱う場合は.parse()等で変換する必要があるので注意です。

以下、実行結果です。

{"Grade": N("1"), "Id": N("1"), "Weight": N("83"), "Height": N("189"), "Name": S("桜木花道"), "Number": N("10"), "Position": S("PF")}
1
桜木花道

Id(プライマリーキー)を指定し検索するため、データが1件がとなります。
この場合、QueryではなくGetItemを使用する方が望ましいでしょう。

GSIを使用したQuery

GSIを作成する

グローバルセカンダリインデックスを作成します。

  • パーティションキー:Position(String)
  • ソートキー:Id(Number)
  • インデックス名:Position_Id_Index

main.rs

use aws_sdk_dynamodb::{Client};
use aws_sdk_dynamodb::model::{AttributeValue};

#[tokio::main]
async fn main() {
    let config = aws_config::load_from_env().await;
    let client = Client::new(&config);

    let resp = client
        .query()
        .table_name("Users")
        .index_name("Position_Id_Index")
        .key_condition_expression("#key = :value".to_string())
        .expression_attribute_names("#key".to_string(), "Position".to_string())
        .expression_attribute_values(":value".to_string(), AttributeValue::S("SF".to_string()))
        .send()
        .await;
    match resp {
        Ok(query_output) => {
            if let Some(items) = query_output.items {
                for item in items {
                    println!("{:?}", item);

                    // Idを取り出す
                    if let Some(attr_val) = item.get("Id") {
                        if let Ok(id_val) = attr_val.as_n() {
                            if let Ok(id) = id_val.parse::<u32>() {
                                println!("{}", id);
                            }
                        }
                    }

                    // Nameを取り出す
                    if let Some(attr_val) = item.get("Name") {
                        if let Ok(name) = attr_val.as_s() {
                            println!("{}", name.to_string());
                        }
                    }
                }
            }
        }
        Err(e) => {
            println!("{}", e);
        }
    }
}

テーブルに対してQueryを実行する場合との違いを説明します。

まず、.index_name()でインデックス名を指定します。

そして、.expression_attribute_names()でGSIのプライマリーキーであるPositionを指定するように修正します。

Positionは文字列(String)であるため、
.expression_attribute_values()ではAttributeValue::S()を使用します。

以下、実行結果です。

{"Id": N("2"), "Number": N("11"), "Position": S("SF"), "Grade": N("1"), "Weight": N("75"), "Name": S("流川楓"), "Height": N("187")}
流川楓
11
{"Position": S("SF"), "Number": N("5"), "Weight": N("62"), "Grade": N("3"), "Name": S("木暮公延"), "Id": N("6"), "Height": N("178")}
木暮公延
5

フィルターでデータを絞り込む

フィルターを使い、プライマリーキーやソートキー以外の絞り込み条件を追加することも可能です。

let resp = client
    .query()
    .table_name("Users")
    .index_name("Position_Id_Index")
    .key_condition_expression("#key = :value".to_string())
    .expression_attribute_names("#key".to_string(), "Position".to_string())
    .expression_attribute_values(":value".to_string(), AttributeValue::S("SF".to_string()))
    .filter_expression("#filterKey >= :filterValue".to_string())
    .expression_attribute_names("#filterKey".to_string(), "height".to_string())
    .expression_attribute_values(":filterValue".to_string(), AttributeValue::N("180".to_string()))
    .send()
    .await;

まとめ

Scan同様に、Queryも書くことができました。ありがとう、SDK。

著:初田直也, 著:山口聖弘, 著:吉川哲史, 著:豊田優貴, 著:松本健太郎, 著:原将己, 著:中村謙弘, 著:フォルシア株式会社
¥1,782 (2022/10/05 16:03時点 | Amazon調べ)

この記事が気に入ったら
フォローしてね!

  • URLをコピーしました!

コメント

コメントする

目次