jpie 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/lib/json_api/controllers/concerns/resource_actions/include_preloading.rb +10 -35
- data/lib/json_api/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c04100490b218fa6dbef0035d581acd97ec7ebae03d045d0966a633bdcfb567
|
|
4
|
+
data.tar.gz: 116d6e4ae7f51f03a47f8222771f2fb508435fce759014eadd3a02642a4f7b85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e86bd604abba1b02e6148d857a2967a05b90a35c796503f2517d25368cbb95a8c102afddc2208a8f77961085bc041a55e27e6b6d0f304b37ed9404362fbf64eb
|
|
7
|
+
data.tar.gz: 3ec3bda5b8ce2e07fb494f790cbc20391b37fd12a9f57c6a352b3129a9037246116d68440f70cb86b17e3ca424ad6bd4efdbba174b3af8825cb1149bfc59fbb5
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ at release time.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.1.0] - 2026-09-15
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Preload the include tree, never join it.** `scope_with_includes` handed the
|
|
15
|
+
tree to `includes`, and Rails answers `includes` with one LEFT OUTER JOIN
|
|
16
|
+
across every path as soon as the scope answers `eager_loading?` with true. A
|
|
17
|
+
single condition on a table that no `joins` names is enough to cause that: a
|
|
18
|
+
resource default such as
|
|
19
|
+
`super.includes(:archival).where(archivals: { archived_at: nil })` does it.
|
|
20
|
+
Every collection path in the tree then multiplied row for row. A reported
|
|
21
|
+
page of 25 records joined 75,712 rows and allocated 796.1MB to return an
|
|
22
|
+
822kB document, and 98% of those bytes were the rows, not the serializer. The
|
|
23
|
+
tree now preloads in every case: the same document costs 18.1MB in 12
|
|
24
|
+
queries. Serialization filters on none of the included tables, so the tree
|
|
25
|
+
never needed the join, and a scope's own `includes` is untouched — it still
|
|
26
|
+
supplies whatever join its own conditions need. Regression spec:
|
|
27
|
+
`include_preload_never_joins_spec.rb`.
|
|
28
|
+
|
|
29
|
+
**One behaviour change comes with it.** A query that puts a condition or an
|
|
30
|
+
order on an included table, and does not join that table itself, now raises.
|
|
31
|
+
Before, it worked only when the client's `?include=` named that table. Look
|
|
32
|
+
for such a query in three places:
|
|
33
|
+
|
|
34
|
+
- the `records` scope of a resource;
|
|
35
|
+
- a model scope that `sort` calls, such as `order(authors: { name: dir })`;
|
|
36
|
+
- a model scope that `filter` calls, such as `where(authors: { name: v })`.
|
|
37
|
+
|
|
38
|
+
Add `joins` or `left_joins` for the table to fix it.
|
|
39
|
+
|
|
10
40
|
## [4.0.0] - 2026-09-09
|
|
11
41
|
|
|
12
42
|
### Changed
|
|
@@ -25,12 +25,16 @@ module JSONAPI
|
|
|
25
25
|
return scope unless includes.any?
|
|
26
26
|
return scope if scope.is_a?(Array)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
|
|
28
|
+
# `preload`, never `includes`. `includes` resolves to one LEFT OUTER JOIN
|
|
29
|
+
# across the whole tree whenever the scope already answers
|
|
30
|
+
# `eager_loading?` with true, which one condition on an unjoined table is
|
|
31
|
+
# enough to cause. Every collection path in the tree then multiplies row
|
|
32
|
+
# for row: a measured page of 25 records loaded 75,712 rows and allocated
|
|
33
|
+
# 800MB, against 18MB for the same document through `preload`.
|
|
34
|
+
# Serialization filters on none of the included tables, so the tree never
|
|
35
|
+
# needs the join. A scope's own `includes` is untouched and still supplies
|
|
36
|
+
# whatever join its own conditions need.
|
|
37
|
+
scope.preload(includes_to_hash(includes))
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def filter_includable(hash, klass)
|
|
@@ -57,35 +61,6 @@ module JSONAPI
|
|
|
57
61
|
reflection.macro == :has_one_attached ? :"#{key}_attachment" : :"#{key}_attachments"
|
|
58
62
|
end
|
|
59
63
|
|
|
60
|
-
def preload_required?(hash, klass)
|
|
61
|
-
hash.any? do |key, value|
|
|
62
|
-
assoc = klass.reflect_on_association(key)
|
|
63
|
-
next false unless assoc
|
|
64
|
-
|
|
65
|
-
assoc.polymorphic? || instance_dependent_scope?(assoc) ||
|
|
66
|
-
(value.present? && preload_required?(value, assoc.klass))
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Rails refuses to eager-load (JOIN) an association whose scope takes the owner
|
|
71
|
-
# instance (`->(owner) { ... }`), raising "instance dependent scopes are not
|
|
72
|
-
# supported". Its arity is non-zero; such associations must be preloaded instead.
|
|
73
|
-
def instance_dependent_scope?(assoc)
|
|
74
|
-
scope = assoc.scope
|
|
75
|
-
!scope.nil? && scope.arity != 0
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def hash_contains_polymorphic?(hash, klass)
|
|
79
|
-
hash.any? { |key, value| polymorphic_in_hash_entry?(key, value, klass) }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def polymorphic_in_hash_entry?(key, value, klass)
|
|
83
|
-
assoc = klass.reflect_on_association(key)
|
|
84
|
-
return false unless assoc
|
|
85
|
-
|
|
86
|
-
assoc.polymorphic? || (value.present? && hash_contains_polymorphic?(value, assoc.klass))
|
|
87
|
-
end
|
|
88
|
-
|
|
89
64
|
# Applies each included resource's own `records` preloads to the records
|
|
90
65
|
# of that resource's class. Walks the whole include tree: a resource
|
|
91
66
|
# reached at depth 3 gets its preloads just as one reached at depth 1.
|
data/lib/json_api/version.rb
CHANGED