2021-09-17

DynamoDBのwebでのページネーションが苦行だったメモ

blogimg

何がやりたかったか

こういうやつ↓

リストがあって、それを特定個で区切ってページごとに表示するやつです。ページネーションとかナビゲーションとか言ったりするやつです。

結論から言うと、よくある3ページ目、5ページ目などの特定のページへ飛べるページネーションはおそらく実装不可能です。上記画像のように次のインデックス前のインデックスなどでしかできません。

こういうやつ↓

前提

  • Lambdaでの、AWS SDK for Goを利用
  • フロントはReactを利用

※dynamoDBの基本的な動作の説明はございません。

※とりあえずロジック考えて動けば良いかなで実装したので絶対もっと良い方法があるはずな気はしています

何が大変だったのか

結論から言うと、DynamoDBがこういう検索の仕方を想定していないせいです。(多分)

DynamoDBには一応ページネーション的な機能はあるのですが、

テーブルクエリ結果をページ分割する

これはあくまでDynamoDBが一度に取得できる1MBサイズの縛りを超えて全ての件数を**1回のコールで**取得したいときの実例です。(この縛りもDynamoDBの罠な気がする)

利用方法としては、取得したい個数を`Limit`で指定できるので、これを指定し、`LastEvaluatedKey`に取得したいアイテムの1つ前のアイテムを指定するという方法です。

さらにフロントからは、

  • 全件取得で件数を取得
  • 6で割ってページネーションが全体で何ページになるか計算
  • 現在のページをstateで管理
  • 全件取得したアイテムを6こずつ区切った配列を用意
  • 現在のページから考えて、上記の区切った配列の最後の要素のidをGetAPIのクエリに混ぜて新しい6個を取得

という面倒なことをやらなければなりません。

参考リンク

実際のコード

自分のプロダクトにおける使い方バックエンド

自分のプロダクトにおける使い方フロントエンド

以下はバックエンド側のサンプルですが、割と端折ってます。詳細は上記のソースコードを読んでみてください。

詳細は公式ドキュメントを参考にしてください。

まずクエリで送られてきたidをもとに該当idのアイテムを取得しておく。(itemとする)

6個取得したいとき

type QueryInput struct {
 ...
 Limit *int64 `min:"1" type:"integer"`
 ExclusiveStartKey map[string]*AttributeValue `type:"map"`
}
// ExclusiveStartKeyに取得したいアイテムの1つ前のアイテムのidなどの項目を指定する
// フロントから取得したいアイテムの1つ前のidを送ってもらう
// 取得しておいたitemからexclusiveStartKeyを作る

exclusiveStartKey = map[string]*dynamodb.AttributeValue{
 "id": {
  S: aws.String(*item["id"].S),
  },
 "post_at": {
  N: aws.String(*item["post_at"].N),
  },
}

ここでの注意点は、グローバルインデックスで検索をかけている場合は、この指定するExclusiveStartKeyもパーティションキーとソートキーだけでなく、そちらのキーたちも必要になります。