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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f1f301750d7affc346eea8252c11e98722a8ac4c82be59fb8a248a69fa459ce
4
- data.tar.gz: ce9e1536025efd0f881b6e45be3db203d7dc0eafc6f4b0d07cf8f9b8aea95761
3
+ metadata.gz: 29d7bf2701670f9e978131309e79791bf9ac674e3fe965d58bc3bb3e3e87eedb
4
+ data.tar.gz: 19da3705ecddadc270f3bfe923f93fe0336ba9b12b3fc459694c74f981330c9b
5
5
  SHA512:
6
- metadata.gz: d9de7d8769fc9c19adbfbe70cef64e643c5d25d55774962d041962f570826566dd518a550fd23f73f4293bcdf5e1d048af354d2c9dc1c41ebe574dac64b5d996
7
- data.tar.gz: '05787dce080a885c00ae4efdff6a9b4f26e2109979845ec8405d78f27f5c9757c30e77b814cb0f6957b49f8787cd468df15c8c8369091d80f39adc83e33f1adf'
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/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)
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', '< 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,7 +27,7 @@ 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
 
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
 
@@ -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>, ["< 3.0.0"])
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>, ["< 3.0.0"])
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>, ["< 3.0.0"])
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
- .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
@@ -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
- pagy = ::Pagy.new(vars)
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
@@ -1,3 +1,3 @@
1
1
  module AlgoliaSearch
2
- VERSION = '2.3.2'
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
@@ -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['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.2
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-03-19 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
@@ -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.0.3.1
185
+ rubygems_version: 3.1.6
186
186
  signing_key:
187
187
  specification_version: 4
188
188
  summary: AlgoliaSearch integration to your favorite ORM