rails 4.1.16 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/guides/CHANGELOG.md +13 -102
- data/guides/Rakefile +2 -2
- data/guides/assets/javascripts/guides.js +6 -0
- data/guides/assets/stylesheets/main.css +4 -1
- data/guides/bug_report_templates/action_controller_gem.rb +2 -2
- data/guides/bug_report_templates/action_controller_master.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +2 -0
- data/guides/rails_guides.rb +2 -2
- data/guides/rails_guides/helpers.rb +1 -1
- data/guides/rails_guides/levenshtein.rb +29 -21
- data/guides/rails_guides/markdown.rb +6 -7
- data/guides/rails_guides/markdown/renderer.rb +1 -1
- data/guides/source/2_3_release_notes.md +3 -3
- data/guides/source/3_0_release_notes.md +4 -4
- data/guides/source/3_1_release_notes.md +2 -2
- data/guides/source/3_2_release_notes.md +2 -2
- data/guides/source/4_1_release_notes.md +8 -9
- data/guides/source/4_2_release_notes.md +572 -0
- data/guides/source/_license.html.erb +1 -1
- data/guides/source/_welcome.html.erb +2 -8
- data/guides/source/action_controller_overview.md +79 -7
- data/guides/source/action_mailer_basics.md +36 -11
- data/guides/source/action_view_overview.md +138 -119
- data/guides/source/active_job_basics.md +253 -0
- data/guides/source/active_model_basics.md +23 -0
- data/guides/source/active_record_basics.md +16 -15
- data/guides/source/active_record_callbacks.md +12 -9
- data/guides/source/{migrations.md → active_record_migrations.md} +90 -217
- data/guides/source/active_record_postgresql.md +437 -0
- data/guides/source/active_record_querying.md +261 -261
- data/guides/source/active_record_validations.md +7 -7
- data/guides/source/active_support_core_extensions.md +105 -44
- data/guides/source/active_support_instrumentation.md +3 -2
- data/guides/source/api_documentation_guidelines.md +62 -16
- data/guides/source/asset_pipeline.md +58 -46
- data/guides/source/association_basics.md +47 -38
- data/guides/source/caching_with_rails.md +31 -6
- data/guides/source/command_line.md +56 -25
- data/guides/source/configuring.md +98 -19
- data/guides/source/contributing_to_ruby_on_rails.md +174 -111
- data/guides/source/credits.html.erb +1 -1
- data/guides/source/debugging_rails_applications.md +438 -284
- data/guides/source/development_dependencies_install.md +17 -4
- data/guides/source/documents.yaml +11 -7
- data/guides/source/engines.md +192 -203
- data/guides/source/form_helpers.md +54 -45
- data/guides/source/generators.md +20 -11
- data/guides/source/getting_started.md +330 -191
- data/guides/source/i18n.md +92 -62
- data/guides/source/index.html.erb +1 -0
- data/guides/source/initialization.md +108 -59
- data/guides/source/layout.html.erb +1 -4
- data/guides/source/layouts_and_rendering.md +24 -23
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +26 -26
- data/guides/source/rails_application_templates.md +21 -3
- data/guides/source/rails_on_rack.md +1 -1
- data/guides/source/routing.md +97 -71
- data/guides/source/ruby_on_rails_guides_guidelines.md +10 -12
- data/guides/source/security.md +39 -33
- data/guides/source/testing.md +111 -108
- data/guides/source/upgrading_ruby_on_rails.md +131 -14
- data/guides/source/working_with_javascript_in_rails.md +18 -16
- data/guides/w3c_validator.rb +2 -0
- metadata +37 -94
- data/guides/bug_report_templates/generic_gem.rb +0 -15
- data/guides/bug_report_templates/generic_master.rb +0 -26
- data/guides/code/getting_started/Gemfile +0 -40
- data/guides/code/getting_started/Gemfile.lock +0 -125
- data/guides/code/getting_started/README.rdoc +0 -28
- data/guides/code/getting_started/Rakefile +0 -6
- data/guides/code/getting_started/app/assets/javascripts/application.js +0 -15
- data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/application.css +0 -13
- data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss +0 -3
- data/guides/code/getting_started/app/controllers/application_controller.rb +0 -5
- data/guides/code/getting_started/app/controllers/comments_controller.rb +0 -23
- data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -53
- data/guides/code/getting_started/app/controllers/welcome_controller.rb +0 -4
- data/guides/code/getting_started/app/helpers/application_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/comments_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/posts_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/welcome_helper.rb +0 -2
- data/guides/code/getting_started/app/models/comment.rb +0 -3
- data/guides/code/getting_started/app/models/post.rb +0 -7
- data/guides/code/getting_started/app/views/comments/_comment.html.erb +0 -15
- data/guides/code/getting_started/app/views/comments/_form.html.erb +0 -13
- data/guides/code/getting_started/app/views/layouts/application.html.erb +0 -14
- data/guides/code/getting_started/app/views/posts/_form.html.erb +0 -27
- data/guides/code/getting_started/app/views/posts/edit.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/index.html.erb +0 -21
- data/guides/code/getting_started/app/views/posts/new.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/show.html.erb +0 -18
- data/guides/code/getting_started/app/views/welcome/index.html.erb +0 -4
- data/guides/code/getting_started/bin/bundle +0 -4
- data/guides/code/getting_started/bin/rails +0 -4
- data/guides/code/getting_started/bin/rake +0 -4
- data/guides/code/getting_started/config.ru +0 -4
- data/guides/code/getting_started/config/application.rb +0 -18
- data/guides/code/getting_started/config/boot.rb +0 -4
- data/guides/code/getting_started/config/database.yml +0 -25
- data/guides/code/getting_started/config/environment.rb +0 -5
- data/guides/code/getting_started/config/environments/development.rb +0 -30
- data/guides/code/getting_started/config/environments/production.rb +0 -80
- data/guides/code/getting_started/config/environments/test.rb +0 -36
- data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +0 -7
- data/guides/code/getting_started/config/initializers/filter_parameter_logging.rb +0 -4
- data/guides/code/getting_started/config/initializers/inflections.rb +0 -16
- data/guides/code/getting_started/config/initializers/locale.rb +0 -9
- data/guides/code/getting_started/config/initializers/mime_types.rb +0 -5
- data/guides/code/getting_started/config/initializers/secret_token.rb +0 -12
- data/guides/code/getting_started/config/initializers/session_store.rb +0 -3
- data/guides/code/getting_started/config/initializers/wrap_parameters.rb +0 -14
- data/guides/code/getting_started/config/locales/en.yml +0 -23
- data/guides/code/getting_started/config/routes.rb +0 -7
- data/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb +0 -10
- data/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb +0 -11
- data/guides/code/getting_started/db/schema.rb +0 -33
- data/guides/code/getting_started/db/seeds.rb +0 -7
- data/guides/code/getting_started/public/404.html +0 -60
- data/guides/code/getting_started/public/422.html +0 -60
- data/guides/code/getting_started/public/500.html +0 -59
- data/guides/code/getting_started/public/favicon.ico +0 -0
- data/guides/code/getting_started/public/robots.txt +0 -5
- data/guides/code/getting_started/test/controllers/comments_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/posts_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/welcome_controller_test.rb +0 -9
- data/guides/code/getting_started/test/fixtures/comments.yml +0 -11
- data/guides/code/getting_started/test/fixtures/posts.yml +0 -9
- data/guides/code/getting_started/test/helpers/comments_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/posts_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/welcome_helper_test.rb +0 -4
- data/guides/code/getting_started/test/models/comment_test.rb +0 -7
- data/guides/code/getting_started/test/models/post_test.rb +0 -7
- data/guides/code/getting_started/test/test_helper.rb +0 -12
@@ -0,0 +1,437 @@
|
|
1
|
+
Active Record and PostgreSQL
|
2
|
+
============================
|
3
|
+
|
4
|
+
This guide covers PostgreSQL specific usage of Active Record.
|
5
|
+
|
6
|
+
After reading this guide, you will know:
|
7
|
+
|
8
|
+
* How to use PostgreSQL's datatypes.
|
9
|
+
* How to use UUID primary keys.
|
10
|
+
* How to implement full text search with PostgreSQL.
|
11
|
+
* How to back your Active Record models with database views.
|
12
|
+
|
13
|
+
--------------------------------------------------------------------------------
|
14
|
+
|
15
|
+
In order to use the PostgreSQL adapter you need to have at least version 8.2
|
16
|
+
installed. Older versions are not supported.
|
17
|
+
|
18
|
+
To get started with PostgreSQL have a look at the
|
19
|
+
[configuring Rails guide](configuring.html#configuring-a-postgresql-database).
|
20
|
+
It describes how to properly setup Active Record for PostgreSQL.
|
21
|
+
|
22
|
+
Datatypes
|
23
|
+
---------
|
24
|
+
|
25
|
+
PostgreSQL offers a number of specific datatypes. Following is a list of types,
|
26
|
+
that are supported by the PostgreSQL adapter.
|
27
|
+
|
28
|
+
### Bytea
|
29
|
+
|
30
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-binary.html)
|
31
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-binarystring.html)
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# db/migrate/20140207133952_create_documents.rb
|
35
|
+
create_table :documents do |t|
|
36
|
+
t.binary 'payload'
|
37
|
+
end
|
38
|
+
|
39
|
+
# app/models/document.rb
|
40
|
+
class Document < ActiveRecord::Base
|
41
|
+
end
|
42
|
+
|
43
|
+
# Usage
|
44
|
+
data = File.read(Rails.root + "tmp/output.pdf")
|
45
|
+
Document.create payload: data
|
46
|
+
```
|
47
|
+
|
48
|
+
### Array
|
49
|
+
|
50
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/arrays.html)
|
51
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-array.html)
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# db/migrate/20140207133952_create_books.rb
|
55
|
+
create_table :books do |t|
|
56
|
+
t.string 'title'
|
57
|
+
t.string 'tags', array: true
|
58
|
+
t.integer 'ratings', array: true
|
59
|
+
end
|
60
|
+
add_index :books, :tags, using: 'gin'
|
61
|
+
add_index :books, :ratings, using: 'gin'
|
62
|
+
|
63
|
+
# app/models/book.rb
|
64
|
+
class Book < ActiveRecord::Base
|
65
|
+
end
|
66
|
+
|
67
|
+
# Usage
|
68
|
+
Book.create title: "Brave New World",
|
69
|
+
tags: ["fantasy", "fiction"],
|
70
|
+
ratings: [4, 5]
|
71
|
+
|
72
|
+
## Books for a single tag
|
73
|
+
Book.where("'fantasy' = ANY (tags)")
|
74
|
+
|
75
|
+
## Books for multiple tags
|
76
|
+
Book.where("tags @> ARRAY[?]::varchar[]", ["fantasy", "fiction"])
|
77
|
+
|
78
|
+
## Books with 3 or more ratings
|
79
|
+
Book.where("array_length(ratings, 1) >= 3")
|
80
|
+
```
|
81
|
+
|
82
|
+
### Hstore
|
83
|
+
|
84
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/hstore.html)
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
# db/migrate/20131009135255_create_profiles.rb
|
88
|
+
ActiveRecord::Schema.define do
|
89
|
+
create_table :profiles do |t|
|
90
|
+
t.hstore 'settings'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# app/models/profile.rb
|
95
|
+
class Profile < ActiveRecord::Base
|
96
|
+
end
|
97
|
+
|
98
|
+
# Usage
|
99
|
+
Profile.create(settings: { "color" => "blue", "resolution" => "800x600" })
|
100
|
+
|
101
|
+
profile = Profile.first
|
102
|
+
profile.settings # => {"color"=>"blue", "resolution"=>"800x600"}
|
103
|
+
|
104
|
+
profile.settings = {"color" => "yellow", "resolution" => "1280x1024"}
|
105
|
+
profile.save!
|
106
|
+
|
107
|
+
## you need to call _will_change! if you are editing the store in place
|
108
|
+
profile.settings["color"] = "green"
|
109
|
+
profile.settings_will_change!
|
110
|
+
profile.save!
|
111
|
+
```
|
112
|
+
|
113
|
+
### JSON
|
114
|
+
|
115
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-json.html)
|
116
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-json.html)
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
# db/migrate/20131220144913_create_events.rb
|
120
|
+
create_table :events do |t|
|
121
|
+
t.json 'payload'
|
122
|
+
end
|
123
|
+
|
124
|
+
# app/models/event.rb
|
125
|
+
class Event < ActiveRecord::Base
|
126
|
+
end
|
127
|
+
|
128
|
+
# Usage
|
129
|
+
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})
|
130
|
+
|
131
|
+
event = Event.first
|
132
|
+
event.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]}
|
133
|
+
|
134
|
+
## Query based on JSON document
|
135
|
+
Event.where("payload->'kind' = ?", "user_renamed")
|
136
|
+
```
|
137
|
+
|
138
|
+
### Range Types
|
139
|
+
|
140
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/rangetypes.html)
|
141
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-range.html)
|
142
|
+
|
143
|
+
This type is mapped to Ruby [`Range`](http://www.ruby-doc.org/core-2.1.1/Range.html) objects.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
# db/migrate/20130923065404_create_events.rb
|
147
|
+
create_table :events do |t|
|
148
|
+
t.daterange 'duration'
|
149
|
+
end
|
150
|
+
|
151
|
+
# app/models/event.rb
|
152
|
+
class Event < ActiveRecord::Base
|
153
|
+
end
|
154
|
+
|
155
|
+
# Usage
|
156
|
+
Event.create(duration: Date.new(2014, 2, 11)..Date.new(2014, 2, 12))
|
157
|
+
|
158
|
+
event = Event.first
|
159
|
+
event.duration # => Tue, 11 Feb 2014...Thu, 13 Feb 2014
|
160
|
+
|
161
|
+
## All Events on a given date
|
162
|
+
Event.where("duration @> ?::date", Date.new(2014, 2, 12))
|
163
|
+
|
164
|
+
## Working with range bounds
|
165
|
+
event = Event.
|
166
|
+
select("lower(duration) AS starts_at").
|
167
|
+
select("upper(duration) AS ends_at").first
|
168
|
+
|
169
|
+
event.starts_at # => Tue, 11 Feb 2014
|
170
|
+
event.ends_at # => Thu, 13 Feb 2014
|
171
|
+
```
|
172
|
+
|
173
|
+
### Composite Types
|
174
|
+
|
175
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/rowtypes.html)
|
176
|
+
|
177
|
+
Currently there is no special support for composite types. They are mapped to
|
178
|
+
normal text columns:
|
179
|
+
|
180
|
+
```sql
|
181
|
+
CREATE TYPE full_address AS
|
182
|
+
(
|
183
|
+
city VARCHAR(90),
|
184
|
+
street VARCHAR(90)
|
185
|
+
);
|
186
|
+
```
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
# db/migrate/20140207133952_create_contacts.rb
|
190
|
+
execute <<-SQL
|
191
|
+
CREATE TYPE full_address AS
|
192
|
+
(
|
193
|
+
city VARCHAR(90),
|
194
|
+
street VARCHAR(90)
|
195
|
+
);
|
196
|
+
SQL
|
197
|
+
create_table :contacts do |t|
|
198
|
+
t.column :address, :full_address
|
199
|
+
end
|
200
|
+
|
201
|
+
# app/models/contact.rb
|
202
|
+
class Contact < ActiveRecord::Base
|
203
|
+
end
|
204
|
+
|
205
|
+
# Usage
|
206
|
+
Contact.create address: "(Paris,Champs-Élysées)"
|
207
|
+
contact = Contact.first
|
208
|
+
contact.address # => "(Paris,Champs-Élysées)"
|
209
|
+
contact.address = "(Paris,Rue Basse)"
|
210
|
+
contact.save!
|
211
|
+
```
|
212
|
+
|
213
|
+
### Enumerated Types
|
214
|
+
|
215
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-enum.html)
|
216
|
+
|
217
|
+
Currently there is no special support for enumerated types. They are mapped as
|
218
|
+
normal text columns:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
# db/migrate/20131220144913_create_events.rb
|
222
|
+
execute <<-SQL
|
223
|
+
CREATE TYPE article_status AS ENUM ('draft', 'published');
|
224
|
+
SQL
|
225
|
+
create_table :articles do |t|
|
226
|
+
t.column :status, :article_status
|
227
|
+
end
|
228
|
+
|
229
|
+
# app/models/article.rb
|
230
|
+
class Article < ActiveRecord::Base
|
231
|
+
end
|
232
|
+
|
233
|
+
# Usage
|
234
|
+
Article.create status: "draft"
|
235
|
+
article = Article.first
|
236
|
+
article.status # => "draft"
|
237
|
+
|
238
|
+
article.status = "published"
|
239
|
+
article.save!
|
240
|
+
```
|
241
|
+
|
242
|
+
### UUID
|
243
|
+
|
244
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-uuid.html)
|
245
|
+
* [generator functions](http://www.postgresql.org/docs/9.3/static/uuid-ossp.html)
|
246
|
+
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
# db/migrate/20131220144913_create_revisions.rb
|
250
|
+
create_table :revisions do |t|
|
251
|
+
t.column :identifier, :uuid
|
252
|
+
end
|
253
|
+
|
254
|
+
# app/models/revision.rb
|
255
|
+
class Revision < ActiveRecord::Base
|
256
|
+
end
|
257
|
+
|
258
|
+
# Usage
|
259
|
+
Revision.create identifier: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11"
|
260
|
+
|
261
|
+
revision = Revision.first
|
262
|
+
revision.identifier # => "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
|
263
|
+
```
|
264
|
+
|
265
|
+
### Bit String Types
|
266
|
+
|
267
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-bit.html)
|
268
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-bitstring.html)
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# db/migrate/20131220144913_create_users.rb
|
272
|
+
create_table :users, force: true do |t|
|
273
|
+
t.column :settings, "bit(8)"
|
274
|
+
end
|
275
|
+
|
276
|
+
# app/models/device.rb
|
277
|
+
class User < ActiveRecord::Base
|
278
|
+
end
|
279
|
+
|
280
|
+
# Usage
|
281
|
+
User.create settings: "01010011"
|
282
|
+
user = User.first
|
283
|
+
user.settings # => "(Paris,Champs-Élysées)"
|
284
|
+
user.settings = "0xAF"
|
285
|
+
user.settings # => 10101111
|
286
|
+
user.save!
|
287
|
+
```
|
288
|
+
|
289
|
+
### Network Address Types
|
290
|
+
|
291
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-net-types.html)
|
292
|
+
|
293
|
+
The types `inet` and `cidr` are mapped to Ruby
|
294
|
+
[`IPAddr`](http://www.ruby-doc.org/stdlib-2.1.1/libdoc/ipaddr/rdoc/IPAddr.html)
|
295
|
+
objects. The `macaddr` type is mapped to normal text.
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
# db/migrate/20140508144913_create_devices.rb
|
299
|
+
create_table(:devices, force: true) do |t|
|
300
|
+
t.inet 'ip'
|
301
|
+
t.cidr 'network'
|
302
|
+
t.macaddr 'address'
|
303
|
+
end
|
304
|
+
|
305
|
+
# app/models/device.rb
|
306
|
+
class Device < ActiveRecord::Base
|
307
|
+
end
|
308
|
+
|
309
|
+
# Usage
|
310
|
+
macbook = Device.create(ip: "192.168.1.12",
|
311
|
+
network: "192.168.2.0/24",
|
312
|
+
address: "32:01:16:6d:05:ef")
|
313
|
+
|
314
|
+
macbook.ip
|
315
|
+
# => #<IPAddr: IPv4:192.168.1.12/255.255.255.255>
|
316
|
+
|
317
|
+
macbook.network
|
318
|
+
# => #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
319
|
+
|
320
|
+
macbook.address
|
321
|
+
# => "32:01:16:6d:05:ef"
|
322
|
+
```
|
323
|
+
|
324
|
+
### Geometric Types
|
325
|
+
|
326
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-geometric.html)
|
327
|
+
|
328
|
+
All geometric types, with the exception of `points` are mapped to normal text.
|
329
|
+
A point is casted to an array containing `x` and `y` coordinates.
|
330
|
+
|
331
|
+
|
332
|
+
UUID Primary Keys
|
333
|
+
-----------------
|
334
|
+
|
335
|
+
NOTE: you need to enable the `uuid-ossp` extension to generate UUIDs.
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
# db/migrate/20131220144913_create_devices.rb
|
339
|
+
enable_extension 'uuid-ossp' unless extension_enabled?('uuid-ossp')
|
340
|
+
create_table :devices, id: :uuid, default: 'uuid_generate_v4()' do |t|
|
341
|
+
t.string :kind
|
342
|
+
end
|
343
|
+
|
344
|
+
# app/models/device.rb
|
345
|
+
class Device < ActiveRecord::Base
|
346
|
+
end
|
347
|
+
|
348
|
+
# Usage
|
349
|
+
device = Device.create
|
350
|
+
device.id # => "814865cd-5a1d-4771-9306-4268f188fe9e"
|
351
|
+
```
|
352
|
+
|
353
|
+
Full Text Search
|
354
|
+
----------------
|
355
|
+
|
356
|
+
```ruby
|
357
|
+
# db/migrate/20131220144913_create_documents.rb
|
358
|
+
create_table :documents do |t|
|
359
|
+
t.string 'title'
|
360
|
+
t.string 'body'
|
361
|
+
end
|
362
|
+
|
363
|
+
execute "CREATE INDEX documents_idx ON documents USING gin(to_tsvector('english', title || ' ' || body));"
|
364
|
+
|
365
|
+
# app/models/document.rb
|
366
|
+
class Document < ActiveRecord::Base
|
367
|
+
end
|
368
|
+
|
369
|
+
# Usage
|
370
|
+
Document.create(title: "Cats and Dogs", body: "are nice!")
|
371
|
+
|
372
|
+
## all documents matching 'cat & dog'
|
373
|
+
Document.where("to_tsvector('english', title || ' ' || body) @@ to_tsquery(?)",
|
374
|
+
"cat & dog")
|
375
|
+
```
|
376
|
+
|
377
|
+
Database Views
|
378
|
+
--------------
|
379
|
+
|
380
|
+
* [view creation](http://www.postgresql.org/docs/9.3/static/sql-createview.html)
|
381
|
+
|
382
|
+
Imagine you need to work with a legacy database containing the following table:
|
383
|
+
|
384
|
+
```
|
385
|
+
rails_pg_guide=# \d "TBL_ART"
|
386
|
+
Table "public.TBL_ART"
|
387
|
+
Column | Type | Modifiers
|
388
|
+
------------+-----------------------------+------------------------------------------------------------
|
389
|
+
INT_ID | integer | not null default nextval('"TBL_ART_INT_ID_seq"'::regclass)
|
390
|
+
STR_TITLE | character varying |
|
391
|
+
STR_STAT | character varying | default 'draft'::character varying
|
392
|
+
DT_PUBL_AT | timestamp without time zone |
|
393
|
+
BL_ARCH | boolean | default false
|
394
|
+
Indexes:
|
395
|
+
"TBL_ART_pkey" PRIMARY KEY, btree ("INT_ID")
|
396
|
+
```
|
397
|
+
|
398
|
+
This table does not follow the Rails conventions at all.
|
399
|
+
Because simple PostgreSQL views are updateable by default,
|
400
|
+
we can wrap it as follows:
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
# db/migrate/20131220144913_create_articles_view.rb
|
404
|
+
execute <<-SQL
|
405
|
+
CREATE VIEW articles AS
|
406
|
+
SELECT "INT_ID" AS id,
|
407
|
+
"STR_TITLE" AS title,
|
408
|
+
"STR_STAT" AS status,
|
409
|
+
"DT_PUBL_AT" AS published_at,
|
410
|
+
"BL_ARCH" AS archived
|
411
|
+
FROM "TBL_ART"
|
412
|
+
WHERE "BL_ARCH" = 'f'
|
413
|
+
SQL
|
414
|
+
|
415
|
+
# app/models/article.rb
|
416
|
+
class Article < ActiveRecord::Base
|
417
|
+
self.primary_key = "id"
|
418
|
+
def archive!
|
419
|
+
update_attribute :archived, true
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# Usage
|
424
|
+
first = Article.create! title: "Winter is coming",
|
425
|
+
status: "published",
|
426
|
+
published_at: 1.year.ago
|
427
|
+
second = Article.create! title: "Brace yourself",
|
428
|
+
status: "draft",
|
429
|
+
published_at: 1.month.ago
|
430
|
+
|
431
|
+
Article.count # => 1
|
432
|
+
first.archive!
|
433
|
+
Article.count # => 2
|
434
|
+
```
|
435
|
+
|
436
|
+
NOTE: This application only cares about non-archived `Articles`. A view also
|
437
|
+
allows for conditions so we can exclude the archived `Articles` directly.
|
@@ -93,9 +93,9 @@ The primary operation of `Model.find(options)` can be summarized as:
|
|
93
93
|
|
94
94
|
Active Record provides several different ways of retrieving a single object.
|
95
95
|
|
96
|
-
####
|
96
|
+
#### `find`
|
97
97
|
|
98
|
-
Using `
|
98
|
+
Using the `find` method, you can retrieve the object corresponding to the specified _primary key_ that matches any supplied options. For example:
|
99
99
|
|
100
100
|
```ruby
|
101
101
|
# Find the client with primary key (id) 10.
|
@@ -109,119 +109,103 @@ The SQL equivalent of the above is:
|
|
109
109
|
SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1
|
110
110
|
```
|
111
111
|
|
112
|
-
`
|
112
|
+
The `find` method will raise an `ActiveRecord::RecordNotFound` exception if no matching record is found.
|
113
113
|
|
114
|
-
|
115
|
-
|
116
|
-
`Model.take` retrieves a record without any implicit ordering. For example:
|
114
|
+
You can also use this method to query for multiple objects. Call the `find` method and pass in an array of primary keys. The return will be an array containing all of the matching records for the supplied _primary keys_. For example:
|
117
115
|
|
118
116
|
```ruby
|
119
|
-
|
120
|
-
|
117
|
+
# Find the clients with primary keys 1 and 10.
|
118
|
+
client = Client.find([1, 10]) # Or even Client.find(1, 10)
|
119
|
+
# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]
|
121
120
|
```
|
122
121
|
|
123
122
|
The SQL equivalent of the above is:
|
124
123
|
|
125
124
|
```sql
|
126
|
-
SELECT * FROM clients
|
125
|
+
SELECT * FROM clients WHERE (clients.id IN (1,10))
|
127
126
|
```
|
128
127
|
|
129
|
-
`
|
130
|
-
|
131
|
-
TIP: The retrieved record may vary depending on the database engine.
|
128
|
+
WARNING: The `find` method will raise an `ActiveRecord::RecordNotFound` exception unless a matching record is found for **all** of the supplied primary keys.
|
132
129
|
|
133
|
-
#### `
|
130
|
+
#### `take`
|
134
131
|
|
135
|
-
`
|
132
|
+
The `take` method retrieves a record without any implicit ordering. For example:
|
136
133
|
|
137
134
|
```ruby
|
138
|
-
client = Client.
|
135
|
+
client = Client.take
|
139
136
|
# => #<Client id: 1, first_name: "Lifo">
|
140
137
|
```
|
141
138
|
|
142
139
|
The SQL equivalent of the above is:
|
143
140
|
|
144
141
|
```sql
|
145
|
-
SELECT * FROM clients
|
142
|
+
SELECT * FROM clients LIMIT 1
|
146
143
|
```
|
147
144
|
|
148
|
-
`
|
145
|
+
The `take` method returns `nil` if no record is found and no exception will be raised.
|
149
146
|
|
150
|
-
|
151
|
-
|
152
|
-
`Model.last` finds the last record ordered by the primary key. For example:
|
147
|
+
You can pass in a numerical argument to the `take` method to return up to that number of results. For example
|
153
148
|
|
154
149
|
```ruby
|
155
|
-
client = Client.
|
156
|
-
# =>
|
150
|
+
client = Client.take(2)
|
151
|
+
# => [
|
152
|
+
#<Client id: 1, first_name: "Lifo">,
|
153
|
+
#<Client id: 220, first_name: "Sara">
|
154
|
+
]
|
157
155
|
```
|
158
156
|
|
159
157
|
The SQL equivalent of the above is:
|
160
158
|
|
161
159
|
```sql
|
162
|
-
SELECT * FROM clients
|
163
|
-
```
|
164
|
-
|
165
|
-
`Model.last` returns `nil` if no matching record is found and no exception will be raised.
|
166
|
-
|
167
|
-
#### `find_by`
|
168
|
-
|
169
|
-
`Model.find_by` finds the first record matching some conditions. For example:
|
170
|
-
|
171
|
-
```ruby
|
172
|
-
Client.find_by first_name: 'Lifo'
|
173
|
-
# => #<Client id: 1, first_name: "Lifo">
|
174
|
-
|
175
|
-
Client.find_by first_name: 'Jon'
|
176
|
-
# => nil
|
160
|
+
SELECT * FROM clients LIMIT 2
|
177
161
|
```
|
178
162
|
|
179
|
-
|
163
|
+
The `take!` method behaves exactly like `take`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found.
|
180
164
|
|
181
|
-
|
182
|
-
Client.where(first_name: 'Lifo').take
|
183
|
-
```
|
165
|
+
TIP: The retrieved record may vary depending on the database engine.
|
184
166
|
|
185
|
-
#### `
|
167
|
+
#### `first`
|
186
168
|
|
187
|
-
`
|
169
|
+
The `first` method finds the first record ordered by the primary key. For example:
|
188
170
|
|
189
171
|
```ruby
|
190
|
-
client = Client.
|
172
|
+
client = Client.first
|
191
173
|
# => #<Client id: 1, first_name: "Lifo">
|
192
174
|
```
|
193
175
|
|
194
176
|
The SQL equivalent of the above is:
|
195
177
|
|
196
178
|
```sql
|
197
|
-
SELECT * FROM clients LIMIT 1
|
179
|
+
SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1
|
198
180
|
```
|
199
181
|
|
200
|
-
`
|
201
|
-
|
202
|
-
#### `first!`
|
182
|
+
The `first` method returns `nil` if no matching record is found and no exception will be raised.
|
203
183
|
|
204
|
-
|
184
|
+
You can pass in a numerical argument to the `first` method to return up to that number of results. For example
|
205
185
|
|
206
186
|
```ruby
|
207
|
-
client = Client.first
|
208
|
-
# =>
|
187
|
+
client = Client.first(3)
|
188
|
+
# => [
|
189
|
+
#<Client id: 1, first_name: "Lifo">,
|
190
|
+
#<Client id: 2, first_name: "Fifo">,
|
191
|
+
#<Client id: 3, first_name: "Filo">
|
192
|
+
]
|
209
193
|
```
|
210
194
|
|
211
195
|
The SQL equivalent of the above is:
|
212
196
|
|
213
197
|
```sql
|
214
|
-
SELECT * FROM clients ORDER BY clients.id ASC LIMIT
|
198
|
+
SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3
|
215
199
|
```
|
216
200
|
|
217
|
-
`
|
201
|
+
The `first!` method behaves exactly like `first`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found.
|
218
202
|
|
219
|
-
#### `last
|
203
|
+
#### `last`
|
220
204
|
|
221
|
-
`
|
205
|
+
The `last` method finds the last record ordered by the primary key. For example:
|
222
206
|
|
223
207
|
```ruby
|
224
|
-
client = Client.last
|
208
|
+
client = Client.last
|
225
209
|
# => #<Client id: 221, first_name: "Russel">
|
226
210
|
```
|
227
211
|
|
@@ -231,92 +215,56 @@ The SQL equivalent of the above is:
|
|
231
215
|
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
|
232
216
|
```
|
233
217
|
|
234
|
-
`
|
235
|
-
|
236
|
-
#### `find_by!`
|
237
|
-
|
238
|
-
`Model.find_by!` finds the first record matching some conditions. It raises `ActiveRecord::RecordNotFound` if no matching record is found. For example:
|
239
|
-
|
240
|
-
```ruby
|
241
|
-
Client.find_by! first_name: 'Lifo'
|
242
|
-
# => #<Client id: 1, first_name: "Lifo">
|
243
|
-
|
244
|
-
Client.find_by! first_name: 'Jon'
|
245
|
-
# => ActiveRecord::RecordNotFound
|
246
|
-
```
|
247
|
-
|
248
|
-
It is equivalent to writing:
|
249
|
-
|
250
|
-
```ruby
|
251
|
-
Client.where(first_name: 'Lifo').take!
|
252
|
-
```
|
253
|
-
|
254
|
-
### Retrieving Multiple Objects
|
255
|
-
|
256
|
-
#### Using Multiple Primary Keys
|
218
|
+
The `last` method returns `nil` if no matching record is found and no exception will be raised.
|
257
219
|
|
258
|
-
|
220
|
+
You can pass in a numerical argument to the `last` method to return up to that number of results. For example
|
259
221
|
|
260
222
|
```ruby
|
261
|
-
|
262
|
-
|
263
|
-
|
223
|
+
client = Client.last(3)
|
224
|
+
# => [
|
225
|
+
#<Client id: 219, first_name: "James">,
|
226
|
+
#<Client id: 220, first_name: "Sara">,
|
227
|
+
#<Client id: 221, first_name: "Russel">
|
228
|
+
]
|
264
229
|
```
|
265
230
|
|
266
231
|
The SQL equivalent of the above is:
|
267
232
|
|
268
233
|
```sql
|
269
|
-
SELECT * FROM clients
|
234
|
+
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3
|
270
235
|
```
|
271
236
|
|
272
|
-
|
237
|
+
The `last!` method behaves exactly like `last`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found.
|
273
238
|
|
274
|
-
####
|
239
|
+
#### `find_by`
|
275
240
|
|
276
|
-
`
|
241
|
+
The `find_by` method finds the first record matching some conditions. For example:
|
277
242
|
|
278
243
|
```ruby
|
279
|
-
Client.
|
280
|
-
# =>
|
281
|
-
#<Client id: 2, first_name: "Raf">]
|
282
|
-
```
|
283
|
-
|
284
|
-
The SQL equivalent of the above is:
|
244
|
+
Client.find_by first_name: 'Lifo'
|
245
|
+
# => #<Client id: 1, first_name: "Lifo">
|
285
246
|
|
286
|
-
|
287
|
-
|
247
|
+
Client.find_by first_name: 'Jon'
|
248
|
+
# => nil
|
288
249
|
```
|
289
250
|
|
290
|
-
|
291
|
-
|
292
|
-
`Model.first(limit)` finds the first number of records specified by `limit` ordered by primary key:
|
251
|
+
It is equivalent to writing:
|
293
252
|
|
294
253
|
```ruby
|
295
|
-
Client.
|
296
|
-
# => [#<Client id: 1, first_name: "Lifo">,
|
297
|
-
#<Client id: 2, first_name: "Raf">]
|
298
|
-
```
|
299
|
-
|
300
|
-
The SQL equivalent of the above is:
|
301
|
-
|
302
|
-
```sql
|
303
|
-
SELECT * FROM clients ORDER BY id ASC LIMIT 2
|
254
|
+
Client.where(first_name: 'Lifo').take
|
304
255
|
```
|
305
256
|
|
306
|
-
|
307
|
-
|
308
|
-
`Model.last(limit)` finds the number of records specified by `limit` ordered by primary key in descending order:
|
257
|
+
The `find_by!` method behaves exactly like `find_by`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found. For example:
|
309
258
|
|
310
259
|
```ruby
|
311
|
-
Client.
|
312
|
-
# =>
|
313
|
-
#<Client id: 9, first_name: "John">]
|
260
|
+
Client.find_by! first_name: 'does not exist'
|
261
|
+
# => ActiveRecord::RecordNotFound
|
314
262
|
```
|
315
263
|
|
316
|
-
|
264
|
+
This is equivalent to writing:
|
317
265
|
|
318
|
-
```
|
319
|
-
|
266
|
+
```ruby
|
267
|
+
Client.where(first_name: 'does not exist').take!
|
320
268
|
```
|
321
269
|
|
322
270
|
### Retrieving Multiple Objects in Batches
|
@@ -328,7 +276,7 @@ This may appear straightforward:
|
|
328
276
|
```ruby
|
329
277
|
# This is very inefficient when the users table has thousands of rows.
|
330
278
|
User.all.each do |user|
|
331
|
-
|
279
|
+
NewsMailer.weekly(user).deliver
|
332
280
|
end
|
333
281
|
```
|
334
282
|
|
@@ -344,7 +292,15 @@ The `find_each` method retrieves a batch of records and then yields _each_ recor
|
|
344
292
|
|
345
293
|
```ruby
|
346
294
|
User.find_each do |user|
|
347
|
-
|
295
|
+
NewsMailer.weekly(user).deliver
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
To add conditions to a `find_each` operation you can chain other Active Record methods such as `where`:
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
User.where(weekly_subscriber: true).find_each do |user|
|
303
|
+
NewsMailer.weekly(user).deliver
|
348
304
|
end
|
349
305
|
```
|
350
306
|
|
@@ -360,7 +316,7 @@ The `:batch_size` option allows you to specify the number of records to be retri
|
|
360
316
|
|
361
317
|
```ruby
|
362
318
|
User.find_each(batch_size: 5000) do |user|
|
363
|
-
|
319
|
+
NewsMailer.weekly(user).deliver
|
364
320
|
end
|
365
321
|
```
|
366
322
|
|
@@ -372,7 +328,7 @@ For example, to send newsletters only to users with the primary key starting fro
|
|
372
328
|
|
373
329
|
```ruby
|
374
330
|
User.find_each(start: 2000, batch_size: 5000) do |user|
|
375
|
-
|
331
|
+
NewsMailer.weekly(user).deliver
|
376
332
|
end
|
377
333
|
```
|
378
334
|
|
@@ -472,8 +428,8 @@ Client.where('locked' => true)
|
|
472
428
|
In the case of a belongs_to relationship, an association key can be used to specify the model if an Active Record object is used as the value. This method works with polymorphic relationships as well.
|
473
429
|
|
474
430
|
```ruby
|
475
|
-
|
476
|
-
Author.joins(:
|
431
|
+
Article.where(author: author)
|
432
|
+
Author.joins(:articles).where(articles: { author: author })
|
477
433
|
```
|
478
434
|
|
479
435
|
NOTE: The values cannot be symbols. For example, you cannot do `Client.where(status: :active)`.
|
@@ -511,7 +467,7 @@ SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
|
|
511
467
|
`NOT` SQL queries can be built by `where.not`.
|
512
468
|
|
513
469
|
```ruby
|
514
|
-
|
470
|
+
Article.where.not(author: author)
|
515
471
|
```
|
516
472
|
|
517
473
|
In other words, this query can be generated by calling `where` with no argument, then immediately chain with `not` passing `where` conditions.
|
@@ -659,6 +615,23 @@ FROM orders
|
|
659
615
|
GROUP BY date(created_at)
|
660
616
|
```
|
661
617
|
|
618
|
+
### Total of grouped items
|
619
|
+
|
620
|
+
To get the total of grouped items on a single query call `count` after the `group`.
|
621
|
+
|
622
|
+
```ruby
|
623
|
+
Order.group(:status).count
|
624
|
+
# => { 'awaiting_approval' => 7, 'paid' => 12 }
|
625
|
+
```
|
626
|
+
|
627
|
+
The SQL that would be executed would be something like this:
|
628
|
+
|
629
|
+
```sql
|
630
|
+
SELECT COUNT (*) AS count_all, status AS status
|
631
|
+
FROM "orders"
|
632
|
+
GROUP BY status
|
633
|
+
```
|
634
|
+
|
662
635
|
Having
|
663
636
|
------
|
664
637
|
|
@@ -690,32 +663,32 @@ Overriding Conditions
|
|
690
663
|
You can specify certain conditions to be removed using the `unscope` method. For example:
|
691
664
|
|
692
665
|
```ruby
|
693
|
-
|
666
|
+
Article.where('id > 10').limit(20).order('id asc').unscope(:order)
|
694
667
|
```
|
695
668
|
|
696
669
|
The SQL that would be executed:
|
697
670
|
|
698
671
|
```sql
|
699
|
-
SELECT * FROM
|
672
|
+
SELECT * FROM articles WHERE id > 10 LIMIT 20
|
700
673
|
|
701
674
|
# Original query without `unscope`
|
702
|
-
SELECT * FROM
|
675
|
+
SELECT * FROM articles WHERE id > 10 ORDER BY id asc LIMIT 20
|
703
676
|
|
704
677
|
```
|
705
678
|
|
706
|
-
You can
|
679
|
+
You can also unscope specific `where` clauses. For example:
|
707
680
|
|
708
681
|
```ruby
|
709
|
-
|
710
|
-
# SELECT "
|
682
|
+
Article.where(id: 10, trashed: false).unscope(where: :id)
|
683
|
+
# SELECT "articles".* FROM "articles" WHERE trashed = 0
|
711
684
|
```
|
712
685
|
|
713
686
|
A relation which has used `unscope` will affect any relation it is
|
714
687
|
merged in to:
|
715
688
|
|
716
689
|
```ruby
|
717
|
-
|
718
|
-
# SELECT "
|
690
|
+
Article.order('id asc').merge(Article.unscope(:order))
|
691
|
+
# SELECT "articles".* FROM "articles"
|
719
692
|
```
|
720
693
|
|
721
694
|
### `only`
|
@@ -723,16 +696,16 @@ Post.order('id asc').merge(Post.unscope(:order))
|
|
723
696
|
You can also override conditions using the `only` method. For example:
|
724
697
|
|
725
698
|
```ruby
|
726
|
-
|
699
|
+
Article.where('id > 10').limit(20).order('id desc').only(:order, :where)
|
727
700
|
```
|
728
701
|
|
729
702
|
The SQL that would be executed:
|
730
703
|
|
731
704
|
```sql
|
732
|
-
SELECT * FROM
|
705
|
+
SELECT * FROM articles WHERE id > 10 ORDER BY id DESC
|
733
706
|
|
734
707
|
# Original query without `only`
|
735
|
-
SELECT "
|
708
|
+
SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20
|
736
709
|
|
737
710
|
```
|
738
711
|
|
@@ -741,27 +714,25 @@ SELECT "posts".* FROM "posts" WHERE (id > 10) ORDER BY id desc LIMIT 20
|
|
741
714
|
The `reorder` method overrides the default scope order. For example:
|
742
715
|
|
743
716
|
```ruby
|
744
|
-
class
|
745
|
-
..
|
746
|
-
..
|
717
|
+
class Article < ActiveRecord::Base
|
747
718
|
has_many :comments, -> { order('posted_at DESC') }
|
748
719
|
end
|
749
720
|
|
750
|
-
|
721
|
+
Article.find(10).comments.reorder('name')
|
751
722
|
```
|
752
723
|
|
753
724
|
The SQL that would be executed:
|
754
725
|
|
755
726
|
```sql
|
756
|
-
SELECT * FROM
|
757
|
-
SELECT * FROM comments WHERE
|
727
|
+
SELECT * FROM articles WHERE id = 10
|
728
|
+
SELECT * FROM comments WHERE article_id = 10 ORDER BY name
|
758
729
|
```
|
759
730
|
|
760
731
|
In case the `reorder` clause is not used, the SQL executed would be:
|
761
732
|
|
762
733
|
```sql
|
763
|
-
SELECT * FROM
|
764
|
-
SELECT * FROM comments WHERE
|
734
|
+
SELECT * FROM articles WHERE id = 10
|
735
|
+
SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC
|
765
736
|
```
|
766
737
|
|
767
738
|
### `reverse_order`
|
@@ -797,25 +768,25 @@ This method accepts **no** arguments.
|
|
797
768
|
The `rewhere` method overrides an existing, named where condition. For example:
|
798
769
|
|
799
770
|
```ruby
|
800
|
-
|
771
|
+
Article.where(trashed: true).rewhere(trashed: false)
|
801
772
|
```
|
802
773
|
|
803
774
|
The SQL that would be executed:
|
804
775
|
|
805
776
|
```sql
|
806
|
-
SELECT * FROM
|
777
|
+
SELECT * FROM articles WHERE `trashed` = 0
|
807
778
|
```
|
808
779
|
|
809
780
|
In case the `rewhere` clause is not used,
|
810
781
|
|
811
782
|
```ruby
|
812
|
-
|
783
|
+
Article.where(trashed: true).where(trashed: false)
|
813
784
|
```
|
814
785
|
|
815
786
|
the SQL executed would be:
|
816
787
|
|
817
788
|
```sql
|
818
|
-
SELECT * FROM
|
789
|
+
SELECT * FROM articles WHERE `trashed` = 1 AND `trashed` = 0
|
819
790
|
```
|
820
791
|
|
821
792
|
Null Relation
|
@@ -824,21 +795,21 @@ Null Relation
|
|
824
795
|
The `none` method returns a chainable relation with no records. Any subsequent conditions chained to the returned relation will continue generating empty relations. This is useful in scenarios where you need a chainable response to a method or a scope that could return zero results.
|
825
796
|
|
826
797
|
```ruby
|
827
|
-
|
798
|
+
Article.none # returns an empty Relation and fires no queries.
|
828
799
|
```
|
829
800
|
|
830
801
|
```ruby
|
831
|
-
# The
|
832
|
-
@
|
802
|
+
# The visible_articles method below is expected to return a Relation.
|
803
|
+
@articles = current_user.visible_articles.where(name: params[:name])
|
833
804
|
|
834
|
-
def
|
805
|
+
def visible_articles
|
835
806
|
case role
|
836
807
|
when 'Country Manager'
|
837
|
-
|
808
|
+
Article.where(country: country)
|
838
809
|
when 'Reviewer'
|
839
|
-
|
810
|
+
Article.published
|
840
811
|
when 'Bad User'
|
841
|
-
|
812
|
+
Article.none # => returning [] or nil breaks the caller code in this case
|
842
813
|
end
|
843
814
|
end
|
844
815
|
```
|
@@ -963,23 +934,23 @@ SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id =
|
|
963
934
|
|
964
935
|
WARNING: This method only works with `INNER JOIN`.
|
965
936
|
|
966
|
-
Active Record lets you use the names of the [associations](association_basics.html) defined on the model as a shortcut for specifying `JOIN`
|
937
|
+
Active Record lets you use the names of the [associations](association_basics.html) defined on the model as a shortcut for specifying `JOIN` clauses for those associations when using the `joins` method.
|
967
938
|
|
968
|
-
For example, consider the following `Category`, `
|
939
|
+
For example, consider the following `Category`, `Article`, `Comment`, `Guest` and `Tag` models:
|
969
940
|
|
970
941
|
```ruby
|
971
942
|
class Category < ActiveRecord::Base
|
972
|
-
has_many :
|
943
|
+
has_many :articles
|
973
944
|
end
|
974
945
|
|
975
|
-
class
|
946
|
+
class Article < ActiveRecord::Base
|
976
947
|
belongs_to :category
|
977
948
|
has_many :comments
|
978
949
|
has_many :tags
|
979
950
|
end
|
980
951
|
|
981
952
|
class Comment < ActiveRecord::Base
|
982
|
-
belongs_to :
|
953
|
+
belongs_to :article
|
983
954
|
has_one :guest
|
984
955
|
end
|
985
956
|
|
@@ -988,7 +959,7 @@ class Guest < ActiveRecord::Base
|
|
988
959
|
end
|
989
960
|
|
990
961
|
class Tag < ActiveRecord::Base
|
991
|
-
belongs_to :
|
962
|
+
belongs_to :article
|
992
963
|
end
|
993
964
|
```
|
994
965
|
|
@@ -997,64 +968,64 @@ Now all of the following will produce the expected join queries using `INNER JOI
|
|
997
968
|
#### Joining a Single Association
|
998
969
|
|
999
970
|
```ruby
|
1000
|
-
Category.joins(:
|
971
|
+
Category.joins(:articles)
|
1001
972
|
```
|
1002
973
|
|
1003
974
|
This produces:
|
1004
975
|
|
1005
976
|
```sql
|
1006
977
|
SELECT categories.* FROM categories
|
1007
|
-
INNER JOIN
|
978
|
+
INNER JOIN articles ON articles.category_id = categories.id
|
1008
979
|
```
|
1009
980
|
|
1010
|
-
Or, in English: "return a Category object for all categories with
|
981
|
+
Or, in English: "return a Category object for all categories with articles". Note that you will see duplicate categories if more than one article has the same category. If you want unique categories, you can use `Category.joins(:articles).uniq`.
|
1011
982
|
|
1012
983
|
#### Joining Multiple Associations
|
1013
984
|
|
1014
985
|
```ruby
|
1015
|
-
|
986
|
+
Article.joins(:category, :comments)
|
1016
987
|
```
|
1017
988
|
|
1018
989
|
This produces:
|
1019
990
|
|
1020
991
|
```sql
|
1021
|
-
SELECT
|
1022
|
-
INNER JOIN categories ON
|
1023
|
-
INNER JOIN comments ON comments.
|
992
|
+
SELECT articles.* FROM articles
|
993
|
+
INNER JOIN categories ON articles.category_id = categories.id
|
994
|
+
INNER JOIN comments ON comments.article_id = articles.id
|
1024
995
|
```
|
1025
996
|
|
1026
|
-
Or, in English: "return all
|
997
|
+
Or, in English: "return all articles that have a category and at least one comment". Note again that articles with multiple comments will show up multiple times.
|
1027
998
|
|
1028
999
|
#### Joining Nested Associations (Single Level)
|
1029
1000
|
|
1030
1001
|
```ruby
|
1031
|
-
|
1002
|
+
Article.joins(comments: :guest)
|
1032
1003
|
```
|
1033
1004
|
|
1034
1005
|
This produces:
|
1035
1006
|
|
1036
1007
|
```sql
|
1037
|
-
SELECT
|
1038
|
-
INNER JOIN comments ON comments.
|
1008
|
+
SELECT articles.* FROM articles
|
1009
|
+
INNER JOIN comments ON comments.article_id = articles.id
|
1039
1010
|
INNER JOIN guests ON guests.comment_id = comments.id
|
1040
1011
|
```
|
1041
1012
|
|
1042
|
-
Or, in English: "return all
|
1013
|
+
Or, in English: "return all articles that have a comment made by a guest."
|
1043
1014
|
|
1044
1015
|
#### Joining Nested Associations (Multiple Level)
|
1045
1016
|
|
1046
1017
|
```ruby
|
1047
|
-
Category.joins(
|
1018
|
+
Category.joins(articles: [{ comments: :guest }, :tags])
|
1048
1019
|
```
|
1049
1020
|
|
1050
1021
|
This produces:
|
1051
1022
|
|
1052
1023
|
```sql
|
1053
1024
|
SELECT categories.* FROM categories
|
1054
|
-
INNER JOIN
|
1055
|
-
INNER JOIN comments ON comments.
|
1025
|
+
INNER JOIN articles ON articles.category_id = categories.id
|
1026
|
+
INNER JOIN comments ON comments.article_id = articles.id
|
1056
1027
|
INNER JOIN guests ON guests.comment_id = comments.id
|
1057
|
-
INNER JOIN tags ON tags.
|
1028
|
+
INNER JOIN tags ON tags.article_id = articles.id
|
1058
1029
|
```
|
1059
1030
|
|
1060
1031
|
### Specifying Conditions on the Joined Tables
|
@@ -1123,18 +1094,18 @@ Active Record lets you eager load any number of associations with a single `Mode
|
|
1123
1094
|
#### Array of Multiple Associations
|
1124
1095
|
|
1125
1096
|
```ruby
|
1126
|
-
|
1097
|
+
Article.includes(:category, :comments)
|
1127
1098
|
```
|
1128
1099
|
|
1129
|
-
This loads all the
|
1100
|
+
This loads all the articles and the associated category and comments for each article.
|
1130
1101
|
|
1131
1102
|
#### Nested Associations Hash
|
1132
1103
|
|
1133
1104
|
```ruby
|
1134
|
-
Category.includes(
|
1105
|
+
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
|
1135
1106
|
```
|
1136
1107
|
|
1137
|
-
This will find the category with id 1 and eager load all of the associated
|
1108
|
+
This will find the category with id 1 and eager load all of the associated articles, the associated articles' tags and comments, and every comment's guest association.
|
1138
1109
|
|
1139
1110
|
### Specifying Conditions on Eager Loaded Associations
|
1140
1111
|
|
@@ -1143,29 +1114,30 @@ Even though Active Record lets you specify conditions on the eager loaded associ
|
|
1143
1114
|
However if you must do this, you may use `where` as you would normally.
|
1144
1115
|
|
1145
1116
|
```ruby
|
1146
|
-
|
1117
|
+
Article.includes(:comments).where(comments: { visible: true })
|
1147
1118
|
```
|
1148
1119
|
|
1149
1120
|
This would generate a query which contains a `LEFT OUTER JOIN` whereas the
|
1150
1121
|
`joins` method would generate one using the `INNER JOIN` function instead.
|
1151
1122
|
|
1152
1123
|
```ruby
|
1153
|
-
SELECT "
|
1124
|
+
SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comments.visible = 1)
|
1154
1125
|
```
|
1155
1126
|
|
1156
|
-
If there was no `where` condition, this would generate the normal set of two
|
1157
|
-
queries.
|
1127
|
+
If there was no `where` condition, this would generate the normal set of two queries.
|
1158
1128
|
|
1159
1129
|
NOTE: Using `where` like this will only work when you pass it a Hash. For
|
1160
1130
|
SQL-fragments you need use `references` to force joined tables:
|
1161
1131
|
|
1162
1132
|
```ruby
|
1163
|
-
|
1133
|
+
Article.includes(:comments).where("comments.visible = true").references(:comments)
|
1164
1134
|
```
|
1165
1135
|
|
1166
|
-
If, in the case of this `includes` query, there were no comments for any
|
1167
|
-
all the
|
1168
|
-
conditions **must** match, otherwise no records will be
|
1136
|
+
If, in the case of this `includes` query, there were no comments for any
|
1137
|
+
articles, all the articles would still be loaded. By using `joins` (an INNER
|
1138
|
+
JOIN), the join conditions **must** match, otherwise no records will be
|
1139
|
+
returned.
|
1140
|
+
|
1169
1141
|
|
1170
1142
|
|
1171
1143
|
Scopes
|
@@ -1176,7 +1148,7 @@ Scoping allows you to specify commonly-used queries which can be referenced as m
|
|
1176
1148
|
To define a simple scope, we use the `scope` method inside the class, passing the query that we'd like to run when this scope is called:
|
1177
1149
|
|
1178
1150
|
```ruby
|
1179
|
-
class
|
1151
|
+
class Article < ActiveRecord::Base
|
1180
1152
|
scope :published, -> { where(published: true) }
|
1181
1153
|
end
|
1182
1154
|
```
|
@@ -1184,7 +1156,7 @@ end
|
|
1184
1156
|
This is exactly the same as defining a class method, and which you use is a matter of personal preference:
|
1185
1157
|
|
1186
1158
|
```ruby
|
1187
|
-
class
|
1159
|
+
class Article < ActiveRecord::Base
|
1188
1160
|
def self.published
|
1189
1161
|
where(published: true)
|
1190
1162
|
end
|
@@ -1194,7 +1166,7 @@ end
|
|
1194
1166
|
Scopes are also chainable within scopes:
|
1195
1167
|
|
1196
1168
|
```ruby
|
1197
|
-
class
|
1169
|
+
class Article < ActiveRecord::Base
|
1198
1170
|
scope :published, -> { where(published: true) }
|
1199
1171
|
scope :published_and_commented, -> { published.where("comments_count > 0") }
|
1200
1172
|
end
|
@@ -1203,14 +1175,14 @@ end
|
|
1203
1175
|
To call this `published` scope we can call it on either the class:
|
1204
1176
|
|
1205
1177
|
```ruby
|
1206
|
-
|
1178
|
+
Article.published # => [published articles]
|
1207
1179
|
```
|
1208
1180
|
|
1209
|
-
Or on an association consisting of `
|
1181
|
+
Or on an association consisting of `Article` objects:
|
1210
1182
|
|
1211
1183
|
```ruby
|
1212
1184
|
category = Category.first
|
1213
|
-
category.
|
1185
|
+
category.articles.published # => [published articles belonging to this category]
|
1214
1186
|
```
|
1215
1187
|
|
1216
1188
|
### Passing in arguments
|
@@ -1218,7 +1190,7 @@ category.posts.published # => [published posts belonging to this category]
|
|
1218
1190
|
Your scope can take arguments:
|
1219
1191
|
|
1220
1192
|
```ruby
|
1221
|
-
class
|
1193
|
+
class Article < ActiveRecord::Base
|
1222
1194
|
scope :created_before, ->(time) { where("created_at < ?", time) }
|
1223
1195
|
end
|
1224
1196
|
```
|
@@ -1226,13 +1198,13 @@ end
|
|
1226
1198
|
Call the scope as if it were a class method:
|
1227
1199
|
|
1228
1200
|
```ruby
|
1229
|
-
|
1201
|
+
Article.created_before(Time.zone.now)
|
1230
1202
|
```
|
1231
1203
|
|
1232
1204
|
However, this is just duplicating the functionality that would be provided to you by a class method.
|
1233
1205
|
|
1234
1206
|
```ruby
|
1235
|
-
class
|
1207
|
+
class Article < ActiveRecord::Base
|
1236
1208
|
def self.created_before(time)
|
1237
1209
|
where("created_at < ?", time)
|
1238
1210
|
end
|
@@ -1242,7 +1214,36 @@ end
|
|
1242
1214
|
Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects:
|
1243
1215
|
|
1244
1216
|
```ruby
|
1245
|
-
category.
|
1217
|
+
category.articles.created_before(time)
|
1218
|
+
```
|
1219
|
+
|
1220
|
+
### Applying a default scope
|
1221
|
+
|
1222
|
+
If we wish for a scope to be applied across all queries to the model we can use the
|
1223
|
+
`default_scope` method within the model itself.
|
1224
|
+
|
1225
|
+
```ruby
|
1226
|
+
class Client < ActiveRecord::Base
|
1227
|
+
default_scope { where("removed_at IS NULL") }
|
1228
|
+
end
|
1229
|
+
```
|
1230
|
+
|
1231
|
+
When queries are executed on this model, the SQL query will now look something like
|
1232
|
+
this:
|
1233
|
+
|
1234
|
+
```sql
|
1235
|
+
SELECT * FROM clients WHERE removed_at IS NULL
|
1236
|
+
```
|
1237
|
+
|
1238
|
+
If you need to do more complex things with a default scope, you can alternatively
|
1239
|
+
define it as a class method:
|
1240
|
+
|
1241
|
+
```ruby
|
1242
|
+
class Client < ActiveRecord::Base
|
1243
|
+
def self.default_scope
|
1244
|
+
# Should return an ActiveRecord::Relation.
|
1245
|
+
end
|
1246
|
+
end
|
1246
1247
|
```
|
1247
1248
|
|
1248
1249
|
### Merging of scopes
|
@@ -1298,36 +1299,6 @@ User.where(state: 'inactive')
|
|
1298
1299
|
As you can see above the `default_scope` is being merged in both
|
1299
1300
|
`scope` and `where` conditions.
|
1300
1301
|
|
1301
|
-
|
1302
|
-
### Applying a default scope
|
1303
|
-
|
1304
|
-
If we wish for a scope to be applied across all queries to the model we can use the
|
1305
|
-
`default_scope` method within the model itself.
|
1306
|
-
|
1307
|
-
```ruby
|
1308
|
-
class Client < ActiveRecord::Base
|
1309
|
-
default_scope { where("removed_at IS NULL") }
|
1310
|
-
end
|
1311
|
-
```
|
1312
|
-
|
1313
|
-
When queries are executed on this model, the SQL query will now look something like
|
1314
|
-
this:
|
1315
|
-
|
1316
|
-
```sql
|
1317
|
-
SELECT * FROM clients WHERE removed_at IS NULL
|
1318
|
-
```
|
1319
|
-
|
1320
|
-
If you need to do more complex things with a default scope, you can alternatively
|
1321
|
-
define it as a class method:
|
1322
|
-
|
1323
|
-
```ruby
|
1324
|
-
class Client < ActiveRecord::Base
|
1325
|
-
def self.default_scope
|
1326
|
-
# Should return an ActiveRecord::Relation.
|
1327
|
-
end
|
1328
|
-
end
|
1329
|
-
```
|
1330
|
-
|
1331
1302
|
### Removing All Scoping
|
1332
1303
|
|
1333
1304
|
If we wish to remove scoping for any reason we can use the `unscoped` method. This is
|
@@ -1470,6 +1441,11 @@ If you'd like to use your own SQL to find records in a table you can use `find_b
|
|
1470
1441
|
Client.find_by_sql("SELECT * FROM clients
|
1471
1442
|
INNER JOIN orders ON clients.id = orders.client_id
|
1472
1443
|
ORDER BY clients.created_at desc")
|
1444
|
+
# => [
|
1445
|
+
#<Client id: 1, first_name: "Lucas" >,
|
1446
|
+
#<Client id: 2, first_name: "Jan" >,
|
1447
|
+
# ...
|
1448
|
+
]
|
1473
1449
|
```
|
1474
1450
|
|
1475
1451
|
`find_by_sql` provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
|
@@ -1479,12 +1455,16 @@ Client.find_by_sql("SELECT * FROM clients
|
|
1479
1455
|
`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
|
1480
1456
|
|
1481
1457
|
```ruby
|
1482
|
-
Client.connection.select_all("SELECT
|
1458
|
+
Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'")
|
1459
|
+
# => [
|
1460
|
+
{"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
|
1461
|
+
{"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
|
1462
|
+
]
|
1483
1463
|
```
|
1484
1464
|
|
1485
1465
|
### `pluck`
|
1486
1466
|
|
1487
|
-
`pluck` can be used to query
|
1467
|
+
`pluck` can be used to query single or multiple columns from the underlying table of a model. It accepts a list of column names as argument and returns an array of values of the specified columns with the corresponding data type.
|
1488
1468
|
|
1489
1469
|
```ruby
|
1490
1470
|
Client.where(active: true).pluck(:id)
|
@@ -1606,20 +1586,20 @@ You can also use `any?` and `many?` to check for existence on a model or relatio
|
|
1606
1586
|
|
1607
1587
|
```ruby
|
1608
1588
|
# via a model
|
1609
|
-
|
1610
|
-
|
1589
|
+
Article.any?
|
1590
|
+
Article.many?
|
1611
1591
|
|
1612
1592
|
# via a named scope
|
1613
|
-
|
1614
|
-
|
1593
|
+
Article.recent.any?
|
1594
|
+
Article.recent.many?
|
1615
1595
|
|
1616
1596
|
# via a relation
|
1617
|
-
|
1618
|
-
|
1597
|
+
Article.where(published: true).any?
|
1598
|
+
Article.where(published: true).many?
|
1619
1599
|
|
1620
1600
|
# via an association
|
1621
|
-
|
1622
|
-
|
1601
|
+
Article.first.categories.any?
|
1602
|
+
Article.first.categories.many?
|
1623
1603
|
```
|
1624
1604
|
|
1625
1605
|
Calculations
|
@@ -1709,19 +1689,26 @@ Running EXPLAIN
|
|
1709
1689
|
You can run EXPLAIN on the queries triggered by relations. For example,
|
1710
1690
|
|
1711
1691
|
```ruby
|
1712
|
-
User.where(id: 1).joins(:
|
1692
|
+
User.where(id: 1).joins(:articles).explain
|
1713
1693
|
```
|
1714
1694
|
|
1715
1695
|
may yield
|
1716
1696
|
|
1717
1697
|
```
|
1718
|
-
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `
|
1719
|
-
|
1720
|
-
| id | select_type | table
|
1721
|
-
|
1722
|
-
| 1 | SIMPLE | users
|
1723
|
-
| 1 | SIMPLE |
|
1724
|
-
|
1698
|
+
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
|
1699
|
+
+----+-------------+----------+-------+---------------+
|
1700
|
+
| id | select_type | table | type | possible_keys |
|
1701
|
+
+----+-------------+----------+-------+---------------+
|
1702
|
+
| 1 | SIMPLE | users | const | PRIMARY |
|
1703
|
+
| 1 | SIMPLE | articles | ALL | NULL |
|
1704
|
+
+----+-------------+----------+-------+---------------+
|
1705
|
+
+---------+---------+-------+------+-------------+
|
1706
|
+
| key | key_len | ref | rows | Extra |
|
1707
|
+
+---------+---------+-------+------+-------------+
|
1708
|
+
| PRIMARY | 4 | const | 1 | |
|
1709
|
+
| NULL | NULL | NULL | 1 | Using where |
|
1710
|
+
+---------+---------+-------+------+-------------+
|
1711
|
+
|
1725
1712
|
2 rows in set (0.00 sec)
|
1726
1713
|
```
|
1727
1714
|
|
@@ -1731,15 +1718,15 @@ Active Record performs a pretty printing that emulates the one of the database
|
|
1731
1718
|
shells. So, the same query running with the PostgreSQL adapter would yield instead
|
1732
1719
|
|
1733
1720
|
```
|
1734
|
-
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "
|
1721
|
+
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_id" = "users"."id" WHERE "users"."id" = 1
|
1735
1722
|
QUERY PLAN
|
1736
1723
|
------------------------------------------------------------------------------
|
1737
1724
|
Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
|
1738
|
-
Join Filter: (
|
1725
|
+
Join Filter: (articles.user_id = users.id)
|
1739
1726
|
-> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
|
1740
1727
|
Index Cond: (id = 1)
|
1741
|
-
-> Seq Scan on
|
1742
|
-
Filter: (
|
1728
|
+
-> Seq Scan on articles (cost=0.00..28.88 rows=8 width=4)
|
1729
|
+
Filter: (articles.user_id = 1)
|
1743
1730
|
(6 rows)
|
1744
1731
|
```
|
1745
1732
|
|
@@ -1748,26 +1735,39 @@ may need the results of previous ones. Because of that, `explain` actually
|
|
1748
1735
|
executes the query, and then asks for the query plans. For example,
|
1749
1736
|
|
1750
1737
|
```ruby
|
1751
|
-
User.where(id: 1).includes(:
|
1738
|
+
User.where(id: 1).includes(:articles).explain
|
1752
1739
|
```
|
1753
1740
|
|
1754
1741
|
yields
|
1755
1742
|
|
1756
1743
|
```
|
1757
1744
|
EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
|
1758
|
-
|
1759
|
-
| id | select_type | table | type | possible_keys |
|
1760
|
-
|
1761
|
-
| 1 | SIMPLE | users | const | PRIMARY |
|
1762
|
-
|
1745
|
+
+----+-------------+-------+-------+---------------+
|
1746
|
+
| id | select_type | table | type | possible_keys |
|
1747
|
+
+----+-------------+-------+-------+---------------+
|
1748
|
+
| 1 | SIMPLE | users | const | PRIMARY |
|
1749
|
+
+----+-------------+-------+-------+---------------+
|
1750
|
+
+---------+---------+-------+------+-------+
|
1751
|
+
| key | key_len | ref | rows | Extra |
|
1752
|
+
+---------+---------+-------+------+-------+
|
1753
|
+
| PRIMARY | 4 | const | 1 | |
|
1754
|
+
+---------+---------+-------+------+-------+
|
1755
|
+
|
1763
1756
|
1 row in set (0.00 sec)
|
1764
1757
|
|
1765
|
-
EXPLAIN for: SELECT `
|
1766
|
-
|
1767
|
-
| id | select_type | table
|
1768
|
-
|
1769
|
-
| 1 | SIMPLE |
|
1770
|
-
|
1758
|
+
EXPLAIN for: SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN (1)
|
1759
|
+
+----+-------------+----------+------+---------------+
|
1760
|
+
| id | select_type | table | type | possible_keys |
|
1761
|
+
+----+-------------+----------+------+---------------+
|
1762
|
+
| 1 | SIMPLE | articles | ALL | NULL |
|
1763
|
+
+----+-------------+----------+------+---------------+
|
1764
|
+
+------+---------+------+------+-------------+
|
1765
|
+
| key | key_len | ref | rows | Extra |
|
1766
|
+
+------+---------+------+------+-------------+
|
1767
|
+
| NULL | NULL | NULL | 1 | Using where |
|
1768
|
+
+------+---------+------+------+-------------+
|
1769
|
+
|
1770
|
+
|
1771
1771
|
1 row in set (0.00 sec)
|
1772
1772
|
```
|
1773
1773
|
|