where_row 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -21
- data/lib/where_row/query_builder.rb +10 -7
- data/lib/where_row/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 563fa38bd6eacdc602cd365f11985ccfa9ea91d7c2f9720227a75a67e9b082d2
|
4
|
+
data.tar.gz: 6dcc2112211fb876d80b5c7451d8f6cf2ebf44b17f7afbc8491cfb92f9d4e70c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e1e3f1a9b1a33143aa7aef4cd68c5d3fbe8681261ded57398f938f4217d63e8089ff3b0dd7dba849da920add74cc184112c3809d6ae3d4bb679c9beac7cf1e3
|
7
|
+
data.tar.gz: 8f634e35ebdfb1e74d2762a93f63165b4c09a6d4d5679c2d38364dd02cbd6be398ba41f3095468135decb3d39e7684972419a6672655b9d546cab27d1748b448
|
data/README.md
CHANGED
@@ -6,47 +6,48 @@ Sometimes, the classic offset method to paginate results can be very inneficient
|
|
6
6
|
|
7
7
|
Consider the following example, as it appears on [Use The Index Luke](https://use-the-index-luke.com/sql/partial-results/fetch-next-page)
|
8
8
|
|
9
|
+
Suppose we want to have an infinite scrolling functionality for a collection of sales, ordered by the date the sale took place. Ordering just by sale_date will not suffice, since many sales can occur on the same date. Hence, we need to order by both the date and the id, to have a deterministic order. In SQL, this would look like this:
|
10
|
+
|
9
11
|
```SQL
|
10
12
|
CREATE INDEX sl_dtid ON sales (sale_date, sale_id)
|
11
13
|
|
12
14
|
SELECT *
|
13
15
|
FROM sales
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
WHERE (sale_date, sale_id) < (?, ?)
|
17
|
+
ORDER BY sale_date DESC, sale_id DESC
|
18
|
+
FETCH FIRST 10 ROWS ONLY
|
17
19
|
```
|
18
20
|
|
19
|
-
The Row Value syntax is
|
20
|
-
maybe there is partial support (for example, the index is not properly utilized).
|
21
|
+
The Row Value syntax is not supported in Rails directly. Furthermore, some databases still don't support this syntax as well, or maybe there is partial support (for example, the index is not properly utilized).
|
21
22
|
|
22
23
|
Thankfully, the same results can be achieved with plain-old logical expressions and comparisons. The equivalent query would look like this:
|
23
24
|
|
24
25
|
```SQL
|
25
|
-
|
26
|
+
SELECT *
|
26
27
|
FROM sales
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
WHERE sale_date <= ?
|
29
|
+
AND NOT (sale_date = ? AND sale_id >= ?)
|
30
|
+
ORDER BY sale_date DESC, sale_id DESC
|
31
|
+
FETCH FIRST 10 ROWS ONLY
|
31
32
|
```
|
32
33
|
|
33
|
-
This is something that can be directly expressed in Rails
|
34
|
+
This is something that can be directly expressed in Rails. One possible way is the following:
|
34
35
|
|
35
36
|
```ruby
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
Sales.
|
38
|
+
where(sale_date: (..date_offset)).
|
39
|
+
where.not(sale_date: date_offset, sale_id: (sale_id_offset..)).
|
40
|
+
order(sale_date: :desc, sale_id: :desc).
|
41
|
+
limit(10)
|
41
42
|
```
|
42
43
|
|
43
|
-
However, the intent of this query is not clear at all when reading through this piece of code. Furthermore, if for any reason we need more than two columns, this will blow up pretty quickly. This gem allows us to generate this query/relation with a more explicit syntax.
|
44
|
+
However, the intent of this query is not clear at all when reading through this piece of code. Furthermore, if for any reason we need more than two columns (maybe by the sale's client_id), this will blow up pretty quickly. This gem allows us to generate this query/relation with a more explicit syntax.
|
44
45
|
|
45
46
|
```ruby
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
Sales.
|
48
|
+
where_row(:sale_date, :sale_id).lt(date_offset, sale_id_offset).
|
49
|
+
order(sale_date: :desc, sale_id: :desc).
|
50
|
+
limit(10)
|
50
51
|
```
|
51
52
|
|
52
53
|
## Installation
|
@@ -84,19 +84,22 @@ module WhereRow
|
|
84
84
|
|
85
85
|
def build_comparison_predicate
|
86
86
|
last_idx = keys.size - 1
|
87
|
-
reversed_op = REVERSE_OP_MAP[operator]
|
88
|
-
last_pred = build_predicate_for_key(relation, last_idx, reversed_op)
|
89
87
|
|
90
|
-
|
88
|
+
if last_idx.zero?
|
89
|
+
return build_predicate(relation, keys[last_idx], values[last_idx], operator)
|
90
|
+
end
|
91
|
+
|
92
|
+
last_pred = build_predicate_for_key(relation, last_idx, operator)
|
91
93
|
|
92
|
-
first_keys_op = :lt if
|
93
|
-
first_keys_op = :gt if
|
94
|
-
first_keys_op ||=
|
94
|
+
first_keys_op = :lt if operator == :lteq
|
95
|
+
first_keys_op = :gt if operator == :gteq
|
96
|
+
first_keys_op ||= operator
|
95
97
|
|
96
98
|
(0...last_idx).
|
97
99
|
map { |i| build_predicate_for_key(relation, i, first_keys_op) }.
|
98
100
|
reduce(:and).
|
99
|
-
and(last_pred)
|
101
|
+
and(last_pred).
|
102
|
+
not
|
100
103
|
end
|
101
104
|
|
102
105
|
def build_predicate_for_key(relation, idx, op)
|
data/lib/where_row/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: where_row
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Odysseas Doumas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|