algoliasearch-rails 2.3.2 → 3.0.1

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: 2f1f301750d7affc346eea8252c11e98722a8ac4c82be59fb8a248a69fa459ce
4
- data.tar.gz: ce9e1536025efd0f881b6e45be3db203d7dc0eafc6f4b0d07cf8f9b8aea95761
3
+ metadata.gz: 14ef8f9ecc85826893e288b3fc0ec82a908b407893edb067f0fe2acc05f89240
4
+ data.tar.gz: acd4e020ef97b41b6900e0c379abe1cb6f02e503980d0da7186e85250ba4eb75
5
5
  SHA512:
6
- metadata.gz: d9de7d8769fc9c19adbfbe70cef64e643c5d25d55774962d041962f570826566dd518a550fd23f73f4293bcdf5e1d048af354d2c9dc1c41ebe574dac64b5d996
7
- data.tar.gz: '05787dce080a885c00ae4efdff6a9b4f26e2109979845ec8405d78f27f5c9757c30e77b814cb0f6957b49f8787cd468df15c8c8369091d80f39adc83e33f1adf'
6
+ metadata.gz: 594ce23363e9706940a948f27b29db9be1a030859714672a3fba8aa2b3818173188cd64b294699f086966bc5b446ba51612a30fb9ae83ec80fbd2ea839ac7ade
7
+ data.tar.gz: 0ba91ece82a88fffadaeff270b1a1de90a7409bc09c370848b31cfeb732712df92c33382031d4d6c956f02d915a19655569e4eac3d9545086e591279aeb2f16e
data/CHANGELOG.MD CHANGED
@@ -1,6 +1,15 @@
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.1](https://github.com/algolia/algoliasearch-rails/compare/3.0.0...3.0.1)
6
+ ### Fixed
7
+ * Ensure init when `check_settings=true` for atomic reindexes [`#453`](https://github.com/algolia/algoliasearch-rails/pull/453)
8
+ * Fix last task_id when no tasks to save [`#452`](https://github.com/algolia/algoliasearch-rails/pull/452)
9
+
10
+ ## [3.0.0](https://github.com/algolia/algoliasearch-rails/compare/2.3.2...3.0.0)
11
+ 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
12
+ For a list of known breaking changes, please refer to [the upgrade guide](./UPGRADING_TO_V3.MD)
4
13
 
5
14
  ## [2.3.2](https://github.com/algolia/algoliasearch-rails/compare/2.3.1...2.3.2)
6
15
  ### 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.1'
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,10 @@ 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
+ save_tasks = AlgoliaSearch.client.save_objects(index_name, objects)
465
+ last_task = save_tasks.present? ? save_tasks.last.task_id : last_task
520
466
  end
521
- index.wait_task(last_task.raw_response["taskID"]) if last_task and (synchronous || options[:synchronous])
467
+ AlgoliaSearch.client.wait_for_task(index_name, last_task) if last_task and (synchronous || options[:synchronous])
522
468
  end
523
469
  nil
524
470
  end
@@ -530,28 +476,32 @@ module AlgoliaSearch
530
476
  next if algolia_indexing_disabled?(options)
531
477
  next if options[:replica]
532
478
 
479
+ algolia_ensure_init(options, settings)
480
+ index_name = algolia_index_name(options)
481
+
533
482
  # 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
483
+ master_settings = AlgoliaSearch.client.get_settings(index_name).to_hash rescue {} # if master doesn't exist yet
536
484
  master_exists = master_settings != {}
537
- master_settings.merge!(JSON.parse(settings.to_settings.to_json)) # convert symbols to strings
485
+ master_settings.merge!(settings.to_hash)
538
486
 
539
487
  # remove the replicas of the temporary index
540
488
  master_settings.delete :replicas
541
489
  master_settings.delete 'replicas'
542
490
 
543
491
  # init temporary index
544
- src_index_name = algolia_index_name(options)
545
- tmp_index_name = "#{src_index_name}.tmp"
492
+ tmp_index_name = "#{index_name}.tmp"
546
493
  tmp_options = options.merge({ :index_name => tmp_index_name })
547
494
  tmp_options.delete(:per_environment) # already included in the temporary index_name
548
495
  tmp_settings = settings.dup
549
496
 
550
497
  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])
498
+ task_id = AlgoliaSearch.client.operation_index(
499
+ index_name,
500
+ Algolia::Search::OperationIndexParams.new(operation: Algolia::Search::OperationType::COPY, destination: tmp_index_name, scope: %w[settings synonyms rules])
501
+ ).task_id
502
+ AlgoliaSearch.client.wait_for_task(index_name, task_id)
553
503
  else
554
- tmp_index = algolia_ensure_init(tmp_options, tmp_settings, master_settings)
504
+ algolia_ensure_init(tmp_options, tmp_settings, master_settings)
555
505
  end
556
506
 
557
507
  algolia_find_in_batches(batch_size) do |group|
@@ -560,11 +510,15 @@ module AlgoliaSearch
560
510
  group = group.select { |o| algolia_indexable?(o, tmp_options) }
561
511
  end
562
512
  objects = group.map { |o| tmp_settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o, tmp_options) }
563
- tmp_index.save_objects(objects)
513
+
514
+ AlgoliaSearch.client.save_objects(tmp_index_name, objects)
564
515
  end
565
516
 
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]
517
+ task_id = AlgoliaSearch.client.operation_index(
518
+ tmp_index_name,
519
+ Algolia::Search::OperationIndexParams.new(operation: "move", destination: index_name)
520
+ ).task_id
521
+ AlgoliaSearch.client.wait_for_task(index_name, task_id) if synchronous || options[:synchronous]
568
522
  end
569
523
  nil
570
524
  end
@@ -572,27 +526,40 @@ module AlgoliaSearch
572
526
  def algolia_set_settings(synchronous = false)
573
527
  algolia_configurations.each do |options, settings|
574
528
  if options[:primary_settings] && options[:inherit]
575
- primary = options[:primary_settings].to_settings
529
+ primary = options[:primary_settings].to_settings.to_hash
576
530
  primary.delete :replicas
577
531
  primary.delete 'replicas'
578
- final_settings = primary.merge(settings.to_settings)
532
+ final_settings = primary.merge(settings.to_settings.to_hash)
579
533
  else
580
- final_settings = settings.to_settings
534
+ final_settings = settings.to_settings.to_hash
581
535
  end
582
536
 
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
537
+ s = final_settings.map do |k, v|
538
+ [settings.setting_name(k), v]
539
+ end.to_h
540
+
541
+ synonyms = s.delete("synonyms") || s.delete(:synonyms)
542
+ unless synonyms.nil? || synonyms.empty?
543
+ resp = AlgoliaSearch.client.save_synonyms(index_name,synonyms.map {|s| Algolia::Search::SynonymHit.new({object_id: s.join("-"), synonyms: s, type: "synonym"}) } )
544
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if synchronous || options[:synchronous]
545
+ end
546
+
547
+ resp = AlgoliaSearch.client.set_settings(index_name, Algolia::Search::IndexSettings.new(s))
548
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if synchronous || options[:synchronous]
586
549
  end
587
550
  end
588
551
 
589
552
  def algolia_index_objects(objects, synchronous = false)
590
553
  algolia_configurations.each do |options, settings|
591
554
  next if algolia_indexing_disabled?(options)
592
- index = algolia_ensure_init(options, settings)
555
+ algolia_ensure_init(options, settings)
556
+ index_name = algolia_index_name(options)
557
+
593
558
  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]
559
+ tasks = AlgoliaSearch.client.save_objects(index_name, objects.map { |o| settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o, options) })
560
+ tasks.each do |task|
561
+ AlgoliaSearch.client.wait_for_task(index_name, task.task_id) if synchronous || options[:synchronous]
562
+ end
596
563
  end
597
564
  end
598
565
 
@@ -600,22 +567,23 @@ module AlgoliaSearch
600
567
  return if algolia_without_auto_index_scope
601
568
  algolia_configurations.each do |options, settings|
602
569
  next if algolia_indexing_disabled?(options)
570
+
603
571
  object_id = algolia_object_id_of(object, options)
604
- index = algolia_ensure_init(options, settings)
572
+ index_name = algolia_index_name(options)
573
+ algolia_ensure_init(options, settings)
605
574
  next if options[:replica]
575
+
606
576
  if algolia_indexable?(object, options)
607
577
  raise ArgumentError.new("Cannot index a record with a blank objectID") if object_id.blank?
578
+ resp = AlgoliaSearch.client.save_object(index_name, settings.get_attributes(object).merge({ 'objectID' => algolia_object_id_of(object, options) }))
608
579
  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))
580
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id)
612
581
  end
613
582
  elsif algolia_conditional_index?(options) && !object_id.blank?
614
583
  # remove non-indexable objects
584
+ resp = AlgoliaSearch.client.delete_object(index_name, object_id)
615
585
  if synchronous || options[:synchronous]
616
- index.delete_object!(object_id)
617
- else
618
- index.delete_object(object_id)
586
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id)
619
587
  end
620
588
  end
621
589
  end
@@ -628,12 +596,14 @@ module AlgoliaSearch
628
596
  raise ArgumentError.new("Cannot index a record with a blank objectID") if object_id.blank?
629
597
  algolia_configurations.each do |options, settings|
630
598
  next if algolia_indexing_disabled?(options)
631
- index = algolia_ensure_init(options, settings)
599
+ algolia_ensure_init(options, settings)
600
+ index_name = algolia_index_name(options)
601
+
632
602
  next if options[:replica]
603
+
604
+ resp = AlgoliaSearch.client.delete_object(index_name, object_id)
633
605
  if synchronous || options[:synchronous]
634
- index.delete_object!(object_id)
635
- else
636
- index.delete_object(object_id)
606
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id)
637
607
  end
638
608
  end
639
609
  nil
@@ -641,22 +611,38 @@ module AlgoliaSearch
641
611
 
642
612
  def algolia_clear_index!(synchronous = false)
643
613
  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
614
+ next if algolia_indexing_disabled?(options) || options[:replica]
615
+
616
+ algolia_ensure_init(options, settings)
617
+ index_name = algolia_index_name(options)
618
+ res = AlgoliaSearch.client.clear_objects(index_name)
619
+
620
+ if synchronous || options[:synchronous]
621
+ AlgoliaSearch.client.wait_for_task(index_name, res.task_id)
622
+ end
649
623
  end
650
624
  nil
651
625
  end
652
626
 
627
+
653
628
  def algolia_raw_search(q, params = {})
654
- index_name = params.delete(:index) ||
629
+ index_name_base = params.delete(:index) ||
655
630
  params.delete('index') ||
656
631
  params.delete(:replica) ||
657
632
  params.delete('replica')
658
- index = algolia_index(index_name)
659
- index.search(q, Hash[params.map { |k,v| [k.to_s, v.to_s] }])
633
+
634
+ opts = algoliasearch_options
635
+ unless index_name_base.nil?
636
+ algolia_configurations.each do |o, s|
637
+ if o[:index_name].to_s == index_name_base.to_s
638
+ opts = o
639
+ ensure_algolia_index(index_name_base)
640
+ end
641
+ end
642
+ end
643
+
644
+ index_name = algolia_index_name(opts, index_name_base)
645
+ 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
646
  end
661
647
 
662
648
  module AdditionalMethods
@@ -672,7 +658,7 @@ module AlgoliaSearch
672
658
  end
673
659
 
674
660
  def algolia_facets
675
- @algolia_json['facets']
661
+ @algolia_json[:facets]
676
662
  end
677
663
 
678
664
  private
@@ -688,7 +674,7 @@ module AlgoliaSearch
688
674
  params[:page] -= 1 if params[:page].to_i > 0
689
675
  end
690
676
  json = algolia_raw_search(q, params)
691
- hit_ids = json['hits'].map { |hit| hit['objectID'] }
677
+ hit_ids = json[:hits].map { |hit| hit[:objectID] }
692
678
  if defined?(::Mongoid::Document) && self.include?(::Mongoid::Document)
693
679
  condition_key = algolia_object_id_method.in
694
680
  else
@@ -697,18 +683,18 @@ module AlgoliaSearch
697
683
  results_by_id = algoliasearch_options[:type].where(condition_key => hit_ids).index_by do |hit|
698
684
  algolia_object_id_of(hit)
699
685
  end
700
- results = json['hits'].map do |hit|
701
- o = results_by_id[hit['objectID'].to_s]
686
+ results = json[:hits].map do |hit|
687
+ o = results_by_id[hit[:objectID].to_s]
702
688
  if o
703
- o.highlight_result = hit['_highlightResult']
704
- o.snippet_result = hit['_snippetResult']
689
+ o.highlight_result = hit[:_highlightResult]
690
+ o.snippet_result = hit[:_snippetResult]
705
691
  o
706
692
  end
707
693
  end.compact
708
694
  # 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'] }))
695
+ total_hits = json[:nbHits].to_i < json[:nbPages].to_i * json[:hitsPerPage].to_i ?
696
+ json[:nbHits].to_i: json[:nbPages].to_i * json[:hitsPerPage].to_i
697
+ res = AlgoliaSearch::Pagination.create(results, total_hits, algoliasearch_options.merge({ :page => json[:page].to_i + 1, :per_page => json[:hitsPerPage] }))
712
698
  res.extend(AdditionalMethods)
713
699
  res.send(:algolia_init_raw_answer, json)
714
700
  res
@@ -719,15 +705,16 @@ module AlgoliaSearch
719
705
  params.delete('index') ||
720
706
  params.delete(:replica) ||
721
707
  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']
708
+ index_name ||= algolia_index_name(algoliasearch_options)
709
+ req = Algolia::Search::SearchForFacetValuesRequest.new({facet_query: text, params: params.to_query})
710
+
711
+ AlgoliaSearch.client.search_for_facet_values(index_name, facet, req).facet_hits
725
712
  end
726
713
 
727
714
  # deprecated (renaming)
728
715
  alias :algolia_search_facet :algolia_search_for_facet_values
729
716
 
730
- def algolia_index(name = nil)
717
+ def ensure_algolia_index(name = nil)
731
718
  if name
732
719
  algolia_configurations.each do |o, s|
733
720
  return algolia_ensure_init(o, s) if o[:index_name].to_s == name.to_s
@@ -737,9 +724,9 @@ module AlgoliaSearch
737
724
  algolia_ensure_init
738
725
  end
739
726
 
740
- def algolia_index_name(options = nil)
727
+ def algolia_index_name(options = nil, index_name = nil)
741
728
  options ||= algoliasearch_options
742
- name = options[:index_name] || model_name.to_s.gsub('::', '_')
729
+ name = index_name || options[:index_name] || model_name.to_s.gsub('::', '_')
743
730
  name = "#{name}_#{Rails.env.to_s}" if options[:per_environment]
744
731
  name
745
732
  end
@@ -774,36 +761,46 @@ module AlgoliaSearch
774
761
 
775
762
  protected
776
763
 
777
- def algolia_ensure_init(options = nil, settings = nil, index_settings = nil)
764
+ def algolia_ensure_init(options = nil, settings = nil, index_settings_hash = nil)
778
765
  raise ArgumentError.new('No `algoliasearch` block found in your model.') if algoliasearch_settings.nil?
779
766
 
780
- @algolia_indexes ||= {}
767
+ @algolia_indexes_init ||= {}
781
768
 
782
769
  options ||= algoliasearch_options
783
770
  settings ||= algoliasearch_settings
784
771
 
785
- return @algolia_indexes[settings] if @algolia_indexes[settings]
772
+ return if @algolia_indexes_init[settings]
786
773
 
787
- @algolia_indexes[settings] = SafeIndex.new(algolia_index_name(options), algoliasearch_options[:raise_on_failure])
774
+ index_name = algolia_index_name(options)
788
775
 
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]
776
+
777
+ index_settings_hash ||= settings.to_settings.to_hash
778
+ index_settings_hash = options[:primary_settings].to_settings.to_hash.merge(index_settings_hash) if options[:inherit]
779
+ replicas = index_settings_hash.delete(:replicas) || index_settings_hash.delete('replicas')
780
+ index_settings_hash[:replicas] = replicas unless replicas.nil? || options[:inherit]
794
781
 
795
782
  options[:check_settings] = true if options[:check_settings].nil?
796
783
 
797
784
  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
785
+ AlgoliaSearch.client.get_settings(index_name, {:getVersion => 1}).to_hash rescue nil # if the index doesn't exist
799
786
  end
800
787
 
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)
788
+ if !algolia_indexing_disabled?(options) && options[:check_settings] && algoliasearch_settings_changed?(current_settings, index_settings_hash)
789
+ s = index_settings_hash.map do |k, v|
790
+ [settings.setting_name(k), v]
791
+ end.to_h
792
+
793
+ synonyms = s.delete("synonyms") || s.delete(:synonyms)
794
+ unless synonyms.nil? || synonyms.empty?
795
+ resp = AlgoliaSearch.client.save_synonyms(index_name,synonyms.map {|s| Algolia::Search::SynonymHit.new({object_id: s.join("-"), synonyms: s, type: "synonym"}) } )
796
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if options[:synchronous]
797
+ end
798
+
799
+ resp = AlgoliaSearch.client.set_settings(index_name, Algolia::Search::IndexSettings.new(s))
800
+ AlgoliaSearch.client.wait_for_task(index_name, resp.task_id) if options[:synchronous]
804
801
  end
805
802
 
806
- @algolia_indexes[settings]
803
+ return
807
804
  end
808
805
 
809
806
  private
@@ -843,7 +840,7 @@ module AlgoliaSearch
843
840
  def algoliasearch_settings_changed?(prev, current)
844
841
  return true if prev.nil?
845
842
  current.each do |k, v|
846
- prev_v = prev[k.to_s]
843
+ prev_v = prev[k.to_sym] || prev[k.to_s]
847
844
  if v.is_a?(Array) and prev_v.is_a?(Array)
848
845
  # compare array of strings, avoiding symbols VS strings comparison
849
846
  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.1
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-12-03 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