DynamoDBで特定項目の有無を調べる

DynamoDBで特定項目の有無を調べてみました。
2021.07.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

DynamoDBを使っていると、「特定の項目がxxxのデータがある? ない?」と調べたくなる場合があります。 たくさんある全データをscan()して判断するのは大変なので、今回は、GSIを使う方法を試してみました。

例えば、写真のメタデータを管理するDynamoDBテーブル

写真のメタデータを管理するDynamoDBテーブルを例とします。

photoId s3Bucket s3Key place
p0001 my-photo-bucket foo/bar/aaa.jpg Tokyo
p0002 my-photo-bucket foo/bar/bbb.jpg Iwate
p0003 my-photo-bucket foo/bar/ccc.jpg Osaka
p0004 my-photo-bucket foo/bar/ddd.jpg Tokyo
p0005 my-photo-bucket foo/bar/eee.jpg Fukuoka

このとき、下記を調べたい例です。

  • placeTokyoのデータはある?ない?
  • placeAmericaのデータはある?ない?

DynamoDBテーブルを準備する

CloudFormationテンプレート

placeでGSIを作成しています。有無を知りたいだけなので、GSIはKEYS_ONLYを使います。

dynamodb.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: DynamoDB Sample

Resources:
  DemoTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: photo-meta-table
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: photoId
          AttributeType: S
        - AttributeName: place
          AttributeType: S
      KeySchema:
        - AttributeName: photoId
          KeyType: HASH
      GlobalSecondaryIndexes:
        - IndexName: place-index
          KeySchema:
            - AttributeName: place
              KeyType: HASH
          Projection:
            ProjectionType: KEYS_ONLY

デプロイ

aws cloudformation deploy \
    --template-file dynamodb.yaml \
    --stack-name DynamoDB-Photo-Meta-Sample-Stack \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

DynamoDBの特定項目の有無を調べる

サンプルコード

下記のPythonコードのように調べることができます。

app.py

import boto3
from boto3.dynamodb.conditions import Key

def main():
    dybamodb = boto3.resource('dynamodb')

    res = is_exist_place('Tokyo', dybamodb)

    print(res)


def is_exist_place(place: str, dynamodb) -> bool:
    table = dynamodb.Table('photo-meta-table')
    options = {
        'IndexName': 'place-index',
        'Select': 'COUNT',
        'KeyConditionExpression': Key('place').eq(place),
        'Limit': 1,
    }
    res = table.query(**options)
    if res.get('Count', 0) != 0:
        return True
    return False


if __name__ == '__main__':
    main()

今回、調べたい項目(place)をGSIのパーティションキーに設定しています。たとえば、GSIのパーティションキーにTokyoを指定してquery()を実行すれば、place==Tokyoのデータのみを取得できます。そのため、「ある? ない?」を判断するためには、Limit=1を指定すれば十分です。全検索をする必要が無いので、とてもシンプルです。

Tokyoが2つあるとき

Tokyoが2つあるときに調べると、Trueが得られます。

DynamoDBテーブルの様子

app.py

$ python app.py
True

Tokyoが1つあるとき

Tokyoが1つあるときに調べると、Trueが得られます。

DynamoDBテーブルの様子

app.py

$ python app.py
True

Tokyoが無いとき

Tokyoがないときに調べると、Falseが得られます。

DynamoDBテーブルの様子

app.py

$ python app.py
False

Tokyoがあって、placeが無い項目もある

Trueが得られます。

DynamoDBテーブルの様子

app.py

$ python app.py
True

Tokyoが無くて、placeが無い項目もある

Falseが得られます。

DynamoDBテーブルの様子

app.py

$ python app.py
False

さいごに

「1つでもxxxがあれば、AAAの処理をする」みたいな処理を作る必要があったので試してみました。 簡単にできて良かったです。

参考