コンテンツのランキングがすぐに変わる可能性がある場合、ページネーションは難しく、ランキングがユーザーごとに異なる場合はさらに難しくなります。(無限スクロールは、リンクが見えないページネーションの一種として扱いましょう。) 2 つの難しい問題があります。新しく追加されたコンテンツが上部に表示されることと、コンテンツが再ランク付けされることです。
新しく追加されたコンテンツのことは忘れて、それを表示するにはページ 1 を更新する必要があることを受け入れましょう。また、 pure を行っているふりをしましょうORDER BY position
。他のもので注文している場合は、ウィンドウ関数を使用する必要がある場合があります。私たちのページには、1 ページあたり 4 行の動物があります。彼らは始めます:
+----+----------+-----------+
| id | position^| animal |
+----+----------+-----------+
| 1 | 1 | Alpacas |
| 2 | 2 | Bats |
| 3 | 3 | Cows |
| 4 | 4 | Dogs |
| 5 | 5 | Elephants |
| 6 | 6 | Foxes |
| 7 | 7 | Giraffes |
| 8 | 8 | Horses |
+----+----------+-----------+
ページ 1 をフェッチした後、ページ 2 をフェッチする前に、多くのアイテムが移動します。DBは次のとおりです。
+----+----------+-----------+
| id | position^| animal |
+----+----------+-----------+
| 4 | 1 | Dogs |
| 2 | 2 | Bats |
| 1 | 3 | Alpacas |
| 5 | 4 | Elephants |
| 6 | 5 | Foxes |
| 7 | 6 | Giraffes |
| 3 | 7 | Cows |
| 8 | 8 | Horses |
+----+----------+-----------+
次の 3 つの一般的なアプローチがあります。
オフセット/リミットアプローチ
これは典型的な素朴なアプローチです。Railsでは、will_paginateとKaminariがどのように機能するかです. ページ 2 をフェッチする場合は、
SELECT * FROM animals
ORDER BY animals.position
OFFSET ((:page_num - 1) * :page_size)
LIMIT :page_size;
行5〜8を取得します。私は象を見ることはないだろうし、牛は二度見るだろう.
最終確認 ID アプローチ
Reddit は別のアプローチを採用しています。ページ サイズに基づいて最初の行を計算する代わりに、クライアントは、ブックマークのように、最後に表示したアイテムの ID を追跡します。「次へ」を押すと、そのブックマークから探し始めます。
SELECT * FROM animals
WHERE position > (
SELECT position FROM animals
WHERE id = :last_seen_id
)
ORDER BY position
LIMIT :page_size;
場合によっては、これはページ/オフセットよりもうまく機能します。しかし、私たちの場合、最後に表示された投稿である Dogs がすぐに 1 位になりました。したがって、クライアントは を送信し?last_seen_id=4
、私のページ 2 はコウモリ、アルパカ、ゾウ、キツネです。見逃した動物はいませんが、コウモリとアルパカは 2 回見ました。
サーバー側の状態
HackerNews (および現在の私たちのサイト) は、サーバー側の継続によってこれを解決します。それらは結果セット全体を (または少なくとも数ページ前に)保存し、"More" リンクはその続きを参照します。ページ 2 をフェッチするときは、「元のクエリのページ 2」を要求します。同じオフセット/制限計算を使用していますが、元のクエリに反しているため、物事が移動したことは気にしません。ゾウ、キツネ、キリン、ウマが見えます。重複も見逃されたアイテムもありません。
欠点は、サーバーに多くの状態を保存する必要があることです。HN では、それは RAM に保存されます。実際には、これらの継続は、[詳細] ボタンを押す前に期限切れになることが多く、有効なリンクを見つけるためにページ 1 に戻る必要があります。ほとんどのアプリケーションでは、それを memcached に格納することも、データベース自体に格納することもできます (独自のテーブルを使用するか、保持可能なカーソルを使用して Oracle または PostgreSQL に格納します)。アプリケーションによっては、パフォーマンスが低下する場合があります。少なくとも PostgreSQL では、適切なデータベース接続を再度ヒットする方法を見つける必要があります。これには、多くのスティッキー状態または巧妙なバックエンド ルーティングが必要です。
考えられるアプローチはこれら 3 つだけですか? そうでない場合、これについて読むための Google ジュースを提供してくれるコンピューター サイエンスの概念はありますか? 結果セット全体を保存せずに継続アプローチを近似する方法はありますか? 長期的には、複雑なイベント ストリーミング/ポイント イン タイム システムがあり、「ページ 1 を取得した時点での結果セット」は永久に導出可能です。それどころか…?