occams-record 1.0.0.rc4 → 1.0.0.rc5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/lib/occams-record/eager_loaders/belongs_to.rb +5 -1
- data/lib/occams-record/eager_loaders/has_one.rb +3 -1
- data/lib/occams-record/merge.rb +76 -26
- data/lib/occams-record/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: a370b14fabfecd12e219d2bdf5c0719e95c71518e283a5aa5601e9de69c5b3d4
|
4
|
+
data.tar.gz: dbfab334da683c11342efeece655780c59272f2ac51c1ddf6c5382fea926571a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35e38ad43a0cbe41adfcbffa72badd4cf3b6f1f716a74fe1e752e419fb4697903156445360f5b159d951983faa716c71ad157edce704dbfa7155f8d51921b44d
|
7
|
+
data.tar.gz: bf2e41947e4366243b3a45ce9a4f4a8bf3fd2dea1f7314f85a61cc3268be3c514fb08e6a5a6fcf6c0a83de35e7a0905ffea3d2d9f64c4511433ec56fc3088174
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
> Do not multiply entities beyond necessity. -- Occam's Razor
|
4
4
|
|
5
|
-
**Breaking change in 1.0.0 RC1** See HISTORY.md.
|
5
|
+
**Breaking change in 1.0.0 RC1** See [HISTORY.md](https://github.com/jhollinger/occams-record/blob/master/HISTORY.md#100rc1-2018-12-01).
|
6
6
|
|
7
7
|
Occam's Record is a high-efficiency, advanced query library for ActiveRecord apps. It is **not** an ORM or an ActiveRecord replacement. Use it to solve pain points in your existing ActiveRecord app. Occams Record gives you two things:
|
8
8
|
|
@@ -228,6 +228,14 @@ The following ActiveRecord features are not supported, and likely never will be.
|
|
228
228
|
|
229
229
|
---
|
230
230
|
|
231
|
+
# Benchmarking
|
232
|
+
|
233
|
+
`bundle exec rake bench` will run a suite of performance benchmarks comparing Occams Record with Active Record. These are primarily used during development to prevent performance regressions. An in-memory Sqlite database is used.
|
234
|
+
|
235
|
+
If you run your own benchmarks, keep in mind exactly what you're measuring. For example if you're benchmarking a report written in AR vs OR, there are many constants in that measurement: the time spent in the database, the time spent transfering the database results back to your Ruby process, any calculations you're doing on your results, and the time spent building your html/json/csv/etc. So if OR is 3x fastr than AR, the total runtime of said report *won't* improve by 3x.
|
236
|
+
|
237
|
+
On the other hand, Active Record makes it *very* easy to forget to eager load associations (the N+1 query problem). Occams Record fixes that. So if your report was missing some associations you could see easily see performance improvements well over 3x.
|
238
|
+
|
231
239
|
# Testing
|
232
240
|
|
233
241
|
To run the tests, simply run:
|
@@ -11,6 +11,8 @@ module OccamsRecord
|
|
11
11
|
# @yield
|
12
12
|
#
|
13
13
|
def query(rows)
|
14
|
+
return if rows.empty?
|
15
|
+
|
14
16
|
ids = rows.map { |row|
|
15
17
|
begin
|
16
18
|
row.send @ref.foreign_key
|
@@ -18,7 +20,9 @@ module OccamsRecord
|
|
18
20
|
raise MissingColumnError.new(row, e.name)
|
19
21
|
end
|
20
22
|
}.compact.uniq
|
21
|
-
|
23
|
+
|
24
|
+
q = base_scope.where(@ref.association_primary_key => ids)
|
25
|
+
yield q if ids.any?
|
22
26
|
end
|
23
27
|
|
24
28
|
#
|
@@ -12,6 +12,7 @@ module OccamsRecord
|
|
12
12
|
#
|
13
13
|
def query(rows)
|
14
14
|
return if rows.empty?
|
15
|
+
|
15
16
|
ids = rows.map { |row|
|
16
17
|
begin
|
17
18
|
row.send @ref.active_record_primary_key
|
@@ -19,9 +20,10 @@ module OccamsRecord
|
|
19
20
|
raise MissingColumnError.new(row, e.name)
|
20
21
|
end
|
21
22
|
}.compact.uniq
|
23
|
+
|
22
24
|
q = base_scope.where(@ref.foreign_key => ids)
|
23
25
|
q.where!(@ref.type => rows[0].class&.model_name) if @ref.options[:as]
|
24
|
-
yield ids.any?
|
26
|
+
yield q if ids.any?
|
25
27
|
end
|
26
28
|
|
27
29
|
#
|
data/lib/occams-record/merge.rb
CHANGED
@@ -29,27 +29,53 @@ module OccamsRecord
|
|
29
29
|
# for the associated rows.
|
30
30
|
#
|
31
31
|
def single!(assoc_rows, mapping)
|
32
|
-
target_attrs = mapping.keys
|
32
|
+
target_attrs = mapping.keys
|
33
33
|
assoc_attrs = mapping.values
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
# Optimized for merges where there's a single mapping key pair (which is the vast majority)
|
36
|
+
if mapping.size == 1
|
37
|
+
target_attr, assoc_attr = target_attrs[0], assoc_attrs[0]
|
38
|
+
assoc_rows_by_ids = assoc_rows.reduce({}) { |a, assoc_row|
|
39
|
+
#begin
|
40
|
+
id = assoc_row.send assoc_attr
|
41
|
+
#rescue NoMethodError => e
|
42
|
+
# raise MissingColumnError.new(assoc_row, e.name)
|
43
|
+
#end
|
44
|
+
a[id] ||= assoc_row
|
45
|
+
a
|
46
|
+
}
|
47
|
+
|
48
|
+
target_rows.each do |row|
|
49
|
+
begin
|
50
|
+
attr = row.send target_attr
|
51
|
+
rescue NoMethodError => e
|
52
|
+
raise MissingColumnError.new(row, e.name)
|
53
|
+
end
|
54
|
+
row.send @assign, attr ? assoc_rows_by_ids[attr] : nil
|
40
55
|
end
|
41
|
-
a[ids] ||= assoc_row
|
42
|
-
a
|
43
|
-
}
|
44
56
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
# Slower but works with any number of mapping key pairs
|
58
|
+
else
|
59
|
+
assoc_rows_by_ids = assoc_rows.reduce({}) { |a, assoc_row|
|
60
|
+
begin
|
61
|
+
ids = assoc_attrs.map { |attr| assoc_row.send attr }
|
62
|
+
rescue NoMethodError => e
|
63
|
+
raise MissingColumnError.new(assoc_row, e.name)
|
64
|
+
end
|
65
|
+
a[ids] ||= assoc_row
|
66
|
+
a
|
67
|
+
}
|
68
|
+
|
69
|
+
target_rows.each do |row|
|
70
|
+
begin
|
71
|
+
attrs = target_attrs.map { |attr| row.send attr }
|
72
|
+
rescue NoMethodError => e
|
73
|
+
raise MissingColumnError.new(row, e.name)
|
74
|
+
end
|
75
|
+
row.send @assign, attrs.any? ? assoc_rows_by_ids[attrs] : nil
|
50
76
|
end
|
51
|
-
row.send @assign, attrs.any? ? assoc_rows_by_ids[attrs] : nil
|
52
77
|
end
|
78
|
+
|
53
79
|
nil
|
54
80
|
end
|
55
81
|
|
@@ -65,22 +91,46 @@ module OccamsRecord
|
|
65
91
|
target_attrs = mapping.keys
|
66
92
|
assoc_attrs = mapping.values
|
67
93
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
94
|
+
# Optimized for merges where there's a single mapping key pair (which is the vast majority)
|
95
|
+
if mapping.size == 1
|
96
|
+
target_attr, assoc_attr = target_attrs[0], assoc_attrs[0]
|
97
|
+
begin
|
98
|
+
assoc_rows_by_attr = assoc_rows.group_by { |r|
|
99
|
+
r.send assoc_attr
|
100
|
+
}
|
101
|
+
rescue NoMethodError => e
|
102
|
+
raise MissingColumnError.new(assoc_rows[0], e.name)
|
103
|
+
end
|
75
104
|
|
76
|
-
|
105
|
+
target_rows.each do |row|
|
106
|
+
begin
|
107
|
+
pkey = row.send target_attr
|
108
|
+
rescue NoMethodError => e
|
109
|
+
raise MissingColumnError.new(row, e.name)
|
110
|
+
end
|
111
|
+
row.send @assign, assoc_rows_by_attr[pkey] || []
|
112
|
+
end
|
113
|
+
|
114
|
+
# Slower but works with any number of mapping key pairs
|
115
|
+
else
|
77
116
|
begin
|
78
|
-
|
117
|
+
assoc_rows_by_attrs = assoc_rows.group_by { |r|
|
118
|
+
assoc_attrs.map { |attr| r.send attr }
|
119
|
+
}
|
79
120
|
rescue NoMethodError => e
|
80
|
-
raise MissingColumnError.new(
|
121
|
+
raise MissingColumnError.new(assoc_rows[0], e.name)
|
122
|
+
end
|
123
|
+
|
124
|
+
target_rows.each do |row|
|
125
|
+
begin
|
126
|
+
pkeys = target_attrs.map { |attr| row.send attr }
|
127
|
+
rescue NoMethodError => e
|
128
|
+
raise MissingColumnError.new(row, e.name)
|
129
|
+
end
|
130
|
+
row.send @assign, assoc_rows_by_attrs[pkeys] || []
|
81
131
|
end
|
82
|
-
row.send @assign, assoc_rows_by_attrs[pkeys] || []
|
83
132
|
end
|
133
|
+
|
84
134
|
nil
|
85
135
|
end
|
86
136
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: occams-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Hollinger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|