DynamoDBのscan()で「Falseの場合のみ取得」をしたらデータHitしなかった話

DynamoDBテーブルのデータを検索するとき、scan()などでFilterExpressionを使います。 しかし、DynamoDBテーブルのデータ内容によっては、条件一致しないことがあるので注意が必要です。
2020.09.04

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

DynamoDBテーブルのデータをscan()query()で検索するとき、FilterExpressionを使って指定した条件を満たしたデータのみ取得することがよくあります。 しかし、DynamoDBテーブルのデータ内容によっては条件一致しないことがありました。

DynamoDBのscan()で「Falseの場合」にデータHitしなかった

DynamoDBテーブルの様子

DynamoDBテーブルにToDoリストのデータがあるとします。

  • donetrue: 完了
  • doneが無い: 未完了

DynamoDBテーブルの様子(doneが無い項目あり)

未完了のデータを取得すると、0件になる

次のようにDynamoDBテーブルから「未完了のデータ一覧」を取得します。 このとき、「doneがFalseならHit」させようとしていますが、取得したデータは0件になったのです。

app.py

import boto3
from boto3.dynamodb.conditions import Attr, Key

dynamodb = boto3.resource('dynamodb')

def main():
    table = dynamodb.Table('todo-table')
    
    option = {
        'FilterExpression': Attr('done').eq(False)
    }
    resp = table.scan(**option)

    for item in resp.get('Items'):
        print(item)

if __name__ == "__main__":
    main()

doneがFalseなら」がHitしない理由

done項目の有無が原因でした。下記のFilterExpressionは、「doneの項目がfalseなら」となりますが、そもそもdone自体が存在しないので、この条件は偽になるのです。……そりゃそうだ。

    option = {
        'FilterExpression': Attr('done').eq(False)
    }

未完了のデータを取得する方法

2つの方法があります。

  • doneにfalseを設定する
  • FilterExpressionの条件を「doneがTrue以外なら」にする

その1. doneにfalseを設定する

DynamoDBテーブルのデータについて、あらかじめdonefalseを入れておきます。

DynamoDBテーブルの様子(doneはすべてある)

このようにすれば、「doneの項目がfalseなら」でHitします。

    option = {
        'FilterExpression': Attr('done').eq(False)
    }

その2. FilterExpressionの条件を「doneがTrue以外なら」にする

DynamoDBテーブルのデータを変更せず検索したい場合は、FilterExpressionの条件を「doneの項目がtrue以外なら」に変更すればHitします。trueではないですからね。そりゃそうだ。

    option = {
        'FilterExpression': Attr('done').ne(True)
    }

さいごに

地味なポイントですが、1度でもハマれば覚えると思います。どなたかの参考になれば幸いです。