Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Jul 19, 2024
2 parents 864baf2 + a29d3dc commit b7bac8d
Show file tree
Hide file tree
Showing 26 changed files with 92 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/Code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ body:
attributes:
label: 👀 Before submitting...
options:
- label: I upgraded to pagy version 9.0.0
- label: I upgraded to pagy version 9.0.1
required: true
- label: I searched through the [Documentation](https://ddnexus.github.io/pagy/)
required: true
Expand Down
20 changes: 6 additions & 14 deletions .github/latest_release_body.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
<!-- whats_new_start -->
### ✴ What's new in 9.0+ ✴
- Wicked-fast [Keyset Pagination](https://ddnexus.github.io/pagy/docs/api/keyset/) for big data! It works with `ActiveRecord::Relation` and `Sequel::Dataset` sets.
- More [Playground Apps](https://ddnexus.github.io/pagy/playground/) to showcase, clone and develop pagy APPs without any setup on your side
- Lots of refactorings and optimizations
- See the [Changelog](https://ddnexus.github.io/pagy/changelog) for breaking changes
- See the [Changelog](https://ddnexus.github.io/pagy/changelog) for possible breaking changes
<!-- whats_new_end -->

### Changes in 9.0.0
### Changes in 9.0.1

<!-- changes_start -->
- Improve Keyset::Sequel and docs
- BREAKING: Rename :max_limit > :limit_max
- BREAKING: Rename variable, param, accessor, extra and helper "items" to "limit"
- Add playground keyset_ar.ru and keyset_s.ru apps and integration with the rest of the gems
- Add keyset pagination base files
- Pagy::Keyset API
- ActiveRecord and Sequel adapters
- BREAKING: Transform the vars positional hash argument in keyword arguments (double splat); internal renaming of setup/assign methods
- Refactor pagy_get_vars in various backend extras
- BREAKING: Refactor the fragment url
- BREAKING: Refactor the anchor_string system
- BREAKING: Drop the support for 8+ deprecations
- Fix countless executing the count query
- Rename row_comparison > tuple_comparison; ignore mixed orders
<!-- changes_end -->

[CHANGELOG](https://ddnexus.github.io/pagy/changelog)
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ If you upgrade from version `< 9.0.0` see the following:
- None
<hr>

## Version 9.0.1

- Fix countless executing the count query
- Rename row_comparison > tuple_comparison; ignore mixed orders

## Version 9.0.0

### Breaking Changes
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: gem
specs:
pagy (9.0.0)
pagy (9.0.1)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -226,7 +226,7 @@ GEM
rematch (2.1.0)
rerun (0.14.0)
listen (~> 3.0)
rexml (3.3.1)
rexml (3.3.2)
strscan
rouge (4.3.0)
rubocop (1.65.0)
Expand Down
2 changes: 1 addition & 1 deletion README.md

Large diffs are not rendered by default.

49 changes: 28 additions & 21 deletions docs/api/keyset.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,25 @@ going to get slower queries).
This tecnique comes with that huge advantage and a set of limitations that makes it particularly useful for APIs and pretty
useless for UIs.

### Keyset Glossary

| Term | Description |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `offset pagination` | Technique to fetch each page by changing the `offset` from the collection start. The usual pagy pagination. |
| `keyset pagination` | Technique to fetch the next page starting from the `latest` fetched record in an `uniquely ordered` collection. |
| `uniquely ordered` | When the concatenation of the values of the ordered columns is unique for each record. It is similar to a composite primary `key` for the ordered table. |
| `set` | The `uniquely ordered` `ActiveRecord::Relation` or `Sequel::Dataset` collection to paginate. |
| `keyset` | The hash of column/direction pairs that pagy extracts from the order of the `set`. |
| `latest` | The hash of `keyset` attributes of the latest retrieved record. Pagy decodes it from the `page`. |
| `page` | The `Base64.urlsafe_encoded` `latest` that can be passed around as a query param. |

### Keyset or Offset pagination?

!!!success Use Keyset pagination with large dataset and API

- You will get the fastest pagination, regardless the table size and the relative position of the page

!!!danger Do not use with UIs even with large datasets
!!!warning Not very convenient with UIs even with large datasets
- You would be missing all the frontend features
- You would have to setup your DB very carefully in order to get good performance anyway

Expand All @@ -51,27 +63,23 @@ useless for UIs.

- You will get all the frontend features
- It will be easier to maintain because it requires almost no knowledge of SQL
- You can avoid the slowness on big tables by simply limiting the `:max_pages` pages: the users would not browse thousands of
- You can avoid the slowness on big tables by simply limiting the `:max_pages` pages: the users would not browse thousands of
records deep into your collection anyway

!!!danger Do not use with APIs
!!!warning Not very convenient with APIs

Unless your collection is small...

- Your server will suffer on big data and your API will be slower for no good reasons
!!!

### Keyset Glossary

| Term | Description |
|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `set` | The uniquely ordered `ActiveRecord::Relation` or `Sequel::Dataset` to paginate. |
| `keyset` | The hash of column/direction pairs that pagy extracts from the order of the `set`. It works similarly to a composite primary `key` for the ordered table. For that reason the concatenation of the values of the ordered columns must be unique for each record. |
| `latest` | The hash of `keyset` attributes of the latest retrieved record. Pagy decodes it from the `page`. |
| `page` | The `latest` (`Base64.urlsafe_encoded`) that can be passed around as a query param. |



## Overview

Pagy Keyset pagination does not waste resources and code complexity checking your set and your table config at every request.

That means that you have to be sure that your set is _uniquely ordered_ and that your tables have the right indexes (for
That means that you have to be sure that your set is `uniquely ordered` and that your tables have the right indexes (for
performance). You do it once during development, and pagy will be fast at each request. ;)

### Constraints
Expand All @@ -80,7 +88,7 @@ performance). You do it once during development, and pagy will be fast at each r
- You don't know the record count nor the page count
- You cannot jump to an arbitrary page (no numbereed pages)
- You can only paginate from one page to the next (in either directions)
- The `set` must be uniquely ordered. Add the primary key (usually `:id`) as the last order column to be sure
- The `set` must be `uniquely ordered`. Add the primary key (usually `:id`) as the last order column to be sure
- You should add the best DB index for your ordering strategy for performance. The keyset pagination would work even without
any index, but that would defeat its purpose (performance).
!!!
Expand All @@ -95,7 +103,7 @@ performance). You do it once during development, and pagy will be fast at each r

## How pagy keyset works

You pass an uniquely ordered `set` and `Pagy::Keyset` queries the page of records. It keeps track of the `latest` fetched
You pass an `uniquely ordered` `set` and `Pagy::Keyset` queries the page of records. It keeps track of the `latest` fetched
record by encoding its keyset attributes into the `page` param of the `next` URL. At each request, the `:page` is decoded and
used to prepare a `when` clause that excludes the records fetched up to that point, and pulls the `:limit` of requested
records. You know that you reached the end of the collection when `pagy.next.nil?`.
Expand Down Expand Up @@ -140,10 +148,9 @@ The current `page`. Default `nil` for the first page.
The `:limit` per page. Default `DEFAULT[:limit]`. You can use the [limit extra](/docs/extras/limit.md) to have it
automatically assigned from the request param.

=== `:row_comparison`
=== `:tuple_comparison`

Boolish variable that enables the row comparison query. Check whether your DB supports it (especially for composite index with
mixed order). Default `nil`.
Boolish variable that enables the tuple comparison e.g. `(brand, id) > (:brand, :id)`. It works only for same direction order, so it is ignored for mixed order queries. Check how your DB supports it (you should only use `NOT NULL` columns). Default `nil`.

==- `:after_latest`

Expand Down Expand Up @@ -185,9 +192,9 @@ Pagy::Keyset(set, typecast_latest:)

==- Records may repeat or be missing from successive pages

!!!danger Your set is not uniquely ordered
!!!danger Your set is not `uniquely ordered`

Pagy does not check if your set is uniquely ordered (read why in the [overview](#overview))
Pagy does not check if your set is `uniquely ordered` (read why in the [overview](#overview))

```rb
# Neither columns are unique
Expand Down Expand Up @@ -221,7 +228,7 @@ Most likely your index is not right, or your case needs a custom query
!!! Success

- Ensure that the composite index reflects exactly the columns sequence and order of your keyset
- Research about your specific DB features: type of index and performance for different ordering: use SQL `EXPLAIN` to confirm.
- Research about your specific DB features: type of index and performance for different ordering: use SQL `EXPLAIN ANALIZE` or similar tool to confirm.
- Consider using your custom optimized `when` query with the [:after_latest](#after-latest) variable
!!!

Expand Down
12 changes: 6 additions & 6 deletions docs/extras/keyset.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ require 'pagy/extras/keyset'
```ruby Controller (action)
# The set argument must be an uniquely ORDERED Activerecord Scope or Sequel Dataset

# minimal unique ordering with the primary key
# Minimal unique ordering with the primary key
set = Product.order(:id)
@pagy, @records = pagy_keyset(set, **vars)

# using same-direction ordering keyset (all :asc, or all :desc)
# notice the primary key as the last column as a tie-breaker for uniqueness
# Using same-direction order keyset (all :asc, or all :desc)
# Notice the primary key added as the last column as a tie-breaker for uniqueness
set = Product.order(:brand, :model, :id)
# allow using the row_comparison query
@pagy, @records = pagy_keyset(set, row_comparison: true)
# Allow using the tuple_comparison e.g. (brand, model, id) > (:brand, :model, :id)
@pagy, @records = pagy_keyset(set, tuple_comparison: true)

# ordering with mixed-direction ordering keyset
# Ordering with mixed-direction order keyset
set = Product.order(brand: :asc, model: :desc, id: :asc)
@pagy, @records = pagy_keyset(set, **vars)
```
Expand Down
2 changes: 1 addition & 1 deletion gem/apps/calendar.ru
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# DOC
# https://ddnexus.github.io/pagy/playground/#5-calendar-app

VERSION = '9.0.0'
VERSION = '9.0.1'

# Gemfile
require 'bundler/inline'
Expand Down
2 changes: 1 addition & 1 deletion gem/apps/demo.ru
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# DOC
# https://ddnexus.github.io/pagy/playground/#3-demo-app

VERSION = '9.0.0'
VERSION = '9.0.1'

require 'bundler/inline'
require 'bundler'
Expand Down
2 changes: 1 addition & 1 deletion gem/apps/rails.ru
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# DOC
# https://ddnexus.github.io/pagy/playground/#2-rails-app

VERSION = '9.0.0'
VERSION = '9.0.1'

# Gemfile
require 'bundler/inline'
Expand Down
2 changes: 1 addition & 1 deletion gem/apps/repro.ru
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# DOC
# https://ddnexus.github.io/pagy/playground/#1-repro-app

VERSION = '9.0.0'
VERSION = '9.0.1'

require 'bundler/inline'
require 'bundler'
Expand Down
2 changes: 1 addition & 1 deletion gem/bin/pagy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

VERSION = '9.0.0'
VERSION = '9.0.1'
APPS = %w[repro rails demo calendar keyset_ar keyset_s].freeze
LINUX = RbConfig::CONFIG['host_os'].include?('linux')
HOST = '0.0.0.0'
Expand Down
2 changes: 1 addition & 1 deletion gem/config/pagy.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Pagy initializer file (9.0.0)
# Pagy initializer file (9.0.1)
# Customize only what you really need and notice that the core Pagy works also without any of the following lines.
# Should you just cherry pick part of this file, please maintain the require-order of the extras

Expand Down
4 changes: 2 additions & 2 deletions gem/javascripts/pagy.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b7bac8d

Please sign in to comment.