algoliasearch-rails 2.3.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c936268f937121a58b8a141df1d9cb5dc25504ff4c25dc3e4d133060162d326
4
- data.tar.gz: 8b2262430fc7edab858ce40e6f46638ceb65ddea27f17b2261e2251c93b725b3
3
+ metadata.gz: 29d7bf2701670f9e978131309e79791bf9ac674e3fe965d58bc3bb3e3e87eedb
4
+ data.tar.gz: 19da3705ecddadc270f3bfe923f93fe0336ba9b12b3fc459694c74f981330c9b
5
5
  SHA512:
6
- metadata.gz: b54ada522502ea04902a815a185d1c7ee016c121ca711a79db89815c3ebf443dd958b68d9b64c7ae48d6260bd423472234babc11c0e6d43f5f0f17793b601d67
7
- data.tar.gz: 7ec62bbe246782b6015be92ec55cf680157116e974c88efc5300f528e7324a437b182e7c6e5cde640d18ae5671b0052fad75338a41ed54e0b307c6a1834eaf77
6
+ metadata.gz: 5c14cccf9397ddc7058a74378c2706f4d2fe185fd1f31024906b4c9e42fc23c9843ad70fda5accdb94fe59c16f7dc5a2848acb39987a2bf77b48e1e4e3e68342
7
+ data.tar.gz: b0e6f450533fc19ee963640d5f0639ec07ce3dbbe369d015a12f5b31047473cbd878f93e16b853763a14821e95e8773d80c0aae2b36baa5b8b8ecbf7eed0fd13
data/CHANGELOG.MD CHANGED
@@ -1,6 +1,14 @@
1
1
  # CHANGELOG
2
2
 
3
- ## [Unreleased](https://github.com/algolia/algoliasearch-rails/compare/2.2.1...master)
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)
8
+
9
+ ## [2.3.2](https://github.com/algolia/algoliasearch-rails/compare/2.3.1...2.3.2)
10
+ ### Added
11
+ * Support for Pagy pagination provider [`#443`](https://github.com/algolia/algoliasearch-rails/pull/443)
4
12
 
5
13
  ## [2.3.1](https://github.com/algolia/algoliasearch-rails/compare/2.3.0...2.3.1)
6
14
  ### 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', '< 3.0.0'
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', '>= 2.5.0', '< 3.0'
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,12 +27,13 @@ group :test do
27
27
  end
28
28
 
29
29
  group :development do
30
- gem 'rake', '~> 10.1.0'
30
+ gem 'rake', '>= 10.1.0'
31
31
  gem 'rdoc'
32
32
  end
33
33
 
34
34
  group :test, :development do
35
35
  gem 'will_paginate', '>= 2.3.15'
36
36
  gem 'kaminari', '< 1'
37
+ gem 'pagy'
37
38
  end
38
39
 
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.0)
81
+ diff-lcs (1.5.1)
81
82
  erubi (1.12.0)
82
- faraday (2.7.10)
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.1.0)
88
+ faraday-net_http_persistent (2.3.0)
87
89
  faraday (~> 2.5)
88
- net-http-persistent (~> 4.0)
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.2)
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 5.x and 6.x are supported.
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
 
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
33
33
  "lib/algoliasearch/configuration.rb",
34
34
  "lib/algoliasearch/pagination.rb",
35
35
  "lib/algoliasearch/pagination/kaminari.rb",
36
+ "lib/algoliasearch/pagination/pagy.rb",
36
37
  "lib/algoliasearch/pagination/will_paginate.rb",
37
38
  "lib/algoliasearch/railtie.rb",
38
39
  "lib/algoliasearch/tasks/algoliasearch.rake",
@@ -78,18 +79,19 @@ Gem::Specification.new do |s|
78
79
 
79
80
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
80
81
  s.add_runtime_dependency(%q<json>, [">= 1.5.1"])
81
- s.add_runtime_dependency(%q<algolia>, ["< 3.0.0"])
82
+ s.add_runtime_dependency(%q<algolia>, [">= 3.5.2"])
82
83
  s.add_development_dependency(%q<will_paginate>, [">= 2.3.15"])
83
84
  s.add_development_dependency(%q<kaminari>, [">= 0"])
85
+ s.add_development_dependency(%q<pagy>, [">= 0"])
84
86
  s.add_development_dependency "rake"
85
87
  s.add_development_dependency "rdoc"
86
88
  else
87
89
  s.add_dependency(%q<json>, [">= 1.5.1"])
88
- s.add_dependency(%q<algolia>, ["< 3.0.0"])
90
+ s.add_dependency(%q<algolia>, [">= 3.5.2"])
89
91
  end
90
92
  else
91
93
  s.add_dependency(%q<json>, [">= 1.5.1"])
92
- s.add_dependency(%q<algolia>, ["< 3.0.0"])
94
+ s.add_dependency(%q<algolia>, [">= 3.5.2"])
93
95
  end
94
96
  end
95
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
- .merge(configuration)
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::Search::Client.new(Algolia::Search::Config.new(@@configuration), client_opts)
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
@@ -0,0 +1,29 @@
1
+ unless defined? Pagy
2
+ raise(AlgoliaSearch::BadConfiguration, "AlgoliaSearch: Please add 'pagy' to your Gemfile to use Pagy pagination backend")
3
+ end
4
+
5
+ module AlgoliaSearch
6
+ module Pagination
7
+ class Pagy
8
+
9
+ def self.create(results, total_hits, options = {})
10
+ vars = {
11
+ count: total_hits,
12
+ page: options[:page],
13
+ items: options[:per_page]
14
+ }
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
+
23
+ [pagy, results]
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+
@@ -3,6 +3,7 @@ module AlgoliaSearch
3
3
 
4
4
  autoload :WillPaginate, 'algoliasearch/pagination/will_paginate'
5
5
  autoload :Kaminari, 'algoliasearch/pagination/kaminari'
6
+ autoload :Pagy, 'algoliasearch/pagination/pagy'
6
7
 
7
8
  def self.create(results, total_hits, options = {})
8
9
  return results if AlgoliaSearch.configuration[:pagination_backend].nil?
@@ -1,3 +1,3 @@
1
1
  module AlgoliaSearch
2
- VERSION = '2.3.1'
2
+ VERSION = '3.0.0'
3
3
  end
@@ -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
- index = algolia_ensure_init(options, settings)
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
- index.delete_objects(ids.select { |id| !id.blank? })
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 = index.save_objects(objects)
464
+ last_task = AlgoliaSearch.client.save_objects(index_name, objects).last.task_id
520
465
  end
521
- index.wait_task(last_task.raw_response["taskID"]) if last_task and (synchronous || options[:synchronous])
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
- master_index = algolia_ensure_init(options, settings)
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!(JSON.parse(settings.to_settings.to_json)) # convert symbols to strings
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
- src_index_name = algolia_index_name(options)
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.copy_index!(src_index_name, tmp_index_name, { scope: %w[settings synonyms rules] })
552
- tmp_index = SafeIndex.new(tmp_index_name, !!options[:raise_on_failure])
553
- else
554
- tmp_index = algolia_ensure_init(tmp_options, tmp_settings, master_settings)
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
- tmp_index.save_objects(objects)
510
+
511
+ AlgoliaSearch.client.save_objects(tmp_index_name, objects)
564
512
  end
565
513
 
566
- move_task = SafeIndex.move_index(tmp_index.name, src_index_name)
567
- master_index.wait_task(move_task.raw_response["taskID"]) if synchronous || options[:synchronous]
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
- index = SafeIndex.new(algolia_index_name(options), true)
584
- task = index.set_settings(final_settings)
585
- index.wait_task(task.raw_response["taskID"]) if synchronous
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
- index = algolia_ensure_init(options, settings)
552
+ algolia_ensure_init(options, settings)
553
+ index_name = algolia_index_name(options)
554
+
593
555
  next if options[:replica]
594
- task = index.save_objects(objects.map { |o| settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o, options) })
595
- index.wait_task(task.raw_response["taskID"]) if synchronous || options[:synchronous]
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
- index = algolia_ensure_init(options, settings)
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
- index.save_object!(settings.get_attributes(object).merge 'objectID' => algolia_object_id_of(object, options))
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
- index.delete_object!(object_id)
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
- index = algolia_ensure_init(options, settings)
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
- index.delete_object!(object_id)
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
- index = algolia_ensure_init(options, settings)
646
- next if options[:replica]
647
- synchronous || options[:synchronous] ? index.clear_objects! : index.clear_objects
648
- @algolia_indexes[settings] = nil
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
- index_name = params.delete(:index) ||
626
+ index_name_base = params.delete(:index) ||
655
627
  params.delete('index') ||
656
628
  params.delete(:replica) ||
657
629
  params.delete('replica')
658
- index = algolia_index(index_name)
659
- index.search(q, Hash[params.map { |k,v| [k.to_s, v.to_s] }])
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['facets']
658
+ @algolia_json[:facets]
676
659
  end
677
660
 
678
661
  private
@@ -683,12 +666,12 @@ module AlgoliaSearch
683
666
 
684
667
  def algolia_search(q, params = {})
685
668
  if AlgoliaSearch.configuration[:pagination_backend]
686
- # kaminari and will_paginate start pagination at 1, Algolia starts at 0
669
+ # kaminari, will_paginate, and pagy start pagination at 1, Algolia starts at 0
687
670
  params[:page] = (params.delete('page') || params.delete(:page)).to_i
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['hits'].map { |hit| hit['objectID'] }
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['hits'].map do |hit|
701
- o = results_by_id[hit['objectID'].to_s]
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['_highlightResult']
704
- o.snippet_result = hit['_snippetResult']
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['nbHits'].to_i < json['nbPages'].to_i * json['hitsPerPage'].to_i ?
710
- json['nbHits'].to_i: json['nbPages'].to_i * json['hitsPerPage'].to_i
711
- res = AlgoliaSearch::Pagination.create(results, total_hits, algoliasearch_options.merge({ :page => json['page'].to_i + 1, :per_page => json['hitsPerPage'] }))
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
- index = algolia_index(index_name)
723
- query = Hash[params.map { |k, v| [k.to_s, v.to_s] }]
724
- index.search_for_facet_values(facet, text, query)['facetHits']
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 algolia_index(name = nil)
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, index_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
- @algolia_indexes ||= {}
764
+ @algolia_indexes_init ||= {}
781
765
 
782
766
  options ||= algoliasearch_options
783
767
  settings ||= algoliasearch_settings
784
768
 
785
- return @algolia_indexes[settings] if @algolia_indexes[settings]
769
+ return if @algolia_indexes_init[settings]
786
770
 
787
- @algolia_indexes[settings] = SafeIndex.new(algolia_index_name(options), algoliasearch_options[:raise_on_failure])
771
+ index_name = algolia_index_name(options)
788
772
 
789
- index_settings ||= settings.to_settings
790
- index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
791
- replicas = index_settings.delete(:replicas) ||
792
- index_settings.delete('replicas')
793
- index_settings[:replicas] = replicas unless replicas.nil? || options[:inherit]
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
- @algolia_indexes[settings].get_settings(:getVersion => 1) rescue nil # if the index doesn't exist
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, index_settings)
802
- set_settings_method = options[:synchronous] ? :set_settings! : :set_settings
803
- @algolia_indexes[settings].send(set_settings_method, index_settings)
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
- @algolia_indexes[settings]
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
- index = AlgoliaSearch.client.init_index(i['name'])
36
- index.delete!
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.list_indexes['items']
52
- list = list.select { |index| index["name"].include?(SAFE_INDEX_PREFIX) }
53
- list.sort_by { |index| index["primary"] || "" }
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: 2.3.1
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: 2023-12-05 00:00:00.000000000 Z
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.0.0
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.0.0
40
+ version: 3.5.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: will_paginate
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pagy
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -117,6 +131,7 @@ files:
117
131
  - lib/algoliasearch/configuration.rb
118
132
  - lib/algoliasearch/pagination.rb
119
133
  - lib/algoliasearch/pagination/kaminari.rb
134
+ - lib/algoliasearch/pagination/pagy.rb
120
135
  - lib/algoliasearch/pagination/will_paginate.rb
121
136
  - lib/algoliasearch/railtie.rb
122
137
  - lib/algoliasearch/tasks/algoliasearch.rake
@@ -167,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
182
  - !ruby/object:Gem::Version
168
183
  version: '0'
169
184
  requirements: []
170
- rubygems_version: 3.0.3.1
185
+ rubygems_version: 3.1.6
171
186
  signing_key:
172
187
  specification_version: 4
173
188
  summary: AlgoliaSearch integration to your favorite ORM