algoliasearch-rails 1.16.3 → 1.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +56 -42
  3. data/{ChangeLog → CHANGELOG.MD} +163 -1
  4. data/Gemfile +25 -6
  5. data/Gemfile.lock +144 -325
  6. data/LICENSE +6 -7
  7. data/README.md +448 -306
  8. data/algoliasearch-rails.gemspec +10 -8
  9. data/lib/algoliasearch-rails.rb +285 -117
  10. data/lib/algoliasearch/configuration.rb +3 -1
  11. data/lib/algoliasearch/tasks/algoliasearch.rake +6 -2
  12. data/lib/algoliasearch/utilities.rb +29 -3
  13. data/lib/algoliasearch/version.rb +3 -0
  14. data/spec/spec_helper.rb +26 -4
  15. data/vendor/assets/javascripts/algolia/algoliasearch.angular.js +23 -12
  16. data/vendor/assets/javascripts/algolia/algoliasearch.angular.min.js +2 -2
  17. data/vendor/assets/javascripts/algolia/algoliasearch.jquery.js +23 -12
  18. data/vendor/assets/javascripts/algolia/algoliasearch.jquery.min.js +2 -2
  19. data/vendor/assets/javascripts/algolia/algoliasearch.js +22 -12
  20. data/vendor/assets/javascripts/algolia/algoliasearch.min.js +2 -2
  21. data/vendor/assets/javascripts/algolia/v2/algoliasearch.angular.js +23 -12
  22. data/vendor/assets/javascripts/algolia/v2/algoliasearch.angular.min.js +2 -2
  23. data/vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.js +23 -12
  24. data/vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.min.js +2 -2
  25. data/vendor/assets/javascripts/algolia/v2/algoliasearch.js +22 -12
  26. data/vendor/assets/javascripts/algolia/v2/algoliasearch.min.js +2 -2
  27. data/vendor/assets/javascripts/algolia/v3/algoliasearch.angular.js +2020 -1301
  28. data/vendor/assets/javascripts/algolia/v3/algoliasearch.angular.min.js +3 -3
  29. data/vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.js +2019 -1300
  30. data/vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.min.js +3 -3
  31. data/vendor/assets/javascripts/algolia/v3/algoliasearch.js +2003 -1284
  32. data/vendor/assets/javascripts/algolia/v3/algoliasearch.min.js +3 -3
  33. metadata +19 -15
  34. data/VERSION +0 -1
data/LICENSE CHANGED
@@ -1,7 +1,6 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
- Copyright (c) 2013 Algolia
4
- http://www.algolia.com/
3
+ Copyright (c) 2013-Present Algolia
5
4
 
6
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
6
  of this software and associated documentation files (the "Software"), to deal
@@ -10,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
9
  copies of the Software, and to permit persons to whom the Software is
11
10
  furnished to do so, subject to the following conditions:
12
11
 
13
- The above copyright notice and this permission notice shall be included in
14
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
15
14
 
16
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- THE SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,44 +1,98 @@
1
- <!--NO_HTML-->
1
+ <p align="center">
2
+ <a href="https://www.algolia.com">
3
+ <img alt="Algolia for Rails" src="https://raw.githubusercontent.com/algolia/algoliasearch-client-common/master/banners/rails.png" >
4
+ </a>
2
5
 
3
- Algolia Search for Rails
4
- ==================
6
+ <h4 align="center">The perfect starting point to integrate <a href="https://algolia.com" target="_blank">Algolia</a> within your Rails project</h4>
5
7
 
6
- <!--/NO_HTML-->
8
+ <p align="center">
9
+ <a href="https://travis-ci.org/algolia/algoliasearch-rails"><img src="https://img.shields.io/travis/algolia/algoliasearch-rails/master.svg" alt="Build Status"></img></a>
10
+ <a href="http://badge.fury.io/rb/algoliasearch-rails"><img src="https://badge.fury.io/rb/algoliasearch-rails.svg" alt="Gem Version"></img></a>
11
+ <a href="https://codeclimate.com/github/algolia/algoliasearch-rails"><img src="https://codeclimate.com/github/algolia/algoliasearch-rails.svg" alt="Code Climate"></img></a>
12
+ <img src="https://img.shields.io/badge/ActiveRecord-yes-blue.svg?style=flat-square" alt="ActiveRecord"></img>
13
+ <img src="https://img.shields.io/badge/Mongoid-yes-blue.svg?style=flat-square" alt="Mongoid"></img>
14
+ <img src="https://img.shields.io/badge/Sequel-yes-blue.svg?style=flat-square" alt="Sequel"></img>
15
+ </p>
16
+ </p>
7
17
 
8
- This gem let you easily integrate the Algolia Search API to your favorite ORM. It's based on the [algoliasearch-client-ruby](https://github.com/algolia/algoliasearch-client-ruby) gem. Rails 3.x, 4.x and 5.x are all supported.
18
+ <p align="center">
19
+ <a href="https://www.algolia.com/doc/framework-integration/rails/getting-started/setup/?language=ruby" target="_blank">Documentation</a> •
20
+ <a href="https://discourse.algolia.com" target="_blank">Community Forum</a> •
21
+ <a href="http://stackoverflow.com/questions/tagged/algolia" target="_blank">Stack Overflow</a> •
22
+ <a href="https://github.com/algolia/algoliasearch-rails/issues" target="_blank">Report a bug</a> •
23
+ <a href="https://www.algolia.com/doc/framework-integration/rails/troubleshooting/faq/" target="_blank">FAQ</a> •
24
+ <a href="https://www.algolia.com/support" target="_blank">Support</a>
25
+ </p>
9
26
 
10
- You might be interested in the sample Ruby on Rails application providing a `autocomplete.js`-based auto-completion and `instantsearch.js`-based instant search results page: [algoliasearch-rails-example](https://github.com/algolia/algoliasearch-rails-example/).
11
27
 
12
- [![Build Status](https://travis-ci.org/algolia/algoliasearch-rails.svg?branch=master)](https://travis-ci.org/algolia/algoliasearch-rails) [![Gem Version](https://badge.fury.io/rb/algoliasearch-rails.svg)](http://badge.fury.io/rb/algoliasearch-rails) [![Code Climate](https://codeclimate.com/github/algolia/algoliasearch-rails.svg)](https://codeclimate.com/github/algolia/algoliasearch-rails) ![ActiveRecord](https://img.shields.io/badge/ActiveRecord-yes-blue.svg?style=flat-square) ![Mongoid](https://img.shields.io/badge/Mongoid-yes-blue.svg?style=flat-square) ![Sequel](https://img.shields.io/badge/Sequel-yes-blue.svg?style=flat-square)
28
+ This gem let you easily integrate the Algolia Search API to your favorite ORM. It's based on the [algoliasearch-client-ruby](https://github.com/algolia/algoliasearch-client-ruby) gem.
29
+ Rails 3.x, 4.x and 5.x are all supported.
13
30
 
14
- <!--NO_HTML-->
31
+ You might be interested in the sample Ruby on Rails application providing a `autocomplete.js`-based auto-completion and `InstantSearch.js`-based instant search results page: [algoliasearch-rails-example](https://github.com/algolia/algoliasearch-rails-example/).
15
32
 
16
- Table of Content
17
- =============
18
33
 
19
- 1. [Install](#install)
20
- 1. [Setup](#setup)
21
- 1. [Quick Start](#quick-start)
22
- 1. [Options](#options)
23
- 1. [Configuration example](#configuration-example)
24
- 1. [Indexing](#indexing)
25
- 1. [Master/Slave](#masterslave)
26
- 1. [Share a single index](#share-a-single-index)
27
- 1. [Target multiple indexes](#target-multiple-indexes)
28
- 1. [Tags](#tags)
29
- 1. [Search](#search)
30
- 1. [Faceting](#faceting)
31
- 1. [Facet search](#facet-search)
32
- 1. [Group by](#group-by)
33
- 1. [Geo-search](#geo-search)
34
- 1. [Caveats](#caveats)
35
- 1. [Note on testing](#note-on-testing)
36
34
 
37
- <!--/NO_HTML-->
35
+ ## API Documentation
38
36
 
39
- ## Setup
37
+ You can find the full reference on [Algolia's website](https://www.algolia.com/doc/framework-integration/rails/).
40
38
 
41
- ### Install
39
+
40
+
41
+ 1. **[Setup](#setup)**
42
+ * [Install](#install)
43
+ * [Configuration](#configuration)
44
+ * [Timeouts](#timeouts)
45
+ * [Notes](#notes)
46
+
47
+ 1. **[Usage](#usage)**
48
+ * [Index Schema](#index-schema)
49
+ * [Relevancy](#relevancy)
50
+ * [Indexing](#indexing)
51
+ * [Frontend Search (realtime experience)](#frontend-search-realtime-experience)
52
+ * [Backend Search](#backend-search)
53
+ * [Backend Pagination](#backend-pagination)
54
+ * [Tags](#tags)
55
+ * [Faceting](#faceting)
56
+ * [Faceted search](#faceted-search)
57
+ * [Group by](#group-by)
58
+ * [Geo-Search](#geo-search)
59
+
60
+ 1. **[Options](#options)**
61
+ * [Auto-indexing &amp; asynchronism](#auto-indexing--asynchronism)
62
+ * [Custom index name](#custom-index-name)
63
+ * [Per-environment indices](#per-environment-indices)
64
+ * [Custom attribute definition](#custom-attribute-definition)
65
+ * [Nested objects/relations](#nested-objectsrelations)
66
+ * [Custom <code>objectID</code>](#custom-objectid)
67
+ * [Restrict indexing to a subset of your data](#restrict-indexing-to-a-subset-of-your-data)
68
+ * [Sanitizer](#sanitizer)
69
+ * [UTF-8 Encoding](#utf-8-encoding)
70
+ * [Exceptions](#exceptions)
71
+ * [Configuration example](#configuration-example)
72
+
73
+ 1. **[Indices](#indices)**
74
+ * [Manual indexing](#manual-indexing)
75
+ * [Manual removal](#manual-removal)
76
+ * [Reindexing](#reindexing)
77
+ * [Clearing an index](#clearing-an-index)
78
+ * [Using the underlying index](#using-the-underlying-index)
79
+ * [Primary/replica](#primaryreplica)
80
+ * [Share a single index](#share-a-single-index)
81
+ * [Target multiple indices](#target-multiple-indices)
82
+
83
+ 1. **[Testing](#testing)**
84
+ * [Notes](#notes)
85
+
86
+ 1. **[Troubleshooting](#troubleshooting)**
87
+ * [Frequently asked questions](#frequently-asked-questions)
88
+
89
+
90
+
91
+ # Setup
92
+
93
+
94
+
95
+ ## Install
42
96
 
43
97
  ```sh
44
98
  gem install algoliasearch-rails
@@ -56,20 +110,51 @@ And run:
56
110
  bundle install
57
111
  ```
58
112
 
59
- ### Configuration
113
+ ## Configuration
60
114
 
61
115
  Create a new file <code>config/initializers/algoliasearch.rb</code> to setup your <code>APPLICATION_ID</code> and <code>API_KEY</code>.
62
116
 
63
-
64
117
  ```ruby
65
118
  AlgoliaSearch.configuration = { application_id: 'YourApplicationID', api_key: 'YourAPIKey' }
66
119
  ```
67
120
 
68
121
  The gem is compatible with [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord), [Mongoid](https://github.com/mongoid/mongoid) and [Sequel](https://github.com/jeremyevans/sequel).
69
122
 
70
- ## Quick Start
123
+ ## Timeouts
124
+
125
+ You can configure a various timeout thresholds by setting the following options at initialization time:
126
+
127
+ ```ruby
128
+ AlgoliaSearch.configuration = {
129
+ application_id: 'YourApplicationID',
130
+ api_key: 'YourAPIKey',
131
+ connect_timeout: 2,
132
+ receive_timeout: 30,
133
+ send_timeout: 30,
134
+ batch_timeout: 120,
135
+ search_timeout: 5
136
+ }
137
+ ```
138
+
139
+ ## Notes
140
+
141
+ This gem makes extensive use of Rails' callbacks to trigger the indexing tasks. If you're using methods bypassing `after_validation`, `before_save` or `after_commit` callbacks, it will not index your changes. For example: `update_attribute` doesn't perform validations checks, to perform validations when updating use `update_attributes`.
71
142
 
72
- ### Schema
143
+ All methods injected by the `AlgoliaSearch` module are prefixed by `algolia_` and aliased to the associated short names if they aren't already defined.
144
+
145
+ ```ruby
146
+ Contact.algolia_reindex! # <=> Contact.reindex!
147
+
148
+ Contact.algolia_search("jon doe") # <=> Contact.search("jon doe")
149
+ ```
150
+
151
+
152
+
153
+ # Usage
154
+
155
+
156
+
157
+ ## Index Schema
73
158
 
74
159
  The following code will create a <code>Contact</code> index and add search capabilities to your <code>Contact</code> model:
75
160
 
@@ -78,7 +163,7 @@ class Contact < ActiveRecord::Base
78
163
  include AlgoliaSearch
79
164
 
80
165
  algoliasearch do
81
- attribute :first_name, :last_name, :email
166
+ attributes :first_name, :last_name, :email
82
167
  end
83
168
  end
84
169
  ```
@@ -112,7 +197,7 @@ class Product < ActiveRecord::Base
112
197
  end
113
198
  ```
114
199
 
115
- ### Relevancy
200
+ ## Relevancy
116
201
 
117
202
  We provide many ways to configure your index allowing you to tune your overall index relevancy. The most important ones are the **searchable attributes** and the attributes reflecting **record popularity**.
118
203
 
@@ -124,11 +209,11 @@ class Product < ActiveRecord::Base
124
209
  # list of attribute used to build an Algolia record
125
210
  attributes :title, :subtitle, :description, :likes_count, :seller_name
126
211
 
127
- # the attributesToIndex` setting defines the attributes
212
+ # the `searchableAttributes` (formerly known as attributesToIndex) setting defines the attributes
128
213
  # you want to search in: here `title`, `subtitle` & `description`.
129
214
  # You need to list them by order of importance. `description` is tagged as
130
215
  # `unordered` to avoid taking the position of a match into account in that attribute.
131
- attributesToIndex ['title', 'subtitle', 'unordered(description)']
216
+ searchableAttributes ['title', 'subtitle', 'unordered(description)']
132
217
 
133
218
  # the `customRanking` setting defines the ranking criteria use to compare two matching
134
219
  # records in case their text-relevance is equal. It should reflect your record popularity.
@@ -138,7 +223,7 @@ class Product < ActiveRecord::Base
138
223
  end
139
224
  ```
140
225
 
141
- ### Indexing
226
+ ## Indexing
142
227
 
143
228
  To index a model, simple call `reindex` on the class:
144
229
 
@@ -156,11 +241,11 @@ algolia_models = ActiveRecord::Base.descendants.select{ |model| model.respond_to
156
241
  algolia_models.each(&:reindex)
157
242
  ```
158
243
 
159
- ### Frontend Search (realtime experience)
244
+ ## Frontend Search (realtime experience)
160
245
 
161
246
  Traditional search implementations tend to have search logic and functionality on the backend. This made sense when the search experience consisted of a user entering a search query, executing that search, and then being redirected to a search result page.
162
247
 
163
- Implementing search on the backend is no longer necessary. In fact, in most cases it is harmful to performance because of added network and processing latency. We highly recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-js) issuing all search requests directly from the end user's browser, mobile device, or client. It will reduce the overall search latency while offloading your servers at the same time.
248
+ Implementing search on the backend is no longer necessary. In fact, in most cases it is harmful to performance because of added network and processing latency. We highly recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-javascript) issuing all search requests directly from the end user's browser, mobile device, or client. It will reduce the overall search latency while offloading your servers at the same time.
164
249
 
165
250
  The JS API client is part of the gem, just require `algolia/v3/algoliasearch.min` somewhere in your JavaScript manifest, for example in `application.js` if you are using Rails 3.1+:
166
251
 
@@ -182,23 +267,58 @@ index.search('something', { hitsPerPage: 10, page: 0 })
182
267
  });
183
268
  ```
184
269
 
185
- **We recently (March 2015) released a new version (V3) of our JavaScript client, if you were using our previous version (V2), [read the migration guide](https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x)**
270
+ **We recently (March 2015) released a new version (V3) of our JavaScript client, if you were using our previous version (V2), [read the migration guide](https://github.com/algolia/algoliasearch-client-javascript/wiki/Migration-guide-from-2.x.x-to-3.x.x)**
186
271
 
187
- ### Backend Search
272
+ ## Backend Search
188
273
 
189
- If you want to search from your backend you can use the `raw_search` method. It retrieves the raw JSON answer from the API:
274
+ ***Notes:*** We recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-javascript) to perform queries directly from the end-user browser without going through your server.
275
+
276
+ A search returns ORM-compliant objects reloading them from your database. We recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-javascript) to perform queries to decrease the overall latency and offload your servers.
190
277
 
191
278
  ```ruby
192
- p Contact.raw_search("jon doe")
279
+ hits = Contact.search("jon doe")
280
+ p hits
281
+ p hits.raw_answer # to get the original JSON raw answer
193
282
  ```
194
283
 
195
- You could also use `search` but it's not recommended. This method will fetch the matching `objectIDs` from the API and perform a database query to retrieve an array of matching models:
284
+ A `highlight_result` attribute is added to each ORM object:
196
285
 
197
286
  ```ruby
198
- p Contact.search("jon doe") # we recommend to use `raw_search` to avoid the database lookup
287
+ hits[0].highlight_result['first_name']['value']
199
288
  ```
200
289
 
201
- ### Backend Pagination
290
+ If you want to retrieve the raw JSON answer from the API, without re-loading the objects from the database, you can use:
291
+
292
+ ```ruby
293
+ json_answer = Contact.raw_search("jon doe")
294
+ p json_answer
295
+ p json_answer['hits']
296
+ p json_answer['facets']
297
+ ```
298
+
299
+ Search parameters can be specified either through the index's [settings](https://github.com/algolia/algoliasearch-client-ruby#index-settings-parameters) statically in your model or dynamically at search time specifying [search parameters](https://github.com/algolia/algoliasearch-client-ruby#search) as second argument of the `search` method:
300
+
301
+ ```ruby
302
+ class Contact < ActiveRecord::Base
303
+ include AlgoliaSearch
304
+
305
+ algoliasearch do
306
+ attribute :first_name, :last_name, :email
307
+
308
+ # default search parameters stored in the index settings
309
+ minWordSizefor1Typo 4
310
+ minWordSizefor2Typos 8
311
+ hitsPerPage 42
312
+ end
313
+ end
314
+ ```
315
+
316
+ ```ruby
317
+ # dynamical search parameters
318
+ p Contact.raw_search('jon doe', { hitsPerPage: 5, page: 2 })
319
+ ```
320
+
321
+ ## Backend Pagination
202
322
 
203
323
  Even if we **highly recommend to perform all search (and therefore pagination) operations from your frontend using JavaScript**, we support both [will_paginate](https://github.com/mislav/will_paginate) and [kaminari](https://github.com/amatsuda/kaminari) as pagination backend.
204
324
 
@@ -222,21 +342,129 @@ Then, as soon as you use the `search` method, the returning results will be a pa
222
342
  <%= paginate @results %>
223
343
  ```
224
344
 
225
- ### Notes
345
+ ## Tags
226
346
 
227
- All methods injected by the `AlgoliaSearch` include are prefixed by `algolia_` and aliased to the associated short names if they aren't already defined.
347
+ Use the <code>tags</code> method to add tags to your record:
228
348
 
229
349
  ```ruby
230
- Contact.algolia_reindex! # <=> Contact.reindex!
350
+ class Contact < ActiveRecord::Base
351
+ include AlgoliaSearch
231
352
 
232
- Contact.algolia_search("jon doe") # <=> Contact.search("jon doe")
353
+ algoliasearch do
354
+ tags ['trusted']
355
+ end
356
+ end
233
357
  ```
234
358
 
235
- ## Options
359
+ or using dynamical values:
236
360
 
237
- ### Auto-indexing & asynchronism
361
+ ```ruby
362
+ class Contact < ActiveRecord::Base
363
+ include AlgoliaSearch
238
364
 
239
- Each time a record is saved; it will be - asynchronously - indexed. On the other hand, each time a record is destroyed, it will be - asynchronously - removed from the index. That means that a network call with the ADD/DELETE operation is sent **synchronously** to the Algolia API but then the engine will **asynchronously** process the operation (so if you do a search just after, the results may not reflect it yet).
365
+ algoliasearch do
366
+ tags do
367
+ [first_name.blank? || last_name.blank? ? 'partial' : 'full', has_valid_email? ? 'valid_email' : 'invalid_email']
368
+ end
369
+ end
370
+ end
371
+ ```
372
+
373
+ At query time, specify <code>{ tagFilters: 'tagvalue' }</code> or <code>{ tagFilters: ['tagvalue1', 'tagvalue2'] }</code> as search parameters to restrict the result set to specific tags.
374
+
375
+ ## Faceting
376
+
377
+ Facets can be retrieved calling the extra `facets` method of the search answer.
378
+
379
+ ```ruby
380
+ class Contact < ActiveRecord::Base
381
+ include AlgoliaSearch
382
+
383
+ algoliasearch do
384
+ # [...]
385
+
386
+ # specify the list of attributes available for faceting
387
+ attributesForFaceting [:company, :zip_code]
388
+ end
389
+ end
390
+ ```
391
+
392
+ ```ruby
393
+ hits = Contact.search('jon doe', { facets: '*' })
394
+ p hits # ORM-compliant array of objects
395
+ p hits.facets # extra method added to retrieve facets
396
+ p hits.facets['company'] # facet values+count of facet 'company'
397
+ p hits.facets['zip_code'] # facet values+count of facet 'zip_code'
398
+ ```
399
+
400
+ ```ruby
401
+ raw_json = Contact.raw_search('jon doe', { facets: '*' })
402
+ p raw_json['facets']
403
+ ```
404
+
405
+ ## Faceted search
406
+
407
+ You can also search for facet values.
408
+
409
+ ```ruby
410
+ Product.search_for_facet_values('category', 'Headphones') # Array of {value, highlighted, count}
411
+ ```
412
+
413
+ This method can also take any parameter a query can take.
414
+ This will adjust the search to only hits which would have matched the query.
415
+
416
+ ```ruby
417
+ # Only sends back the categories containing red Apple products (and only counts those)
418
+ Product.search_for_facet_values('category', 'phone', {
419
+ query: 'red',
420
+ filters: 'brand:Apple'
421
+ }) # Array of phone categories linked to red Apple products
422
+ ```
423
+
424
+ ## Group by
425
+
426
+ More info on distinct for grouping can be found
427
+ [here](https://www.algolia.com/doc/guides/managing-results/refine-results/grouping/).
428
+
429
+ ```ruby
430
+ class Contact < ActiveRecord::Base
431
+ include AlgoliaSearch
432
+
433
+ algoliasearch do
434
+ # [...]
435
+
436
+ # specify the attribute to be used for distinguishing the records
437
+ # in this case the records will be grouped by company
438
+ attributeForDistinct "company"
439
+ end
440
+ end
441
+ ```
442
+
443
+ ## Geo-Search
444
+
445
+ Use the <code>geoloc</code> method to localize your record:
446
+
447
+ ```ruby
448
+ class Contact < ActiveRecord::Base
449
+ include AlgoliaSearch
450
+
451
+ algoliasearch do
452
+ geoloc :lat_attr, :lng_attr
453
+ end
454
+ end
455
+ ```
456
+
457
+ At query time, specify <code>{ aroundLatLng: "37.33, -121.89", aroundRadius: 50000 }</code> as search parameters to restrict the result set to 50KM around San Jose.
458
+
459
+
460
+
461
+ # Options
462
+
463
+
464
+
465
+ ## Auto-indexing & asynchronism
466
+
467
+ Each time a record is saved, it will be *asynchronously* indexed. On the other hand, each time a record is destroyed, it will be - asynchronously - removed from the index. That means that a network call with the ADD/DELETE operation is sent **synchronously** to the Algolia API but then the engine will **asynchronously** process the operation (so if you do a search just after, the results may not reflect it yet).
240
468
 
241
469
  You can disable auto-indexing and auto-removing setting the following options:
242
470
 
@@ -250,7 +478,7 @@ class Contact < ActiveRecord::Base
250
478
  end
251
479
  ```
252
480
 
253
- #### Temporary disable auto-indexing
481
+ ### Temporary disable auto-indexing
254
482
 
255
483
  You can temporary disable auto-indexing using the <code>without_auto_index</code> scope. This is often used for performance reason.
256
484
 
@@ -262,7 +490,7 @@ end
262
490
  Contact.reindex! # will use batch operations
263
491
  ```
264
492
 
265
- #### Queues & background jobs
493
+ ### Queues & background jobs
266
494
 
267
495
  You can configure the auto-indexing & auto-removal process to use a queue to perform those operations in background. ActiveJob (Rails >=4.2) queues are used by default but you can define your own queuing mechanism:
268
496
 
@@ -276,7 +504,7 @@ class Contact < ActiveRecord::Base
276
504
  end
277
505
  ```
278
506
 
279
- #### Things to Consider
507
+ ### Things to Consider
280
508
 
281
509
  If you are performing updates & deletions in the background then a record deletion can be committed to your database prior
282
510
  to the job actually executing. Thus if you were to load the record to remove it from the database than your ActiveRecord#find will fail with a RecordNotFound.
@@ -300,7 +528,7 @@ class MySidekiqWorker
300
528
  end
301
529
  ```
302
530
 
303
- #### With Sidekiq
531
+ ### With Sidekiq
304
532
 
305
533
  If you're using [Sidekiq](https://github.com/mperham/sidekiq):
306
534
 
@@ -319,13 +547,21 @@ end
319
547
 
320
548
  class MySidekiqWorker
321
549
  def perform(id, remove)
322
- c = Contact.find(id)
323
- remove ? c.remove_from_index! : c.index!
550
+ if remove
551
+ # the record has likely already been removed from your database so we cannot
552
+ # use ActiveRecord#find to load it
553
+ index = Algolia::Index.new("index_name")
554
+ index.delete_object(id)
555
+ else
556
+ # the record should be present
557
+ c = Contact.find(id)
558
+ c.index!
559
+ end
324
560
  end
325
561
  end
326
562
  ```
327
563
 
328
- #### With DelayedJob
564
+ ### With DelayedJob
329
565
 
330
566
  If you're using [delayed_job](https://github.com/collectiveidea/delayed_job):
331
567
 
@@ -348,7 +584,7 @@ end
348
584
 
349
585
  ```
350
586
 
351
- #### Synchronism & testing
587
+ ### Synchronism & testing
352
588
 
353
589
  You can force indexing and removing to be synchronous (in that case the gem will call the `wait_task` method to ensure the operation has been taken into account once the method returns) by setting the following option: (this is **NOT** recommended, except for testing purpose)
354
590
 
@@ -362,22 +598,7 @@ class Contact < ActiveRecord::Base
362
598
  end
363
599
  ```
364
600
 
365
- ### Exceptions
366
-
367
- You can disable exceptions that could be raised while trying to reach Algolia's API by using the `raise_on_failure` option:
368
-
369
- ```ruby
370
- class Contact < ActiveRecord::Base
371
- include AlgoliaSearch
372
-
373
- # only raise exceptions in development env
374
- algoliasearch raise_on_failure: Rails.env.development? do
375
- attribute :first_name, :last_name, :email
376
- end
377
- end
378
- ```
379
-
380
- ### Custom index name
601
+ ## Custom index name
381
602
 
382
603
  By default, the index name will be the class name, e.g. "Contact". You can customize the index name by using the `index_name` option:
383
604
 
@@ -391,7 +612,7 @@ class Contact < ActiveRecord::Base
391
612
  end
392
613
  ```
393
614
 
394
- ### Per-environment indexes
615
+ ## Per-environment indices
395
616
 
396
617
  You can suffix the index name with the current Rails environment using the following option:
397
618
 
@@ -405,7 +626,7 @@ class Contact < ActiveRecord::Base
405
626
  end
406
627
  ```
407
628
 
408
- ### Custom attribute definition
629
+ ## Custom attribute definition
409
630
 
410
631
  You can use a block to specify a complex attribute value
411
632
 
@@ -446,7 +667,9 @@ class Contact < ActiveRecord::Base
446
667
  end
447
668
  ```
448
669
 
449
- ### Nested objects/relations
670
+ ## Nested objects/relations
671
+
672
+ ### Defining the relationship
450
673
 
451
674
  You can easily embed nested objects defining an extra attribute returning any JSON-compliant object (an array or a hash or a combination of both).
452
675
 
@@ -473,7 +696,83 @@ class Profile < ActiveRecord::Base
473
696
  end
474
697
  ```
475
698
 
476
- ### Custom `objectID`
699
+ ### Propagating the change from a nested child
700
+
701
+ #### With ActiveRecord
702
+
703
+ With ActiveRecord, we'll be using `touch` and `after_touch` to achieve this.
704
+
705
+ ```ruby
706
+ # app/models/app.rb
707
+ class App < ApplicationRecord
708
+ include AlgoliaSearch
709
+
710
+ belongs_to :author, class_name: :User
711
+ after_touch :index!
712
+
713
+ algoliasearch do
714
+ attribute :title
715
+ attribute :author do
716
+ author.as_json
717
+ end
718
+ end
719
+ end
720
+
721
+ # app/models/user.rb
722
+ class User < ApplicationRecord
723
+ # If your association uses belongs_to
724
+ # - use `touch: true`
725
+ # - do not define an `after_save` hook
726
+ has_many :apps, foreign_key: :author_id
727
+
728
+ after_save { apps.each(&:touch) }
729
+ end
730
+ ```
731
+
732
+ #### With Sequel
733
+
734
+ With Sequel, you can use the `touch` plugin to propagate the changes:
735
+
736
+ ```ruby
737
+ # app/models/app.rb
738
+ class App < Sequel::Model
739
+ include AlgoliaSearch
740
+
741
+ many_to_one :author, class: :User
742
+
743
+ plugin :timestamps
744
+ plugin :touch
745
+
746
+ algoliasearch do
747
+ attribute :title
748
+ attribute :author do
749
+ author.to_hash
750
+ end
751
+ end
752
+ end
753
+
754
+ # app/models/user.rb
755
+ class User < Sequel::Model
756
+ one_to_many :apps, key: :author_id
757
+
758
+ plugin :timestamps
759
+ # Can't use the associations since it won't trigger the after_save
760
+ plugin :touch
761
+
762
+ # Define the associations that need to be touched here
763
+ # Less performant, but allows for the after_save hook to trigger
764
+ def touch_associations
765
+ apps.map(&:touch)
766
+ end
767
+
768
+ def touch
769
+ super
770
+ touch_associations
771
+ end
772
+ end
773
+ ```
774
+
775
+ ## Custom `objectID`
477
776
 
478
777
  By default, the `objectID` is based on your record's `id`. You can change this behavior specifying the `:id` option (be sure to use a uniq field).
479
778
 
@@ -486,7 +785,7 @@ class UniqUser < ActiveRecord::Base
486
785
  end
487
786
  ```
488
787
 
489
- ### Restrict indexing to a subset of your data
788
+ ## Restrict indexing to a subset of your data
490
789
 
491
790
  You can add constraints controlling if a record must be indexed by using options the `:if` or `:unless` options.
492
791
 
@@ -528,7 +827,6 @@ class Contact < ActiveRecord::Base
528
827
  end
529
828
  ```
530
829
 
531
-
532
830
  You can index a subset of your records using either:
533
831
 
534
832
  ```ruby
@@ -542,7 +840,7 @@ or
542
840
  MyModel.index_objects MyModel.limit(5)
543
841
  ```
544
842
 
545
- ### Sanitizer
843
+ ## Sanitizer
546
844
 
547
845
  You can sanitize all your attributes using the `sanitize` option. It will strip all HTML tags from your attributes.
548
846
 
@@ -563,8 +861,7 @@ If you're using Rails 4.2+, you also need to depend on `rails-html-sanitizer`:
563
861
  gem 'rails-html-sanitizer'
564
862
  ```
565
863
 
566
-
567
- ### UTF-8 Encoding
864
+ ## UTF-8 Encoding
568
865
 
569
866
  You can force the UTF-8 encoding of all your attributes using the `force_utf8_encoding` option:
570
867
 
@@ -581,8 +878,22 @@ end
581
878
 
582
879
  ***Notes:*** This option is not compatible with Ruby 1.8
583
880
 
881
+ ## Exceptions
584
882
 
585
- ### Configuration example
883
+ You can disable exceptions that could be raised while trying to reach Algolia's API by using the `raise_on_failure` option:
884
+
885
+ ```ruby
886
+ class Contact < ActiveRecord::Base
887
+ include AlgoliaSearch
888
+
889
+ # only raise exceptions in development env
890
+ algoliasearch raise_on_failure: Rails.env.development? do
891
+ attribute :first_name, :last_name, :email
892
+ end
893
+ end
894
+ ```
895
+
896
+ ## Configuration example
586
897
 
587
898
  Here is a real-word configuration example (from [HN Search](https://github.com/algolia/hn-search)):
588
899
 
@@ -601,7 +912,7 @@ class Item < ActiveRecord::Base
601
912
 
602
913
  # `title` is more important than `{story,comment}_text`, `{story,comment}_text` more than `url`, `url` more than `author`
603
914
  # btw, do not take into account position in most fields to avoid first word match boost
604
- attributesToIndex ['unordered(title)', 'unordered(story_text)', 'unordered(comment_text)', 'unordered(url)', 'author']
915
+ searchableAttributes ['unordered(title)', 'unordered(story_text)', 'unordered(comment_text)', 'unordered(url)', 'author']
605
916
 
606
917
  # tags used for filtering
607
918
  tags do
@@ -639,9 +950,13 @@ class Item < ActiveRecord::Base
639
950
  end
640
951
  ```
641
952
 
642
- ## Indexing
643
953
 
644
- ### Manual indexing
954
+
955
+ # Indices
956
+
957
+
958
+
959
+ ## Manual indexing
645
960
 
646
961
  You can trigger indexing using the <code>index!</code> instance method.
647
962
 
@@ -650,7 +965,7 @@ c = Contact.create!(params[:contact])
650
965
  c.index!
651
966
  ```
652
967
 
653
- ### Manual removal
968
+ ## Manual removal
654
969
 
655
970
  And trigger index removing using the <code>remove_from_index!</code> instance method.
656
971
 
@@ -659,13 +974,13 @@ c.remove_from_index!
659
974
  c.destroy
660
975
  ```
661
976
 
662
- ### Reindexing
977
+ ## Reindexing
663
978
 
664
979
  The gem provides 2 ways to reindex all your objects:
665
980
 
666
- #### Atomical reindexing
981
+ ### Atomical reindexing
667
982
 
668
- To reindex all your records (taking into account the deleted objects), the `reindex` class method indexes all your objects to a temporary index called `<INDEX_NAME>.tmp` and moves the temporary index to the final one once everything is indexed (atomically). This is the safest way to reindex all your content.
983
+ To reindex all your records (taking into account the deleted objects), the `reindex` class method indices all your objects to a temporary index called `<INDEX_NAME>.tmp` and moves the temporary index to the final one once everything is indexed (atomically). This is the safest way to reindex all your content.
669
984
 
670
985
  ```ruby
671
986
  Contact.reindex
@@ -673,7 +988,9 @@ Contact.reindex
673
988
 
674
989
  **Notes**: if you're using an index-specific API key, ensure you're allowing both `<INDEX_NAME>` and `<INDEX_NAME>.tmp`.
675
990
 
676
- #### Regular reindexing
991
+ **Warning:** You should not use such an atomic reindexing operation while scoping/filtering the model because this operation **replaces the entire index**, keeping the filtered objects only. ie: Don't do `MyModel.where(...).reindex` but do `MyModel.where(...).reindex!` (with the trailing `!`)!!!
992
+
993
+ ### Regular reindexing
677
994
 
678
995
  To reindex all your objects in place (without temporary index and therefore without deleting removed objects), use the `reindex!` class method:
679
996
 
@@ -681,7 +998,7 @@ To reindex all your objects in place (without temporary index and therefore with
681
998
  Contact.reindex!
682
999
  ```
683
1000
 
684
- ### Clearing an index
1001
+ ## Clearing an index
685
1002
 
686
1003
  To clear an index, use the <code>clear_index!</code> class method:
687
1004
 
@@ -689,7 +1006,7 @@ To clear an index, use the <code>clear_index!</code> class method:
689
1006
  Contact.clear_index!
690
1007
  ```
691
1008
 
692
- ### Using the underlying index
1009
+ ## Using the underlying index
693
1010
 
694
1011
  You can access the underlying `index` object by calling the `index` class method:
695
1012
 
@@ -698,11 +1015,10 @@ index = Contact.index
698
1015
  # index.get_settings, index.partial_update_object, ...
699
1016
  ```
700
1017
 
701
- ## Indexes
702
-
703
- ### Master/slave
1018
+ ## Primary/replica
704
1019
 
705
- You can define slave indexes using the <code>add_slave</code> method:
1020
+ You can define replica indices using the <code>add_replica</code> method.
1021
+ Use `inherit: true` on the replica block if you want it to inherit from the primary settings.
706
1022
 
707
1023
  ```ruby
708
1024
  class Book < ActiveRecord::Base
@@ -711,31 +1027,31 @@ class Book < ActiveRecord::Base
711
1027
  include AlgoliaSearch
712
1028
 
713
1029
  algoliasearch per_environment: true do
714
- attributesToIndex [:name, :author, :editor]
1030
+ searchableAttributes [:name, :author, :editor]
715
1031
 
716
- # define a slave index to search by `author` only
717
- add_slave 'Book_by_author', per_environment: true do
718
- attributesToIndex [:author]
1032
+ # define a replica index to search by `author` only
1033
+ add_replica 'Book_by_author', per_environment: true do
1034
+ searchableAttributes [:author]
719
1035
  end
720
1036
 
721
- # define a slave index to search by `editor` only
722
- add_slave 'Book_by_editor', per_environment: true do
723
- attributesToIndex [:editor]
1037
+ # define a replica index with custom ordering but same settings than the main block
1038
+ add_replica 'Book_custom_order', inherit: true, per_environment: true do
1039
+ customRanking ['asc(rank)']
724
1040
  end
725
1041
  end
726
1042
 
727
1043
  end
728
1044
  ```
729
1045
 
730
- To search using a slave, use the following code:
1046
+ To search using a replica, use the following code:
731
1047
 
732
1048
  ```ruby
733
- Book.raw_search 'foo bar', slave: 'Book_by_editor'
1049
+ Book.raw_search 'foo bar', replica: 'Book_by_editor'
734
1050
  # or
735
- Book.search 'foo bar', slave: 'Book_by_editor'
1051
+ Book.search 'foo bar', replica: 'Book_by_editor'
736
1052
  ```
737
1053
 
738
- ### Share a single index
1054
+ ## Share a single index
739
1055
 
740
1056
  It can make sense to share an index between several models. In order to implement that, you'll need to ensure you don't have any conflict with the `objectID` of the underlying models.
741
1057
 
@@ -773,9 +1089,9 @@ end
773
1089
 
774
1090
  ***Notes:*** If you target a single index from several models, you must never use `MyModel.reindex` and only use `MyModel.reindex!`. The `reindex` method uses a temporary index to perform an atomic reindexing: if you use it, the resulting index will only contain records for the current model because it will not reindex the others.
775
1091
 
776
- ### Target multiple indexes
1092
+ ## Target multiple indices
777
1093
 
778
- You can index a record in several indexes using the <code>add_index</code> method:
1094
+ You can index a record in several indices using the <code>add_index</code> method:
779
1095
 
780
1096
  ```ruby
781
1097
  class Book < ActiveRecord::Base
@@ -788,7 +1104,7 @@ class Book < ActiveRecord::Base
788
1104
 
789
1105
  # store all books in index 'SECURED_INDEX_NAME'
790
1106
  algoliasearch index_name: SECURED_INDEX_NAME do
791
- attributesToIndex [:name, :author]
1107
+ searchableAttributes [:name, :author]
792
1108
  # convert security to tags
793
1109
  tags do
794
1110
  [released ? 'public' : 'private', premium ? 'premium' : 'standard']
@@ -796,7 +1112,7 @@ class Book < ActiveRecord::Base
796
1112
 
797
1113
  # store all 'public' (released and not premium) books in index 'PUBLIC_INDEX_NAME'
798
1114
  add_index PUBLIC_INDEX_NAME, if: :public? do
799
- attributesToIndex [:name, :author]
1115
+ searchableAttributes [:name, :author]
800
1116
  end
801
1117
  end
802
1118
 
@@ -816,198 +1132,15 @@ Book.raw_search 'foo bar', index: 'Book_by_editor'
816
1132
  Book.search 'foo bar', index: 'Book_by_editor'
817
1133
  ```
818
1134
 
819
- ## Features
820
1135
 
821
- ### Tags
822
1136
 
823
- Use the <code>tags</code> method to add tags to your record:
1137
+ # Testing
824
1138
 
825
- ```ruby
826
- class Contact < ActiveRecord::Base
827
- include AlgoliaSearch
828
1139
 
829
- algoliasearch do
830
- tags ['trusted']
831
- end
832
- end
833
- ```
834
-
835
- or using dynamical values:
836
-
837
- ```ruby
838
- class Contact < ActiveRecord::Base
839
- include AlgoliaSearch
840
-
841
- algoliasearch do
842
- tags do
843
- [first_name.blank? || last_name.blank? ? 'partial' : 'full', has_valid_email? ? 'valid_email' : 'invalid_email']
844
- end
845
- end
846
- end
847
- ```
848
-
849
- At query time, specify <code>{ tagFilters: 'tagvalue' }</code> or <code>{ tagFilters: ['tagvalue1', 'tagvalue2'] }</code> as search parameters to restrict the result set to specific tags.
850
-
851
- ### Search
852
-
853
- ***Notes:*** We recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-js) to perform queries directly from the end-user browser without going through your server.
854
-
855
- A search returns ORM-compliant objects reloading them from your database. We recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-js) to perform queries to decrease the overall latency and offload your servers.
856
-
857
-
858
- ```ruby
859
- hits = Contact.search("jon doe")
860
- p hits
861
- p hits.raw_answer # to get the original JSON raw answer
862
- ```
863
-
864
- A `highlight_result` attribute is added to each ORM object:
865
-
866
- ```ruby
867
- hits[0].highlight_result['first_name']['value']
868
- ```
869
1140
 
870
- If you want to retrieve the raw JSON answer from the API, without re-loading the objects from the database, you can use:
1141
+ ## Notes
871
1142
 
872
- ```ruby
873
- json_answer = Contact.raw_search("jon doe")
874
- p json_answer
875
- p json_answer['hits']
876
- p json_answer['facets']
877
- ```
878
-
879
- Search parameters can be specified either through the index's [settings](https://github.com/algolia/algoliasearch-client-ruby#index-settings-parameters) statically in your model or dynamically at search time specifying [search parameters](https://github.com/algolia/algoliasearch-client-ruby#search) as second argument of the `search` method:
880
-
881
- ```ruby
882
- class Contact < ActiveRecord::Base
883
- include AlgoliaSearch
884
-
885
- algoliasearch do
886
- attribute :first_name, :last_name, :email
887
-
888
- # default search parameters stored in the index settings
889
- minWordSizeForApprox1 4
890
- minWordSizeForApprox2 8
891
- hitsPerPage 42
892
- end
893
- end
894
- ```
895
-
896
-
897
- ```ruby
898
- # dynamical search parameters
899
- p Contact.raw_search("jon doe", { :hitsPerPage => 5, :page => 2 })
900
- ```
901
-
902
- ### Faceting
903
-
904
- Facets can be retrieved calling the extra `facets` method of the search answer.
905
-
906
- ```ruby
907
- class Contact < ActiveRecord::Base
908
- include AlgoliaSearch
909
-
910
- algoliasearch do
911
- # [...]
912
-
913
- # specify the list of attributes available for faceting
914
- attributesForFaceting [:company, :zip_code]
915
- end
916
- end
917
- ```
918
-
919
-
920
- ```ruby
921
- hits = Contact.search("jon doe", { :facets => '*' })
922
- p hits # ORM-compliant array of objects
923
- p hits.facets # extra method added to retrieve facets
924
- p hits.facets['company'] # facet values+count of facet 'company'
925
- p hits.facets['zip_code'] # facet values+count of facet 'zip_code'
926
- ```
927
-
928
-
929
- ```ruby
930
- raw_json = Contact.raw_search("jon doe", { :facets => '*' })
931
- p raw_json['facets']
932
- ```
933
-
934
- ### Facet search
935
-
936
- You can also search for facet values.
937
-
938
- ```ruby
939
- Product.search_for_facet_values('category', 'Headphones') # Array of {value, highlighted, count}
940
- ```
941
-
942
- This method can also take any parameter a query can take.
943
- This will adjust the search to only hits which would have matched the query.
944
-
945
- ```ruby
946
- # Only sends back the categories containing red Apple products (and only counts those)
947
- Product.search_for_facet_values('category', 'phone', {
948
- query: 'red',
949
- filters: 'brand:Apple'
950
- }) # Array of phone categories linked to red Apple products
951
- ```
952
-
953
- ### Group by
954
-
955
- More info on distinct for grouping can be found
956
- [here](https://www.algolia.com/doc/guides/search/distinct#distinct-for-grouping).
957
-
958
- ```ruby
959
- class Contact < ActiveRecord::Base
960
- include AlgoliaSearch
961
-
962
- algoliasearch do
963
- # [...]
964
-
965
- # specify the attribute to be used for distinguishing the records
966
- # in this case the records will be grouped by company
967
- attributeForDistinct "company"
968
- end
969
- end
970
- ```
971
-
972
- ### Geo-Search
973
-
974
- Use the <code>geoloc</code> method to localize your record:
975
-
976
- ```ruby
977
- class Contact < ActiveRecord::Base
978
- include AlgoliaSearch
979
-
980
- algoliasearch do
981
- geoloc :lat_attr, :lng_attr
982
- end
983
- end
984
- ```
985
-
986
- At query time, specify <code>{ aroundLatLng: "37.33, -121.89", aroundRadius: 50000 }</code> as search parameters to restrict the result set to 50KM around San Jose.
987
-
988
- ### Caveats
989
-
990
- This gem makes intensive use of Rails' callbacks to trigger the indexing tasks. If you're using methods bypassing `after_validation`, `before_save` or `after_commit` callbacks, it will not index your changes. For example: `update_attribute` doesn't perform validations checks, to perform validations when updating use `update_attributes`.
991
-
992
- ### Timeouts
993
-
994
- You can configure a bunch of timeout threshold by setting the following options at initialization time:
995
-
996
- ```ruby
997
- AlgoliaSearch.configuration = {
998
- application_id: 'YourApplicationID',
999
- api_key: 'YourAPIKey'
1000
- connect_timeout: 2,
1001
- receive_timeout: 30,
1002
- send_timeout: 30,
1003
- batch_timeout: 120,
1004
- search_timeout: 5
1005
- }
1006
- ```
1007
-
1008
- ### Note on testing
1009
-
1010
- To run the specs, please set the <code>ALGOLIA_APPLICATION_ID</code> and <code>ALGOLIA_API_KEY</code> environment variables. Since the tests are creating and removing indexes, DO NOT use your production account.
1143
+ To run the specs, please set the <code>ALGOLIA_APPLICATION_ID</code> and <code>ALGOLIA_API_KEY</code> environment variables. Since the tests are creating and removing indices, DO NOT use your production account.
1011
1144
 
1012
1145
  You may want to disable all indexing (add, update & delete operations) API calls, you can set the `disable_indexing` option:
1013
1146
 
@@ -1015,14 +1148,14 @@ You may want to disable all indexing (add, update & delete operations) API calls
1015
1148
  class User < ActiveRecord::Base
1016
1149
  include AlgoliaSearch
1017
1150
 
1018
- algoliasearch :per_environment => true, :disable_indexing => Rails.env.test? do
1151
+ algoliasearch per_environment: true, disable_indexing: Rails.env.test? do
1019
1152
  end
1020
1153
  end
1021
1154
 
1022
1155
  class User < ActiveRecord::Base
1023
1156
  include AlgoliaSearch
1024
1157
 
1025
- algoliasearch :per_environment => true, :disable_indexing => Proc.new { Rails.env.test? || more_complex_condition } do
1158
+ algoliasearch per_environment: true, disable_indexing: Proc.new { Rails.env.test? || more_complex_condition } do
1026
1159
  end
1027
1160
  end
1028
1161
  ```
@@ -1050,3 +1183,12 @@ describe 'With a mocked client' do
1050
1183
  end
1051
1184
  ```
1052
1185
 
1186
+
1187
+ ## ❓ Troubleshooting
1188
+
1189
+ Encountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/api-client/troubleshooting/faq/ruby/) where you will find answers for the most common issues and gotchas with the client.
1190
+
1191
+ ## Use the Dockerfile
1192
+
1193
+ If you want to contribute to this project without installing all its dependencies, you can use our Docker image. Please check our [dedicated guide](DOCKER_README.MD) to learn more.
1194
+