algoliasearch-rails 2.3.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|