Slide 1

Slide 1 text

ページネーションをどう実装す るか

Slide 2

Slide 2 text

ページ番号・ベース (page: Int!, pageSize: Int!) カーソル・ベース (after: String, first: Int)

Slide 3

Slide 3 text

GraphQL Cursor Connections Specification https://relay.dev/graphql/connections.htm#sec- Connection-Types

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Cursor Connections では2つの観点が絡み合っている リソースAとリソースBの関係性を辿る ページネーション

Slide 6

Slide 6 text

リソースAとリソースBの関係性を辿る

Slide 7

Slide 7 text

Edge リソースAとリソースBのつながりを表現する中間的な型。

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Edge が"2つのリソースの関係"がもつ情報をもたせるのに適 した場所になる。

Slide 10

Slide 10 text

補足: Connection が付加的な情報を持つこともある

Slide 11

Slide 11 text

ページネーション

Slide 12

Slide 12 text

Cursor Connections ではページ番号を使うのではなく、 起点となる Edge.cursor から範囲を指定してデータを取っ てくる。 after: "Y3Vyc29yMg==", first: 10 Y3Vyc29yMg== のカーソルをもつオブジェクトの次の10 件を取得 before: "Y3Vyc29yMg==", last: 10 Y3Vyc29yMg== のカーソルをもつオブジェクトの手前10 件を取得

Slide 13

Slide 13 text

メリット 整合性のあるページネーションが可能 次のページの先頭に、前のページの末尾と同じデータが入 る、ということがない 無限スクロールしていくようなUIに適している オフセットが大きい数字になった場合の LIMIT ... OFFSET ... が遅い問題から解放される

Slide 14

Slide 14 text

デメリット シーク法を用いたSQLは複雑性が増す(WHERE) 隣接していないページへのジャンプが難しい データの増減がページNのデータに影響して当然、という要 件には向かない ソート方法の種類に応じて、SQLのパターンが増える。

Slide 15

Slide 15 text

↓ 開発経験からして、大抵はこの要 件を持つような気がする。 (考えていないだけだけの可能性が大いに有。たとえばオートコンプリー トの類とか) データの増減がページNのデータに影響して当然、という要 件 これまでのB2B製品の

Slide 16

Slide 16 text

改めて ページネーションをどう実装す るか

Slide 17

Slide 17 text

リソースAとリソースBの関係性を辿る Cursor Connections の使用に則り、 Connection/Edge/Node の型を使うのが良さそう ページネーション ページ番号・ベースかカーソル・ベースか、設計時点で要 件を満たしつつ楽な方を実装すればよいのでは? ただし、将来的に実装しなかった方が欲しくなる可能性が あるので、両方の実装が共存できるようにしておきたい

Slide 18

Slide 18 text

例えば・・・

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

命名規則(案) クエリ(フィールド) カーソル・ベース users ページ番号・ベース usersByPage ページ情報 カーソル・ベース CursorBasedPageInfo ページ番号・ベース PageBasedPageInfo

Slide 21

Slide 21 text

おまけ: UIにページネーションの機能がない場 合…

Slide 22

Slide 22 text

例えばフォルダー構造

Slide 23

Slide 23 text

階層構造×ページネーションというパターンは見たことがな い。

Slide 24

Slide 24 text

↓のような複雑なクエリからB/Eをどう守れば良いのか…

Slide 25

Slide 25 text

複雑性を用いたサーバー保護の観点からフォルダー一覧もペー ジネーションできるようにした方が良い。 pageSize の上限はシステムのフォルダー作成数上限と相 談してうまいこと決める 最悪、複数回リクエストすれば良い(Apollo Clientの fetchMore )

Slide 26

Slide 26 text

GraphQL スキーマ設計ガイド 第2版 安易な気持ちで tags: [Tag!]! という定義をルールに逆 らって作ってしまいました。すると Tag はいくつかのさら なる別の型への展開を持ち、ここで complexityの計算が崩 壊しました。教訓として、DBから1アクションで取れるリス トデータであっても、スカラ型でもenumでもない場合はイ ンメモリでCursor Connections相当の構造に変換するべき です。つらいです。

Slide 27

Slide 27 text

これなら安心して処理を拒否することができる。

Slide 28

Slide 28 text

これなら処理してあげても良いかもしれない…? (フォルダーごとに先頭3つの連絡先データを見せる)

Slide 29

Slide 29 text

と思ったが、やっぱり歪に見える。 階層ごとにページネーションが必要 全てのフォルダーに対して、子フォルダーを全て取得でき たかどうか気にしてあげる必要がある 複雑性を無駄に大きく見積もる必要がある

Slide 30

Slide 30 text

階層構造がネックになる場合、フラットなデータ構造に変更す ることも視野に入れる。

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

シンプルになった。 その代わりに、F/Eがツリー構造を扱いたい場合には変換処理 を入れてもらうことになる。