occams-record 0.10.0 → 0.11.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 +5 -5
- data/README.md +31 -37
- data/lib/occams-record/eager_loaders/has_one.rb +1 -1
- data/lib/occams-record/raw_query.rb +33 -0
- data/lib/occams-record/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 68db1cbd50ea9da8301200e9d57ca2517fe28624663c3d3ea2e463c99825f8d4
|
4
|
+
data.tar.gz: 4d55fddeaf26d1b2bc6b84ca09ec1d7a6d4ea23e5847b537c98f9ff5827bbc24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5924cf19d1fa6923146b289a661d80a964aa2616bc09768e8967dbe3a849349bb7aaf6c6edcb9fbe9301bf88cbad6f5a6dfb7397f8287c61b332d316197a18cb
|
7
|
+
data.tar.gz: f186dbeb9ea6f15cc4eebcb5cb2e6b135e5ba5b967c12e1162c938253f22fda343b3e2cc0401b93ba591fa94ebd695e6535e3a1ea8b4447aba11759dda549d28
|
data/README.md
CHANGED
@@ -2,28 +2,32 @@
|
|
2
2
|
|
3
3
|
> Do not multiply entities beyond necessity. -- Occam's Razor
|
4
4
|
|
5
|
-
Occam's Record is a high-efficiency query API for ActiveRecord.
|
5
|
+
Occam's Record is a high-efficiency query API for ActiveRecord. It is **not** an ORM or an ActiveRecord replacement. Use it to solve pain points in your existing ActiveRecord app.
|
6
6
|
|
7
|
-
|
7
|
+
* 3x-5x faster than ActiveRecord.
|
8
|
+
* Uses 1/3 the memory of ActiveRecord.
|
9
|
+
* Eliminates the N+1 query problem.
|
10
|
+
* Allows custom SQL when eager loading associations (use `select`, `where`, `order`, etc).
|
11
|
+
* `find_each`/`find_in_batches` respects `order` and `limit`.
|
12
|
+
* Allows eager loading of associations when using raw SQL.
|
13
|
+
* Allows `find_each`/`find_in_batches` when using raw SQL.
|
8
14
|
|
9
|
-
|
10
|
-
* OccamsRecord objects are **purely database rows** - they don't have any instance methods from your Rails models.
|
11
|
-
* OccamsRecord queries must specify each association that will be used. Otherwise they simply won't be availble.
|
12
|
-
|
13
|
-
For more on the rational behind OccamsRecord, see the Rational section at the end of the README. But in short, OccamsRecord is 3x-5x faster, uses 1/3 of the memory, and eliminates the N+1 query problem.
|
15
|
+
[Look over the speed and memory measurements yourself!](https://github.com/jhollinger/occams-record/wiki/Measurements). OccamsRecord achieves all of this by making some very specific trade-offs:
|
14
16
|
|
15
|
-
|
17
|
+
* OccamsRecord results are **read-only**.
|
18
|
+
* OccamsRecord results are **purely database rows** - they don't have any instance methods from your Rails models.
|
19
|
+
* OccamsRecord queries must eager load each association that will be used. Otherwise they simply won't be availble.
|
16
20
|
|
17
21
|
## Usage
|
18
22
|
|
23
|
+
Full documentation is available at [rubydoc.info/gems/occams-record](http://www.rubydoc.info/gems/occams-record).
|
24
|
+
|
19
25
|
**Add to your Gemfile**
|
20
26
|
|
21
27
|
```ruby
|
22
28
|
gem 'occams-record'
|
23
29
|
```
|
24
30
|
|
25
|
-
Full documentation is available at [rubydoc.info/gems/occams-record](http://www.rubydoc.info/gems/occams-record).
|
26
|
-
|
27
31
|
**Simple example**
|
28
32
|
|
29
33
|
```ruby
|
@@ -62,7 +66,7 @@ widgets[1].splines.map { |s| s.description }
|
|
62
66
|
|
63
67
|
**An insane example, but only half as insane as the one that prompted the creation of this library**
|
64
68
|
|
65
|
-
Here we're eager loading several levels down. Notice the `Proc` given to `eager_load(:orders)`. The `select:` option is just for convenience; you may instead pass a `Proc` and customize the query with any of ActiveRecord's query builder helpers (`select`, `where`, `order`, etc).
|
69
|
+
Here we're eager loading several levels down. Notice the `Proc` given to `eager_load(:orders)`. The `select:` option is just for convenience; you may instead pass a `Proc` and customize the query with any of ActiveRecord's query builder helpers (`select`, `where`, `order`, `limit`, etc).
|
66
70
|
|
67
71
|
```ruby
|
68
72
|
widgets = OccamsRecord.
|
@@ -118,14 +122,14 @@ widgets[0].expensive?
|
|
118
122
|
=> false
|
119
123
|
|
120
124
|
widgets[0].orders[0].description
|
121
|
-
=> "O839SJZ98B 1/8/2017"
|
125
|
+
=> "O839SJZ98B - 1/8/2017"
|
122
126
|
```
|
123
127
|
|
124
128
|
## Raw SQL queries
|
125
129
|
|
126
|
-
If you have a complicated query to run, you may drop down to hand-written SQL while still taking advantage of eager loading and variable escaping.
|
130
|
+
If you have a complicated query to run, you may drop down to hand-written SQL while still taking advantage of eager loading and variable escaping (not possible in ActiveRecord). Note the slightly different syntax for binding variables.
|
127
131
|
|
128
|
-
NOTE this feature is quite new and might have some bugs. Issues and Pull Requests welcome.
|
132
|
+
NOTE this feature is quite new and might have some bugs. Since we are not yet at 1.0, breaking changes may occur. Issues and Pull Requests welcome.
|
129
133
|
|
130
134
|
```ruby
|
131
135
|
widgets = OccamsRecord.sql(%(
|
@@ -151,30 +155,20 @@ widgets = OccamsRecord.
|
|
151
155
|
run
|
152
156
|
```
|
153
157
|
|
154
|
-
|
155
|
-
|
156
|
-
**What does OccamsRecord buy you?**
|
157
|
-
|
158
|
-
* OccamsRecord results are **one-third the size** of ActiveRecord results.
|
159
|
-
* OccamsRecord queries run **three to five times faster** than ActiveRecord queries.
|
160
|
-
* When eager loading associations you may specify which columns to `SELECT`. (This can be a significant performance boost to both your database and Rails app, on top of the above numbers.)
|
161
|
-
* When eager loading associations you may completely customize the query (`WHERE`, `ORDER BY`, `LIMIT`, etc.)
|
162
|
-
* By forcing eager loading of associations, OccamsRecord bypasses the primary cause of performance problems in Rails: N+1 queries.
|
163
|
-
* Forced eager loading also makes you consider the "shape" of your data, which can help you identify areas that need refactored (e.g. redundant foreign keys, more denormalization, etc.)
|
164
|
-
|
165
|
-
**What don't you give up?**
|
166
|
-
|
167
|
-
* You can still write your queries using ActiveRecord's query builder, as well as your existing models' associations & scopes.
|
168
|
-
* You can still use ActiveRecord for everything else - small queries, creating, updating, and deleting records.
|
169
|
-
* You can still inject some instance methods into your results, if you must. See below.
|
158
|
+
To use `find_each` or `find_in_batches` with raw SQL you must provide the `LIMIT` and `OFFSET` statements yourself.
|
170
159
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
160
|
+
```ruby
|
161
|
+
widgets = OccamsRecord.sql(%(
|
162
|
+
SELECT * FROM widgets
|
163
|
+
WHERE category_id = %{cat_id}
|
164
|
+
LIMIT %{batch_limit}
|
165
|
+
OFFSET %{batch_offset}
|
166
|
+
), {
|
167
|
+
cat_id: 5
|
168
|
+
}).find_each { |widget|
|
169
|
+
puts widget.name
|
170
|
+
}
|
171
|
+
```
|
178
172
|
|
179
173
|
## Unsupported features
|
180
174
|
|
@@ -12,7 +12,7 @@ module OccamsRecord
|
|
12
12
|
return if rows.empty?
|
13
13
|
ids = rows.map { |r| r.send @ref.active_record_primary_key }.compact.uniq
|
14
14
|
q = base_scope.where(@ref.foreign_key => ids)
|
15
|
-
q.where!(@ref.type => rows[0].class
|
15
|
+
q.where!(@ref.type => rows[0].class&.model_name) if @ref.options[:as]
|
16
16
|
yield q
|
17
17
|
end
|
18
18
|
|
@@ -25,6 +25,10 @@ module OccamsRecord
|
|
25
25
|
# eager_load(:category).
|
26
26
|
# run
|
27
27
|
#
|
28
|
+
# NOTE To use find_each/find_in_batches, your SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}',
|
29
|
+
# and an ORDER BY is strongly recomended.
|
30
|
+
# OccamsRecord will provide the bind values for you.
|
31
|
+
#
|
28
32
|
# @param sql [String] The SELECT statement to run. Binds should use Ruby's named string substitution.
|
29
33
|
# @param binds [Hash] Bind values (Symbol keys)
|
30
34
|
# @param use [Array<Module>] optional Module to include in the result class (single or array)
|
@@ -46,6 +50,7 @@ module OccamsRecord
|
|
46
50
|
attr_reader :binds
|
47
51
|
|
48
52
|
include EagerLoaders::Builder
|
53
|
+
include Batches
|
49
54
|
|
50
55
|
#
|
51
56
|
# Initialize a new query.
|
@@ -133,5 +138,33 @@ module OccamsRecord
|
|
133
138
|
a
|
134
139
|
}
|
135
140
|
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Returns an Enumerator that yields batches of records, of size "of".
|
144
|
+
# The SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}'.
|
145
|
+
# The bind values will be provided by OccamsRecord.
|
146
|
+
#
|
147
|
+
# @param of [Integer] batch size
|
148
|
+
# @return [Enumerator] yields batches
|
149
|
+
#
|
150
|
+
def batches(of:)
|
151
|
+
unless @sql =~ /LIMIT\s+%\{batch_limit\}/i and @sql =~ /OFFSET\s+%\{batch_offset\}/i
|
152
|
+
raise ArgumentError, "When using find_each/find_in_batches you must specify 'LIMIT %{batch_limit} OFFSET %{batch_offset}'. SQL statement: #{@sql}"
|
153
|
+
end
|
154
|
+
|
155
|
+
Enumerator.new do |y|
|
156
|
+
offset = 0
|
157
|
+
loop do
|
158
|
+
results = RawQuery.new(@sql, @binds.merge({
|
159
|
+
batch_limit: of,
|
160
|
+
batch_offset: offset,
|
161
|
+
}), use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders).model(@model).run
|
162
|
+
|
163
|
+
y.yield results if results.any?
|
164
|
+
break if results.size < of
|
165
|
+
offset += results.size
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
136
169
|
end
|
137
170
|
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: 0.
|
4
|
+
version: 0.11.0
|
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-
|
11
|
+
date: 2018-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -72,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
72
|
version: '0'
|
73
73
|
requirements: []
|
74
74
|
rubyforge_project:
|
75
|
-
rubygems_version: 2.
|
75
|
+
rubygems_version: 2.7.3
|
76
76
|
signing_key:
|
77
77
|
specification_version: 4
|
78
78
|
summary: The missing high-efficiency query API for ActiveRecord
|