algoliasearch-rails 2.3.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.MD +5 -1
- data/Gemfile +3 -3
- data/Gemfile.lock +9 -5
- data/README.md +1 -6
- data/algoliasearch-rails.gemspec +3 -3
- data/lib/algoliasearch/configuration.rb +11 -18
- data/lib/algoliasearch/pagination/pagy.rb +8 -1
- data/lib/algoliasearch/version.rb +1 -1
- data/lib/algoliasearch-rails.rb +145 -151
- data/spec/spec_helper.rb +9 -5
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29d7bf2701670f9e978131309e79791bf9ac674e3fe965d58bc3bb3e3e87eedb
|
|
4
|
+
data.tar.gz: 19da3705ecddadc270f3bfe923f93fe0336ba9b12b3fc459694c74f981330c9b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c14cccf9397ddc7058a74378c2706f4d2fe185fd1f31024906b4c9e42fc23c9843ad70fda5accdb94fe59c16f7dc5a2848acb39987a2bf77b48e1e4e3e68342
|
|
7
|
+
data.tar.gz: b0e6f450533fc19ee963640d5f0639ec07ce3dbbe369d015a12f5b31047473cbd878f93e16b853763a14821e95e8773d80c0aae2b36baa5b8b8ecbf7eed0fd13
|
data/CHANGELOG.MD
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
-
## [Unreleased](https://github.com/algolia/algoliasearch-rails/compare/
|
|
3
|
+
## [Unreleased](https://github.com/algolia/algoliasearch-rails/compare/3.0.0...master)
|
|
4
|
+
|
|
5
|
+
## [3.0.0](https://github.com/algolia/algoliasearch-rails/compare/2.3.2...3.0.0)
|
|
6
|
+
This new major version leverages the latest version of the Ruby Algolia API client. It also drops (official) support for Rails 5.x and Ruby versions older than 2.5
|
|
7
|
+
For a list of known breaking changes, please refer to [the upgrade guide](./UPGRADING_TO_V3.MD)
|
|
4
8
|
|
|
5
9
|
## [2.3.2](https://github.com/algolia/algoliasearch-rails/compare/2.3.1...2.3.2)
|
|
6
10
|
### Added
|
data/Gemfile
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
source "http://rubygems.org"
|
|
2
2
|
|
|
3
3
|
gem 'json', '>= 1.5.1'
|
|
4
|
-
gem 'algolia', '
|
|
4
|
+
gem 'algolia', '>= 3.5.2'
|
|
5
5
|
|
|
6
6
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
|
7
7
|
gem 'rubysl', '~> 2.0', :platform => :rbx
|
|
@@ -16,7 +16,7 @@ group :test do
|
|
|
16
16
|
else
|
|
17
17
|
gem 'sqlite3', '< 1.4.0', :platform => [:rbx, :ruby]
|
|
18
18
|
end
|
|
19
|
-
gem 'rspec', '
|
|
19
|
+
gem 'rspec', '~> 3.0'
|
|
20
20
|
gem 'jdbc-sqlite3', :platform => :jruby
|
|
21
21
|
gem 'activerecord-jdbc-adapter', :platform => :jruby
|
|
22
22
|
gem 'activerecord-jdbcsqlite3-adapter', :platform => :jruby
|
|
@@ -27,7 +27,7 @@ group :test do
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
group :development do
|
|
30
|
-
gem 'rake', '
|
|
30
|
+
gem 'rake', '>= 10.1.0'
|
|
31
31
|
gem 'rdoc'
|
|
32
32
|
end
|
|
33
33
|
|
data/Gemfile.lock
CHANGED
|
@@ -70,6 +70,7 @@ GEM
|
|
|
70
70
|
faraday-net_http_persistent (>= 0.15, < 3)
|
|
71
71
|
multi_json (~> 1.0)
|
|
72
72
|
net-http-persistent
|
|
73
|
+
base64 (0.2.0)
|
|
73
74
|
builder (3.2.4)
|
|
74
75
|
case_transform (0.2)
|
|
75
76
|
activesupport
|
|
@@ -77,15 +78,16 @@ GEM
|
|
|
77
78
|
connection_pool (2.4.1)
|
|
78
79
|
crass (1.0.6)
|
|
79
80
|
date (3.3.3)
|
|
80
|
-
diff-lcs (1.5.
|
|
81
|
+
diff-lcs (1.5.1)
|
|
81
82
|
erubi (1.12.0)
|
|
82
|
-
faraday (2.
|
|
83
|
+
faraday (2.8.1)
|
|
84
|
+
base64
|
|
83
85
|
faraday-net_http (>= 2.0, < 3.1)
|
|
84
86
|
ruby2_keywords (>= 0.0.4)
|
|
85
87
|
faraday-net_http (3.0.2)
|
|
86
|
-
faraday-net_http_persistent (2.
|
|
88
|
+
faraday-net_http_persistent (2.3.0)
|
|
87
89
|
faraday (~> 2.5)
|
|
88
|
-
net-http-persistent (
|
|
90
|
+
net-http-persistent (>= 4.0.4, < 5)
|
|
89
91
|
globalid (1.1.0)
|
|
90
92
|
activesupport (>= 5.0)
|
|
91
93
|
i18n (1.14.1)
|
|
@@ -109,7 +111,7 @@ GEM
|
|
|
109
111
|
mini_portile2 (2.8.4)
|
|
110
112
|
minitest (5.19.0)
|
|
111
113
|
multi_json (1.15.0)
|
|
112
|
-
net-http-persistent (4.0.
|
|
114
|
+
net-http-persistent (4.0.4)
|
|
113
115
|
connection_pool (~> 2.2)
|
|
114
116
|
net-imap (0.3.7)
|
|
115
117
|
date
|
|
@@ -124,6 +126,7 @@ GEM
|
|
|
124
126
|
nokogiri (1.13.10)
|
|
125
127
|
mini_portile2 (~> 2.8.0)
|
|
126
128
|
racc (~> 1.4)
|
|
129
|
+
pagy (6.5.0)
|
|
127
130
|
psych (5.1.0)
|
|
128
131
|
stringio
|
|
129
132
|
racc (1.7.1)
|
|
@@ -201,6 +204,7 @@ DEPENDENCIES
|
|
|
201
204
|
jdbc-sqlite3
|
|
202
205
|
json (>= 1.5.1)
|
|
203
206
|
kaminari (< 1)
|
|
207
|
+
pagy
|
|
204
208
|
rails (~> 6.1)
|
|
205
209
|
rake (~> 10.1.0)
|
|
206
210
|
rdoc
|
data/README.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
|
|
28
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
|
|
29
|
+
Rails 6.x and 7.x are supported.
|
|
30
30
|
|
|
31
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/).
|
|
32
32
|
|
|
@@ -128,11 +128,6 @@ You can configure a various timeout thresholds by setting the following options
|
|
|
128
128
|
AlgoliaSearch.configuration = {
|
|
129
129
|
application_id: 'YourApplicationID',
|
|
130
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
131
|
}
|
|
137
132
|
```
|
|
138
133
|
|
data/algoliasearch-rails.gemspec
CHANGED
|
@@ -79,7 +79,7 @@ Gem::Specification.new do |s|
|
|
|
79
79
|
|
|
80
80
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
81
81
|
s.add_runtime_dependency(%q<json>, [">= 1.5.1"])
|
|
82
|
-
s.add_runtime_dependency(%q<algolia>, ["
|
|
82
|
+
s.add_runtime_dependency(%q<algolia>, [">= 3.5.2"])
|
|
83
83
|
s.add_development_dependency(%q<will_paginate>, [">= 2.3.15"])
|
|
84
84
|
s.add_development_dependency(%q<kaminari>, [">= 0"])
|
|
85
85
|
s.add_development_dependency(%q<pagy>, [">= 0"])
|
|
@@ -87,11 +87,11 @@ Gem::Specification.new do |s|
|
|
|
87
87
|
s.add_development_dependency "rdoc"
|
|
88
88
|
else
|
|
89
89
|
s.add_dependency(%q<json>, [">= 1.5.1"])
|
|
90
|
-
s.add_dependency(%q<algolia>, ["
|
|
90
|
+
s.add_dependency(%q<algolia>, [">= 3.5.2"])
|
|
91
91
|
end
|
|
92
92
|
else
|
|
93
93
|
s.add_dependency(%q<json>, [">= 1.5.1"])
|
|
94
|
-
s.add_dependency(%q<algolia>, ["
|
|
94
|
+
s.add_dependency(%q<algolia>, [">= 3.5.2"])
|
|
95
95
|
end
|
|
96
96
|
end
|
|
97
97
|
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
module AlgoliaSearch
|
|
2
2
|
module Configuration
|
|
3
|
-
REQUIRED_CONFIGURATION = {
|
|
4
|
-
user_agent: "Algolia for Rails (#{AlgoliaSearch::VERSION}); Rails (#{defined?(::Rails::VERSION::STRING) ? ::Rails::VERSION::STRING : 'unknown'})",
|
|
5
|
-
symbolize_keys: false
|
|
6
|
-
}
|
|
7
|
-
|
|
8
3
|
def initialize
|
|
9
4
|
@client = nil
|
|
10
5
|
end
|
|
@@ -14,19 +9,8 @@ module AlgoliaSearch
|
|
|
14
9
|
end
|
|
15
10
|
|
|
16
11
|
def configuration=(configuration)
|
|
17
|
-
user_agent = [REQUIRED_CONFIGURATION[:user_agent], configuration[:append_to_user_agent]].compact.join('; ')
|
|
18
12
|
@@configuration = default_configuration
|
|
19
|
-
|
|
20
|
-
.merge(REQUIRED_CONFIGURATION)
|
|
21
|
-
.merge({ user_agent: user_agent })
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def client_opts
|
|
25
|
-
@@opts ||= {}
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def client_opts=(opts)
|
|
29
|
-
@@opts = opts
|
|
13
|
+
.merge(configuration)
|
|
30
14
|
end
|
|
31
15
|
|
|
32
16
|
def client
|
|
@@ -38,7 +22,16 @@ module AlgoliaSearch
|
|
|
38
22
|
end
|
|
39
23
|
|
|
40
24
|
def setup_client
|
|
41
|
-
@client = Algolia::
|
|
25
|
+
@client = Algolia::SearchClient.create(
|
|
26
|
+
@@configuration[:application_id],
|
|
27
|
+
@@configuration[:api_key],
|
|
28
|
+
{
|
|
29
|
+
user_agent_segments: [
|
|
30
|
+
"Algolia for Rails (#{AlgoliaSearch::VERSION})",
|
|
31
|
+
"Rails (#{defined?(::Rails::VERSION::STRING) ? ::Rails::VERSION::STRING : 'unknown'})",
|
|
32
|
+
@@configuration[:append_to_user_agent]
|
|
33
|
+
].compact
|
|
34
|
+
})
|
|
42
35
|
end
|
|
43
36
|
|
|
44
37
|
def default_configuration
|
|
@@ -5,6 +5,7 @@ end
|
|
|
5
5
|
module AlgoliaSearch
|
|
6
6
|
module Pagination
|
|
7
7
|
class Pagy
|
|
8
|
+
|
|
8
9
|
def self.create(results, total_hits, options = {})
|
|
9
10
|
vars = {
|
|
10
11
|
count: total_hits,
|
|
@@ -12,7 +13,13 @@ module AlgoliaSearch
|
|
|
12
13
|
items: options[:per_page]
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
pagy_version = Gem::Version.new(::Pagy::VERSION)
|
|
17
|
+
pagy = if pagy_version >= Gem::Version.new('9.0')
|
|
18
|
+
::Pagy.new(**vars)
|
|
19
|
+
else
|
|
20
|
+
::Pagy.new(vars)
|
|
21
|
+
end
|
|
22
|
+
|
|
16
23
|
[pagy, results]
|
|
17
24
|
end
|
|
18
25
|
end
|
data/lib/algoliasearch-rails.rb
CHANGED
|
@@ -95,7 +95,6 @@ module AlgoliaSearch
|
|
|
95
95
|
|
|
96
96
|
def use_serializer(serializer)
|
|
97
97
|
@serializer = serializer
|
|
98
|
-
# instance_variable_set("@serializer", serializer)
|
|
99
98
|
end
|
|
100
99
|
|
|
101
100
|
def attribute(*names, &block)
|
|
@@ -177,16 +176,7 @@ module AlgoliaSearch
|
|
|
177
176
|
end
|
|
178
177
|
|
|
179
178
|
attributes.merge!(attributes_to_hash(@additional_attributes, object)) if @additional_attributes
|
|
180
|
-
|
|
181
|
-
if @options[:sanitize]
|
|
182
|
-
sanitizer = begin
|
|
183
|
-
::HTML::FullSanitizer.new
|
|
184
|
-
rescue NameError
|
|
185
|
-
# from rails 4.2
|
|
186
|
-
::Rails::Html::FullSanitizer.new
|
|
187
|
-
end
|
|
188
|
-
attributes = sanitize_attributes(attributes, sanitizer)
|
|
189
|
-
end
|
|
179
|
+
attributes = sanitize_attributes(attributes, Rails::Html::FullSanitizer.new) if @options[:sanitize]
|
|
190
180
|
|
|
191
181
|
if @options[:force_utf8_encoding] && Object.const_defined?(:RUBY_VERSION) && RUBY_VERSION.to_f > 1.8
|
|
192
182
|
attributes = encode_attributes(attributes)
|
|
@@ -241,10 +231,20 @@ module AlgoliaSearch
|
|
|
241
231
|
end
|
|
242
232
|
|
|
243
233
|
def to_settings
|
|
234
|
+
settings = to_hash
|
|
235
|
+
|
|
236
|
+
# Remove the synonyms setting since those need to be set separately
|
|
237
|
+
settings.delete(:synonyms)
|
|
238
|
+
settings.delete("synonyms")
|
|
239
|
+
|
|
240
|
+
Algolia::Search::IndexSettings.new(settings)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def to_hash
|
|
244
244
|
settings = {}
|
|
245
245
|
OPTIONS.each do |k|
|
|
246
246
|
v = get_setting(k)
|
|
247
|
-
settings[k] = v if !v.nil?
|
|
247
|
+
settings[setting_name(k)] = v if !v.nil?
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
if !@options[:replica]
|
|
@@ -256,9 +256,18 @@ module AlgoliaSearch
|
|
|
256
256
|
end
|
|
257
257
|
settings.delete(:replicas) if settings[:replicas].empty?
|
|
258
258
|
end
|
|
259
|
+
|
|
259
260
|
settings
|
|
260
261
|
end
|
|
261
262
|
|
|
263
|
+
def setting_name(name)
|
|
264
|
+
name.to_s.gsub(/::/, '/').
|
|
265
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
266
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
|
267
|
+
tr("-", "_").
|
|
268
|
+
downcase
|
|
269
|
+
end
|
|
270
|
+
|
|
262
271
|
def add_index(index_name, options = {}, &block)
|
|
263
272
|
raise ArgumentError.new('Cannot specify additional index on a replica index') if @options[:replica]
|
|
264
273
|
raise ArgumentError.new('No block given') if !block_given?
|
|
@@ -287,70 +296,6 @@ module AlgoliaSearch
|
|
|
287
296
|
autoload :AlgoliaJob, 'algoliasearch/algolia_job'
|
|
288
297
|
end
|
|
289
298
|
|
|
290
|
-
# this class wraps an Algolia::Index object ensuring all raised exceptions
|
|
291
|
-
# are correctly logged or thrown depending on the `raise_on_failure` option
|
|
292
|
-
class SafeIndex
|
|
293
|
-
def initialize(name, raise_on_failure)
|
|
294
|
-
@index = AlgoliaSearch.client.init_index(name)
|
|
295
|
-
@raise_on_failure = raise_on_failure.nil? || raise_on_failure
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
::Algolia::Search::Index.instance_methods(false).each do |m|
|
|
299
|
-
define_method(m) do |*args, &block|
|
|
300
|
-
SafeIndex.log_or_throw(m, @raise_on_failure) do
|
|
301
|
-
@index.send(m, *args, &block)
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
# special handling of wait_task to handle null task_id
|
|
307
|
-
def wait_task(task_id)
|
|
308
|
-
return if task_id.nil? && !@raise_on_failure # ok
|
|
309
|
-
SafeIndex.log_or_throw(:wait_task, @raise_on_failure) do
|
|
310
|
-
@index.wait_task(task_id)
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
# special handling of get_settings to avoid raising errors on 404
|
|
315
|
-
def get_settings(*args)
|
|
316
|
-
SafeIndex.log_or_throw(:get_settings, @raise_on_failure) do
|
|
317
|
-
begin
|
|
318
|
-
@index.get_settings(*args)
|
|
319
|
-
rescue Algolia::AlgoliaHttpError => e
|
|
320
|
-
return {} if e.code == 404 # not fatal
|
|
321
|
-
raise e
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
# expose move as well
|
|
327
|
-
def self.move_index(old_name, new_name)
|
|
328
|
-
SafeIndex.log_or_throw(:move_index, true) do
|
|
329
|
-
AlgoliaSearch.client.move_index(old_name, new_name)
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
private
|
|
334
|
-
def self.log_or_throw(method, raise_on_failure, &block)
|
|
335
|
-
begin
|
|
336
|
-
yield
|
|
337
|
-
rescue Algolia::AlgoliaError => e
|
|
338
|
-
raise e if raise_on_failure
|
|
339
|
-
# log the error
|
|
340
|
-
(Rails.logger || Logger.new(STDOUT)).error("[algoliasearch-rails] #{e.message}")
|
|
341
|
-
# return something
|
|
342
|
-
case method.to_s
|
|
343
|
-
when 'search'
|
|
344
|
-
# some attributes are required
|
|
345
|
-
{ 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facets' => {}, 'error' => e }
|
|
346
|
-
else
|
|
347
|
-
# empty answer
|
|
348
|
-
{ 'error' => e }
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
|
|
354
299
|
# these are the class methods added when AlgoliaSearch is included
|
|
355
300
|
module ClassMethods
|
|
356
301
|
|
|
@@ -367,7 +312,6 @@ module AlgoliaSearch
|
|
|
367
312
|
alias_method :raw_search, :algolia_raw_search unless method_defined? :raw_search
|
|
368
313
|
alias_method :search_facet, :algolia_search_facet unless method_defined? :search_facet
|
|
369
314
|
alias_method :search_for_facet_values, :algolia_search_for_facet_values unless method_defined? :search_for_facet_values
|
|
370
|
-
alias_method :index, :algolia_index unless method_defined? :index
|
|
371
315
|
alias_method :index_name, :algolia_index_name unless method_defined? :index_name
|
|
372
316
|
alias_method :must_reindex?, :algolia_must_reindex? unless method_defined? :must_reindex?
|
|
373
317
|
end
|
|
@@ -497,7 +441,8 @@ module AlgoliaSearch
|
|
|
497
441
|
return if algolia_without_auto_index_scope
|
|
498
442
|
algolia_configurations.each do |options, settings|
|
|
499
443
|
next if algolia_indexing_disabled?(options)
|
|
500
|
-
|
|
444
|
+
algolia_ensure_init(options, settings)
|
|
445
|
+
index_name = algolia_index_name(options)
|
|
501
446
|
next if options[:replica]
|
|
502
447
|
last_task = nil
|
|
503
448
|
|
|
@@ -505,7 +450,7 @@ module AlgoliaSearch
|
|
|
505
450
|
if algolia_conditional_index?(options)
|
|
506
451
|
# delete non-indexable objects
|
|
507
452
|
ids = group.select { |o| !algolia_indexable?(o, options) }.map { |o| algolia_object_id_of(o, options) }
|
|
508
|
-
|
|
453
|
+
AlgoliaSearch.client.delete_objects(index_name, ids.select { |id| !id.blank? })
|
|
509
454
|
# select only indexable objects
|
|
510
455
|
group = group.select { |o| algolia_indexable?(o, options) }
|
|
511
456
|
end
|
|
@@ -516,9 +461,9 @@ module AlgoliaSearch
|
|
|
516
461
|
end
|
|
517
462
|
attributes.merge 'objectID' => algolia_object_id_of(o, options)
|
|
518
463
|
end
|
|
519
|
-
last_task =
|
|
464
|
+
last_task = AlgoliaSearch.client.save_objects(index_name, objects).last.task_id
|
|
520
465
|
end
|
|
521
|
-
|
|
466
|
+
AlgoliaSearch.client.wait_for_task(index_name, last_task) if last_task and (synchronous || options[:synchronous])
|
|
522
467
|
end
|
|
523
468
|
nil
|
|
524
469
|
end
|
|
@@ -530,28 +475,30 @@ module AlgoliaSearch
|
|
|
530
475
|
next if algolia_indexing_disabled?(options)
|
|
531
476
|
next if options[:replica]
|
|
532
477
|
|
|
478
|
+
algolia_ensure_init(options, settings)
|
|
479
|
+
index_name = algolia_index_name(options)
|
|
480
|
+
|
|
533
481
|
# fetch the master settings
|
|
534
|
-
|
|
535
|
-
master_settings = master_index.get_settings rescue {} # if master doesn't exist yet
|
|
482
|
+
master_settings = AlgoliaSearch.client.get_settings(index_name).to_hash rescue {} # if master doesn't exist yet
|
|
536
483
|
master_exists = master_settings != {}
|
|
537
|
-
master_settings.merge!(
|
|
484
|
+
master_settings.merge!(settings.to_hash)
|
|
538
485
|
|
|
539
486
|
# remove the replicas of the temporary index
|
|
540
487
|
master_settings.delete :replicas
|
|
541
488
|
master_settings.delete 'replicas'
|
|
542
489
|
|
|
543
490
|
# init temporary index
|
|
544
|
-
|
|
545
|
-
tmp_index_name = "#{src_index_name}.tmp"
|
|
491
|
+
tmp_index_name = "#{index_name}.tmp"
|
|
546
492
|
tmp_options = options.merge({ :index_name => tmp_index_name })
|
|
547
493
|
tmp_options.delete(:per_environment) # already included in the temporary index_name
|
|
548
494
|
tmp_settings = settings.dup
|
|
549
495
|
|
|
550
496
|
if options[:check_settings] == false && master_exists
|
|
551
|
-
AlgoliaSearch.client.
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
497
|
+
task_id = AlgoliaSearch.client.operation_index(
|
|
498
|
+
index_name,
|
|
499
|
+
Algolia::Search::OperationIndexParams.new(operation: Algolia::Search::OperationType::COPY, destination: tmp_index_name, scope: %w[settings synonyms rules])
|
|
500
|
+
).task_id
|
|
501
|
+
AlgoliaSearch.client.wait_for_task(index_name, task_id)
|
|
555
502
|
end
|
|
556
503
|
|
|
557
504
|
algolia_find_in_batches(batch_size) do |group|
|
|
@@ -560,11 +507,15 @@ module AlgoliaSearch
|
|
|
560
507
|
group = group.select { |o| algolia_indexable?(o, tmp_options) }
|
|
561
508
|
end
|
|
562
509
|
objects = group.map { |o| tmp_settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o, tmp_options) }
|
|
563
|
-
|
|
510
|
+
|
|
511
|
+
AlgoliaSearch.client.save_objects(tmp_index_name, objects)
|
|
564
512
|
end
|
|
565
513
|
|
|
566
|
-
|
|
567
|
-
|
|
514
|
+
task_id = AlgoliaSearch.client.operation_index(
|
|
515
|
+
tmp_index_name,
|
|
516
|
+
Algolia::Search::OperationIndexParams.new(operation: "move", destination: index_name)
|
|
517
|
+
).task_id
|
|
518
|
+
AlgoliaSearch.client.wait_for_task(index_name, task_id) if synchronous || options[:synchronous]
|
|
568
519
|
end
|
|
569
520
|
nil
|
|
570
521
|
end
|
|
@@ -572,27 +523,40 @@ module AlgoliaSearch
|
|
|
572
523
|
def algolia_set_settings(synchronous = false)
|
|
573
524
|
algolia_configurations.each do |options, settings|
|
|
574
525
|
if options[:primary_settings] && options[:inherit]
|
|
575
|
-
primary = options[:primary_settings].to_settings
|
|
526
|
+
primary = options[:primary_settings].to_settings.to_hash
|
|
576
527
|
primary.delete :replicas
|
|
577
528
|
primary.delete 'replicas'
|
|
578
|
-
final_settings = primary.merge(settings.to_settings)
|
|
529
|
+
final_settings = primary.merge(settings.to_settings.to_hash)
|
|
579
530
|
else
|
|
580
|
-
final_settings = settings.to_settings
|
|
531
|
+
final_settings = settings.to_settings.to_hash
|
|
581
532
|
end
|
|
582
533
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
534
|
+
s = final_settings.map do |k, v|
|
|
535
|
+
[settings.setting_name(k), v]
|
|
536
|
+
end.to_h
|
|
537
|
+
|
|
538
|
+
synonyms = s.delete("synonyms") || s.delete(:synonyms)
|
|
539
|
+
unless synonyms.nil? || synonyms.empty?
|
|
540
|
+
resp = AlgoliaSearch.client.save_synonyms(index_name,synonyms.map {|s| Algolia::Search::SynonymHit.new({object_id: s.join("-"), synonyms: s, type: "synonym"}) } )
|
|
541
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if synchronous || options[:synchronous]
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
resp = AlgoliaSearch.client.set_settings(index_name, Algolia::Search::IndexSettings.new(s))
|
|
545
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if synchronous || options[:synchronous]
|
|
586
546
|
end
|
|
587
547
|
end
|
|
588
548
|
|
|
589
549
|
def algolia_index_objects(objects, synchronous = false)
|
|
590
550
|
algolia_configurations.each do |options, settings|
|
|
591
551
|
next if algolia_indexing_disabled?(options)
|
|
592
|
-
|
|
552
|
+
algolia_ensure_init(options, settings)
|
|
553
|
+
index_name = algolia_index_name(options)
|
|
554
|
+
|
|
593
555
|
next if options[:replica]
|
|
594
|
-
|
|
595
|
-
|
|
556
|
+
tasks = AlgoliaSearch.client.save_objects(index_name, objects.map { |o| settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o, options) })
|
|
557
|
+
tasks.each do |task|
|
|
558
|
+
AlgoliaSearch.client.wait_for_task(index_name, task.task_id) if synchronous || options[:synchronous]
|
|
559
|
+
end
|
|
596
560
|
end
|
|
597
561
|
end
|
|
598
562
|
|
|
@@ -600,22 +564,23 @@ module AlgoliaSearch
|
|
|
600
564
|
return if algolia_without_auto_index_scope
|
|
601
565
|
algolia_configurations.each do |options, settings|
|
|
602
566
|
next if algolia_indexing_disabled?(options)
|
|
567
|
+
|
|
603
568
|
object_id = algolia_object_id_of(object, options)
|
|
604
|
-
|
|
569
|
+
index_name = algolia_index_name(options)
|
|
570
|
+
algolia_ensure_init(options, settings)
|
|
605
571
|
next if options[:replica]
|
|
572
|
+
|
|
606
573
|
if algolia_indexable?(object, options)
|
|
607
574
|
raise ArgumentError.new("Cannot index a record with a blank objectID") if object_id.blank?
|
|
575
|
+
resp = AlgoliaSearch.client.save_object(index_name, settings.get_attributes(object).merge({ 'objectID' => algolia_object_id_of(object, options) }))
|
|
608
576
|
if synchronous || options[:synchronous]
|
|
609
|
-
|
|
610
|
-
else
|
|
611
|
-
index.save_object(settings.get_attributes(object).merge 'objectID' => algolia_object_id_of(object, options))
|
|
577
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id)
|
|
612
578
|
end
|
|
613
579
|
elsif algolia_conditional_index?(options) && !object_id.blank?
|
|
614
580
|
# remove non-indexable objects
|
|
581
|
+
resp = AlgoliaSearch.client.delete_object(index_name, object_id)
|
|
615
582
|
if synchronous || options[:synchronous]
|
|
616
|
-
|
|
617
|
-
else
|
|
618
|
-
index.delete_object(object_id)
|
|
583
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id)
|
|
619
584
|
end
|
|
620
585
|
end
|
|
621
586
|
end
|
|
@@ -628,12 +593,14 @@ module AlgoliaSearch
|
|
|
628
593
|
raise ArgumentError.new("Cannot index a record with a blank objectID") if object_id.blank?
|
|
629
594
|
algolia_configurations.each do |options, settings|
|
|
630
595
|
next if algolia_indexing_disabled?(options)
|
|
631
|
-
|
|
596
|
+
algolia_ensure_init(options, settings)
|
|
597
|
+
index_name = algolia_index_name(options)
|
|
598
|
+
|
|
632
599
|
next if options[:replica]
|
|
600
|
+
|
|
601
|
+
resp = AlgoliaSearch.client.delete_object(index_name, object_id)
|
|
633
602
|
if synchronous || options[:synchronous]
|
|
634
|
-
|
|
635
|
-
else
|
|
636
|
-
index.delete_object(object_id)
|
|
603
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id)
|
|
637
604
|
end
|
|
638
605
|
end
|
|
639
606
|
nil
|
|
@@ -641,22 +608,38 @@ module AlgoliaSearch
|
|
|
641
608
|
|
|
642
609
|
def algolia_clear_index!(synchronous = false)
|
|
643
610
|
algolia_configurations.each do |options, settings|
|
|
644
|
-
next if algolia_indexing_disabled?(options)
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
611
|
+
next if algolia_indexing_disabled?(options) || options[:replica]
|
|
612
|
+
|
|
613
|
+
algolia_ensure_init(options, settings)
|
|
614
|
+
index_name = algolia_index_name(options)
|
|
615
|
+
res = AlgoliaSearch.client.clear_objects(index_name)
|
|
616
|
+
|
|
617
|
+
if synchronous || options[:synchronous]
|
|
618
|
+
AlgoliaSearch.client.wait_for_task(index_name, res.task_id)
|
|
619
|
+
end
|
|
649
620
|
end
|
|
650
621
|
nil
|
|
651
622
|
end
|
|
652
623
|
|
|
624
|
+
|
|
653
625
|
def algolia_raw_search(q, params = {})
|
|
654
|
-
|
|
626
|
+
index_name_base = params.delete(:index) ||
|
|
655
627
|
params.delete('index') ||
|
|
656
628
|
params.delete(:replica) ||
|
|
657
629
|
params.delete('replica')
|
|
658
|
-
|
|
659
|
-
|
|
630
|
+
|
|
631
|
+
opts = algoliasearch_options
|
|
632
|
+
unless index_name_base.nil?
|
|
633
|
+
algolia_configurations.each do |o, s|
|
|
634
|
+
if o[:index_name].to_s == index_name_base.to_s
|
|
635
|
+
opts = o
|
|
636
|
+
ensure_algolia_index(index_name_base)
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
index_name = algolia_index_name(opts, index_name_base)
|
|
642
|
+
AlgoliaSearch.client.search_single_index(index_name,Hash[params.to_h.map { |k,v| [k.to_s, v.to_s] }].merge({query: q})).to_hash
|
|
660
643
|
end
|
|
661
644
|
|
|
662
645
|
module AdditionalMethods
|
|
@@ -672,7 +655,7 @@ module AlgoliaSearch
|
|
|
672
655
|
end
|
|
673
656
|
|
|
674
657
|
def algolia_facets
|
|
675
|
-
@algolia_json[
|
|
658
|
+
@algolia_json[:facets]
|
|
676
659
|
end
|
|
677
660
|
|
|
678
661
|
private
|
|
@@ -688,7 +671,7 @@ module AlgoliaSearch
|
|
|
688
671
|
params[:page] -= 1 if params[:page].to_i > 0
|
|
689
672
|
end
|
|
690
673
|
json = algolia_raw_search(q, params)
|
|
691
|
-
hit_ids = json[
|
|
674
|
+
hit_ids = json[:hits].map { |hit| hit[:objectID] }
|
|
692
675
|
if defined?(::Mongoid::Document) && self.include?(::Mongoid::Document)
|
|
693
676
|
condition_key = algolia_object_id_method.in
|
|
694
677
|
else
|
|
@@ -697,18 +680,18 @@ module AlgoliaSearch
|
|
|
697
680
|
results_by_id = algoliasearch_options[:type].where(condition_key => hit_ids).index_by do |hit|
|
|
698
681
|
algolia_object_id_of(hit)
|
|
699
682
|
end
|
|
700
|
-
results = json[
|
|
701
|
-
o = results_by_id[hit[
|
|
683
|
+
results = json[:hits].map do |hit|
|
|
684
|
+
o = results_by_id[hit[:objectID].to_s]
|
|
702
685
|
if o
|
|
703
|
-
o.highlight_result = hit[
|
|
704
|
-
o.snippet_result = hit[
|
|
686
|
+
o.highlight_result = hit[:_highlightResult]
|
|
687
|
+
o.snippet_result = hit[:_snippetResult]
|
|
705
688
|
o
|
|
706
689
|
end
|
|
707
690
|
end.compact
|
|
708
691
|
# Algolia has a default limit of 1000 retrievable hits
|
|
709
|
-
total_hits = json[
|
|
710
|
-
json[
|
|
711
|
-
res = AlgoliaSearch::Pagination.create(results, total_hits, algoliasearch_options.merge({ :page => json[
|
|
692
|
+
total_hits = json[:nbHits].to_i < json[:nbPages].to_i * json[:hitsPerPage].to_i ?
|
|
693
|
+
json[:nbHits].to_i: json[:nbPages].to_i * json[:hitsPerPage].to_i
|
|
694
|
+
res = AlgoliaSearch::Pagination.create(results, total_hits, algoliasearch_options.merge({ :page => json[:page].to_i + 1, :per_page => json[:hitsPerPage] }))
|
|
712
695
|
res.extend(AdditionalMethods)
|
|
713
696
|
res.send(:algolia_init_raw_answer, json)
|
|
714
697
|
res
|
|
@@ -719,15 +702,16 @@ module AlgoliaSearch
|
|
|
719
702
|
params.delete('index') ||
|
|
720
703
|
params.delete(:replica) ||
|
|
721
704
|
params.delete('replicas')
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
705
|
+
index_name ||= algolia_index_name(algoliasearch_options)
|
|
706
|
+
req = Algolia::Search::SearchForFacetValuesRequest.new({facet_query: text, params: params.to_query})
|
|
707
|
+
|
|
708
|
+
AlgoliaSearch.client.search_for_facet_values(index_name, facet, req).facet_hits
|
|
725
709
|
end
|
|
726
710
|
|
|
727
711
|
# deprecated (renaming)
|
|
728
712
|
alias :algolia_search_facet :algolia_search_for_facet_values
|
|
729
713
|
|
|
730
|
-
def
|
|
714
|
+
def ensure_algolia_index(name = nil)
|
|
731
715
|
if name
|
|
732
716
|
algolia_configurations.each do |o, s|
|
|
733
717
|
return algolia_ensure_init(o, s) if o[:index_name].to_s == name.to_s
|
|
@@ -737,9 +721,9 @@ module AlgoliaSearch
|
|
|
737
721
|
algolia_ensure_init
|
|
738
722
|
end
|
|
739
723
|
|
|
740
|
-
def algolia_index_name(options = nil)
|
|
724
|
+
def algolia_index_name(options = nil, index_name = nil)
|
|
741
725
|
options ||= algoliasearch_options
|
|
742
|
-
name = options[:index_name] || model_name.to_s.gsub('::', '_')
|
|
726
|
+
name = index_name || options[:index_name] || model_name.to_s.gsub('::', '_')
|
|
743
727
|
name = "#{name}_#{Rails.env.to_s}" if options[:per_environment]
|
|
744
728
|
name
|
|
745
729
|
end
|
|
@@ -774,36 +758,46 @@ module AlgoliaSearch
|
|
|
774
758
|
|
|
775
759
|
protected
|
|
776
760
|
|
|
777
|
-
def algolia_ensure_init(options = nil, settings = nil,
|
|
761
|
+
def algolia_ensure_init(options = nil, settings = nil, index_settings_hash = nil)
|
|
778
762
|
raise ArgumentError.new('No `algoliasearch` block found in your model.') if algoliasearch_settings.nil?
|
|
779
763
|
|
|
780
|
-
@
|
|
764
|
+
@algolia_indexes_init ||= {}
|
|
781
765
|
|
|
782
766
|
options ||= algoliasearch_options
|
|
783
767
|
settings ||= algoliasearch_settings
|
|
784
768
|
|
|
785
|
-
return
|
|
769
|
+
return if @algolia_indexes_init[settings]
|
|
786
770
|
|
|
787
|
-
|
|
771
|
+
index_name = algolia_index_name(options)
|
|
788
772
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
773
|
+
|
|
774
|
+
index_settings_hash ||= settings.to_settings.to_hash
|
|
775
|
+
index_settings_hash = options[:primary_settings].to_settings.to_hash.merge(index_settings_hash) if options[:inherit]
|
|
776
|
+
replicas = index_settings_hash.delete(:replicas) || index_settings_hash.delete('replicas')
|
|
777
|
+
index_settings_hash[:replicas] = replicas unless replicas.nil? || options[:inherit]
|
|
794
778
|
|
|
795
779
|
options[:check_settings] = true if options[:check_settings].nil?
|
|
796
780
|
|
|
797
781
|
current_settings = if options[:check_settings] && !algolia_indexing_disabled?(options)
|
|
798
|
-
|
|
782
|
+
AlgoliaSearch.client.get_settings(index_name, {:getVersion => 1}).to_hash rescue nil # if the index doesn't exist
|
|
799
783
|
end
|
|
800
784
|
|
|
801
|
-
if !algolia_indexing_disabled?(options) && options[:check_settings] && algoliasearch_settings_changed?(current_settings,
|
|
802
|
-
|
|
803
|
-
|
|
785
|
+
if !algolia_indexing_disabled?(options) && options[:check_settings] && algoliasearch_settings_changed?(current_settings, index_settings_hash)
|
|
786
|
+
s = index_settings_hash.map do |k, v|
|
|
787
|
+
[settings.setting_name(k), v]
|
|
788
|
+
end.to_h
|
|
789
|
+
|
|
790
|
+
synonyms = s.delete("synonyms") || s.delete(:synonyms)
|
|
791
|
+
unless synonyms.nil? || synonyms.empty?
|
|
792
|
+
resp = AlgoliaSearch.client.save_synonyms(index_name,synonyms.map {|s| Algolia::Search::SynonymHit.new({object_id: s.join("-"), synonyms: s, type: "synonym"}) } )
|
|
793
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if options[:synchronous]
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
resp = AlgoliaSearch.client.set_settings(index_name, Algolia::Search::IndexSettings.new(s))
|
|
797
|
+
AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if options[:synchronous]
|
|
804
798
|
end
|
|
805
799
|
|
|
806
|
-
|
|
800
|
+
return
|
|
807
801
|
end
|
|
808
802
|
|
|
809
803
|
private
|
|
@@ -843,7 +837,7 @@ module AlgoliaSearch
|
|
|
843
837
|
def algoliasearch_settings_changed?(prev, current)
|
|
844
838
|
return true if prev.nil?
|
|
845
839
|
current.each do |k, v|
|
|
846
|
-
prev_v = prev[k.to_s]
|
|
840
|
+
prev_v = prev[k.to_sym] || prev[k.to_s]
|
|
847
841
|
if v.is_a?(Array) and prev_v.is_a?(Array)
|
|
848
842
|
# compare array of strings, avoiding symbols VS strings comparison
|
|
849
843
|
return true if v.map { |x| x.to_s } != prev_v.map { |x| x.to_s }
|
data/spec/spec_helper.rb
CHANGED
|
@@ -32,8 +32,12 @@ RSpec.configure do |c|
|
|
|
32
32
|
# Remove all indexes setup in this run in local or CI
|
|
33
33
|
c.after(:suite) do
|
|
34
34
|
safe_index_list.each do |i|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
begin
|
|
36
|
+
res = AlgoliaSearch.client.delete_index(i.name)
|
|
37
|
+
AlgoliaSearch.client.wait_for_task(i.name, res.task_id)
|
|
38
|
+
rescue
|
|
39
|
+
# fail gracefully
|
|
40
|
+
end
|
|
37
41
|
end
|
|
38
42
|
end
|
|
39
43
|
end
|
|
@@ -48,7 +52,7 @@ end
|
|
|
48
52
|
|
|
49
53
|
# get a list of safe indexes in local or CI
|
|
50
54
|
def safe_index_list
|
|
51
|
-
list = AlgoliaSearch.client.
|
|
52
|
-
list = list.select { |index| index
|
|
53
|
-
list.sort_by { |index| index
|
|
55
|
+
list = AlgoliaSearch.client.list_indices.items
|
|
56
|
+
list = list.select { |index| index.name.include?(SAFE_INDEX_PREFIX) }
|
|
57
|
+
list.sort_by { |index| index.primary || "" }
|
|
54
58
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: algoliasearch-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Algolia
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -28,16 +28,16 @@ dependencies:
|
|
|
28
28
|
name: algolia
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 3.
|
|
33
|
+
version: 3.5.2
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 3.
|
|
40
|
+
version: 3.5.2
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: will_paginate
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
182
182
|
- !ruby/object:Gem::Version
|
|
183
183
|
version: '0'
|
|
184
184
|
requirements: []
|
|
185
|
-
rubygems_version: 3.
|
|
185
|
+
rubygems_version: 3.1.6
|
|
186
186
|
signing_key:
|
|
187
187
|
specification_version: 4
|
|
188
188
|
summary: AlgoliaSearch integration to your favorite ORM
|