occams-record 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|