searchkick 4.6.3 → 5.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.
data/lib/searchkick.rb CHANGED
@@ -1,29 +1,40 @@
1
1
  # dependencies
2
2
  require "active_support"
3
3
  require "active_support/core_ext/hash/deep_merge"
4
- require "elasticsearch"
4
+ require "active_support/core_ext/module/attr_internal"
5
+ require "active_support/core_ext/module/delegation"
6
+ require "active_support/notifications"
5
7
  require "hashie"
6
8
 
9
+ # stdlib
10
+ require "forwardable"
11
+
7
12
  # modules
8
- require "searchkick/bulk_indexer"
13
+ require "searchkick/controller_runtime"
9
14
  require "searchkick/index"
15
+ require "searchkick/index_cache"
16
+ require "searchkick/index_options"
10
17
  require "searchkick/indexer"
11
18
  require "searchkick/hash_wrapper"
12
- require "searchkick/middleware"
19
+ require "searchkick/log_subscriber"
13
20
  require "searchkick/model"
14
21
  require "searchkick/multi_search"
15
22
  require "searchkick/query"
16
23
  require "searchkick/reindex_queue"
17
24
  require "searchkick/record_data"
18
25
  require "searchkick/record_indexer"
26
+ require "searchkick/relation"
27
+ require "searchkick/relation_indexer"
19
28
  require "searchkick/results"
20
29
  require "searchkick/version"
21
30
 
22
31
  # integrations
23
32
  require "searchkick/railtie" if defined?(Rails)
24
- require "searchkick/logging" if defined?(ActiveSupport::Notifications)
25
33
 
26
34
  module Searchkick
35
+ # requires faraday
36
+ autoload :Middleware, "searchkick/middleware"
37
+
27
38
  # background jobs
28
39
  autoload :BulkReindexJob, "searchkick/bulk_reindex_job"
29
40
  autoload :ProcessBatchJob, "searchkick/process_batch_job"
@@ -33,19 +44,21 @@ module Searchkick
33
44
  # errors
34
45
  class Error < StandardError; end
35
46
  class MissingIndexError < Error; end
36
- class UnsupportedVersionError < Error; end
37
- # TODO switch to Error
38
- class InvalidQueryError < Elasticsearch::Transport::Transport::Errors::BadRequest; end
47
+ class UnsupportedVersionError < Error
48
+ def message
49
+ "This version of Searchkick requires Elasticsearch 7+ or OpenSearch 1+"
50
+ end
51
+ end
52
+ class InvalidQueryError < Error; end
39
53
  class DangerousOperation < Error; end
40
54
  class ImportError < Error; end
41
55
 
42
56
  class << self
43
- attr_accessor :search_method_name, :wordnet_path, :timeout, :models, :client_options, :redis, :index_prefix, :index_suffix, :queue_name, :model_options
57
+ attr_accessor :search_method_name, :timeout, :models, :client_options, :redis, :index_prefix, :index_suffix, :queue_name, :model_options, :client_type
44
58
  attr_writer :client, :env, :search_timeout
45
59
  attr_reader :aws_credentials
46
60
  end
47
61
  self.search_method_name = :search
48
- self.wordnet_path = "/var/lib/wn_s.pl"
49
62
  self.timeout = 10
50
63
  self.models = []
51
64
  self.client_options = {}
@@ -54,15 +67,45 @@ module Searchkick
54
67
 
55
68
  def self.client
56
69
  @client ||= begin
57
- require "typhoeus/adapters/faraday" if defined?(Typhoeus) && Gem::Version.new(Faraday::VERSION) < Gem::Version.new("0.14.0")
58
-
59
- Elasticsearch::Client.new({
60
- url: ENV["ELASTICSEARCH_URL"] || ENV["OPENSEARCH_URL"],
61
- transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
62
- retry_on_failure: 2
63
- }.deep_merge(client_options)) do |f|
64
- f.use Searchkick::Middleware
65
- f.request signer_middleware_key, signer_middleware_aws_params if aws_credentials
70
+ client_type =
71
+ if self.client_type
72
+ self.client_type
73
+ elsif defined?(OpenSearch::Client) && defined?(Elasticsearch::Client)
74
+ raise Error, "Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch"
75
+ elsif defined?(OpenSearch::Client)
76
+ :opensearch
77
+ elsif defined?(Elasticsearch::Client)
78
+ :elasticsearch
79
+ else
80
+ raise Error, "No client found - install the `elasticsearch` or `opensearch-ruby` gem"
81
+ end
82
+
83
+ # check after client to ensure faraday is installed
84
+ # TODO remove in Searchkick 6
85
+ if defined?(Typhoeus) && Gem::Version.new(Faraday::VERSION) < Gem::Version.new("0.14.0")
86
+ require "typhoeus/adapters/faraday"
87
+ end
88
+
89
+ if client_type == :opensearch
90
+ OpenSearch::Client.new({
91
+ url: ENV["OPENSEARCH_URL"],
92
+ transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
93
+ retry_on_failure: 2
94
+ }.deep_merge(client_options)) do |f|
95
+ f.use Searchkick::Middleware
96
+ f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
97
+ end
98
+ else
99
+ raise Error, "The `elasticsearch` gem must be 7+" if Elasticsearch::VERSION.to_i < 7
100
+
101
+ Elasticsearch::Client.new({
102
+ url: ENV["ELASTICSEARCH_URL"],
103
+ transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
104
+ retry_on_failure: 2
105
+ }.deep_merge(client_options)) do |f|
106
+ f.use Searchkick::Middleware
107
+ f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
108
+ end
66
109
  end
67
110
  end
68
111
  end
@@ -96,14 +139,6 @@ module Searchkick
96
139
  Gem::Version.new(server_version.split("-")[0]) < Gem::Version.new(version.split("-")[0])
97
140
  end
98
141
 
99
- # memoize for performance
100
- def self.server_below7?
101
- unless defined?(@server_below7)
102
- @server_below7 = server_below?("7.0.0")
103
- end
104
- @server_below7
105
- end
106
-
107
142
  def self.search(term = "*", model: nil, **options, &block)
108
143
  options = options.dup
109
144
  klass = model
@@ -129,17 +164,27 @@ module Searchkick
129
164
  end
130
165
  end
131
166
 
132
- options = options.merge(block: block) if block
133
- query = Searchkick::Query.new(klass, term, **options)
167
+ # TODO remove in Searchkick 6
134
168
  if options[:execute] == false
135
- query
136
- else
137
- query.execute
169
+ Searchkick.warn("The execute option is no longer needed")
170
+ options.delete(:execute)
138
171
  end
172
+
173
+ options = options.merge(block: block) if block
174
+ Searchkick::Relation.new(klass, term, **options)
139
175
  end
140
176
 
141
177
  def self.multi_search(queries)
142
- Searchkick::MultiSearch.new(queries).perform
178
+ return if queries.empty?
179
+
180
+ queries = queries.map { |q| q.send(:query) }
181
+ event = {
182
+ name: "Multi Search",
183
+ body: queries.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join,
184
+ }
185
+ ActiveSupport::Notifications.instrument("multi_search.searchkick", event) do
186
+ Searchkick::MultiSearch.new(queries).perform
187
+ end
143
188
  end
144
189
 
145
190
  # callbacks
@@ -160,13 +205,25 @@ module Searchkick
160
205
  end
161
206
  end
162
207
 
163
- def self.callbacks(value = nil)
208
+ # message is private
209
+ def self.callbacks(value = nil, message: nil)
164
210
  if block_given?
165
211
  previous_value = callbacks_value
166
212
  begin
167
213
  self.callbacks_value = value
168
214
  result = yield
169
- indexer.perform if callbacks_value == :bulk
215
+ if callbacks_value == :bulk && indexer.queued_items.any?
216
+ event = {}
217
+ if message
218
+ message.call(event)
219
+ else
220
+ event[:name] = "Bulk"
221
+ event[:count] = indexer.queued_items.size
222
+ end
223
+ ActiveSupport::Notifications.instrument("request.searchkick", event) do
224
+ indexer.perform
225
+ end
226
+ end
170
227
  result
171
228
  ensure
172
229
  self.callbacks_value = previous_value
@@ -177,12 +234,8 @@ module Searchkick
177
234
  end
178
235
 
179
236
  def self.aws_credentials=(creds)
180
- begin
181
- # TODO remove in Searchkick 5 (just use aws_sigv4)
182
- require "faraday_middleware/aws_signers_v4"
183
- rescue LoadError
184
- require "faraday_middleware/aws_sigv4"
185
- end
237
+ require "faraday_middleware/aws_sigv4"
238
+
186
239
  @aws_credentials = creds
187
240
  @client = nil # reset client
188
241
  end
@@ -197,7 +250,6 @@ module Searchkick
197
250
  }
198
251
  end
199
252
 
200
- # TODO use ConnectionPool::Wrapper when redis is set so this is no longer needed
201
253
  def self.with_redis
202
254
  if redis
203
255
  if redis.respond_to?(:with)
@@ -215,29 +267,41 @@ module Searchkick
215
267
  end
216
268
 
217
269
  # private
218
- def self.load_records(records, ids)
219
- records =
220
- if records.respond_to?(:primary_key)
221
- # ActiveRecord
222
- records.where(records.primary_key => ids) if records.primary_key
223
- elsif records.respond_to?(:queryable)
224
- # Mongoid 3+
225
- records.queryable.for_ids(ids)
226
- elsif records.respond_to?(:unscoped) && :id.respond_to?(:in)
227
- # Nobrainer
228
- records.unscoped.where(:id.in => ids)
229
- elsif records.respond_to?(:key_column_names)
230
- records.where(records.key_column_names.first => ids)
270
+ def self.load_records(relation, ids)
271
+ relation =
272
+ if relation.respond_to?(:primary_key)
273
+ primary_key = relation.primary_key
274
+ raise Error, "Need primary key to load records" if !primary_key
275
+
276
+ relation.where(primary_key => ids)
277
+ elsif relation.respond_to?(:queryable)
278
+ relation.queryable.for_ids(ids)
231
279
  end
232
280
 
233
- raise Searchkick::Error, "Not sure how to load records" if !records
281
+ raise Error, "Not sure how to load records" if !relation
282
+
283
+ relation
284
+ end
234
285
 
235
- records
286
+ # private
287
+ def self.load_model(class_name, allow_child: false)
288
+ model = class_name.safe_constantize
289
+ raise Error, "Could not find class: #{class_name}" unless model
290
+ if allow_child
291
+ unless model.respond_to?(:searchkick_klass)
292
+ raise Error, "#{class_name} is not a searchkick model"
293
+ end
294
+ else
295
+ unless Searchkick.models.include?(model)
296
+ raise Error, "#{class_name} is not a searchkick model"
297
+ end
298
+ end
299
+ model
236
300
  end
237
301
 
238
302
  # private
239
303
  def self.indexer
240
- Thread.current[:searchkick_indexer] ||= Searchkick::Indexer.new
304
+ Thread.current[:searchkick_indexer] ||= Indexer.new
241
305
  end
242
306
 
243
307
  # private
@@ -250,22 +314,9 @@ module Searchkick
250
314
  Thread.current[:searchkick_callbacks_enabled] = value
251
315
  end
252
316
 
253
- # private
254
- def self.signer_middleware_key
255
- defined?(FaradayMiddleware::AwsSignersV4) ? :aws_signers_v4 : :aws_sigv4
256
- end
257
-
258
317
  # private
259
318
  def self.signer_middleware_aws_params
260
- if signer_middleware_key == :aws_sigv4
261
- {service: "es", region: "us-east-1"}.merge(aws_credentials)
262
- else
263
- {
264
- credentials: aws_credentials[:credentials] || Aws::Credentials.new(aws_credentials[:access_key_id], aws_credentials[:secret_access_key]),
265
- service_name: "es",
266
- region: aws_credentials[:region] || "us-east-1"
267
- }
268
- end
319
+ {service: "es", region: "us-east-1"}.merge(aws_credentials)
269
320
  end
270
321
 
271
322
  # private
@@ -275,31 +326,55 @@ module Searchkick
275
326
  def self.relation?(klass)
276
327
  if klass.respond_to?(:current_scope)
277
328
  !klass.current_scope.nil?
278
- elsif defined?(Mongoid::Threaded)
279
- !Mongoid::Threaded.current_scope(klass).nil?
329
+ else
330
+ klass.is_a?(Mongoid::Criteria) || !Mongoid::Threaded.current_scope(klass).nil?
331
+ end
332
+ end
333
+
334
+ # private
335
+ def self.scope(model)
336
+ # safety check to make sure used properly in code
337
+ raise Error, "Cannot scope relation" if relation?(model)
338
+
339
+ if model.searchkick_options[:unscope]
340
+ model.unscoped
341
+ else
342
+ model
280
343
  end
281
344
  end
282
345
 
283
346
  # private
284
347
  def self.not_found_error?(e)
285
- (defined?(Elasticsearch) && e.is_a?(Elasticsearch::Transport::Transport::Errors::NotFound)) ||
348
+ (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Errors::NotFound)) ||
349
+ (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Errors::NotFound)) ||
286
350
  (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Errors::NotFound))
287
351
  end
288
352
 
289
353
  # private
290
354
  def self.transport_error?(e)
291
- (defined?(Elasticsearch) && e.is_a?(Elasticsearch::Transport::Transport::Error)) ||
355
+ (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Error)) ||
356
+ (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Error)) ||
292
357
  (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Error))
293
358
  end
294
- end
295
359
 
296
- require "active_model/callbacks"
297
- ActiveModel::Callbacks.include(Searchkick::Model)
298
- # TODO use
299
- # ActiveSupport.on_load(:mongoid) do
300
- # Mongoid::Document::ClassMethods.include Searchkick::Model
301
- # end
360
+ # private
361
+ def self.not_allowed_error?(e)
362
+ (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Errors::MethodNotAllowed)) ||
363
+ (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Errors::MethodNotAllowed)) ||
364
+ (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Errors::MethodNotAllowed))
365
+ end
366
+ end
302
367
 
303
368
  ActiveSupport.on_load(:active_record) do
304
369
  extend Searchkick::Model
305
370
  end
371
+
372
+ ActiveSupport.on_load(:mongoid) do
373
+ Mongoid::Document::ClassMethods.include Searchkick::Model
374
+ end
375
+
376
+ ActiveSupport.on_load(:action_controller) do
377
+ include Searchkick::ControllerRuntime
378
+ end
379
+
380
+ Searchkick::LogSubscriber.attach_to :searchkick
@@ -4,9 +4,12 @@ namespace :searchkick do
4
4
  class_name = ENV["CLASS"]
5
5
  abort "USAGE: rake searchkick:reindex CLASS=Product" unless class_name
6
6
 
7
- model = class_name.safe_constantize
8
- abort "Could not find class: #{class_name}" unless model
9
- abort "#{class_name} is not a searchkick model" unless Searchkick.models.include?(model)
7
+ model =
8
+ begin
9
+ Searchkick.load_model(class_name)
10
+ rescue Searchkick::Error => e
11
+ abort e.message
12
+ end
10
13
 
11
14
  puts "Reindexing #{model.name}..."
12
15
  model.reindex
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchkick
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.3
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-19 00:00:00.000000000 Z
11
+ date: 2022-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -16,34 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5'
27
- - !ruby/object:Gem::Dependency
28
- name: elasticsearch
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '6'
34
- - - "<"
35
- - !ruby/object:Gem::Version
36
- version: '7.14'
37
- type: :runtime
38
- prerelease: false
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: '6'
44
- - - "<"
45
- - !ruby/object:Gem::Version
46
- version: '7.14'
26
+ version: '5.2'
47
27
  - !ruby/object:Gem::Dependency
48
28
  name: hashie
49
29
  requirement: !ruby/object:Gem::Requirement
@@ -68,13 +48,14 @@ files:
68
48
  - LICENSE.txt
69
49
  - README.md
70
50
  - lib/searchkick.rb
71
- - lib/searchkick/bulk_indexer.rb
72
51
  - lib/searchkick/bulk_reindex_job.rb
52
+ - lib/searchkick/controller_runtime.rb
73
53
  - lib/searchkick/hash_wrapper.rb
74
54
  - lib/searchkick/index.rb
55
+ - lib/searchkick/index_cache.rb
75
56
  - lib/searchkick/index_options.rb
76
57
  - lib/searchkick/indexer.rb
77
- - lib/searchkick/logging.rb
58
+ - lib/searchkick/log_subscriber.rb
78
59
  - lib/searchkick/middleware.rb
79
60
  - lib/searchkick/model.rb
80
61
  - lib/searchkick/multi_search.rb
@@ -86,6 +67,8 @@ files:
86
67
  - lib/searchkick/record_indexer.rb
87
68
  - lib/searchkick/reindex_queue.rb
88
69
  - lib/searchkick/reindex_v2_job.rb
70
+ - lib/searchkick/relation.rb
71
+ - lib/searchkick/relation_indexer.rb
89
72
  - lib/searchkick/results.rb
90
73
  - lib/searchkick/version.rb
91
74
  - lib/tasks/searchkick.rake
@@ -101,14 +84,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
84
  requirements:
102
85
  - - ">="
103
86
  - !ruby/object:Gem::Version
104
- version: '2.4'
87
+ version: '2.6'
105
88
  required_rubygems_version: !ruby/object:Gem::Requirement
106
89
  requirements:
107
90
  - - ">="
108
91
  - !ruby/object:Gem::Version
109
92
  version: '0'
110
93
  requirements: []
111
- rubygems_version: 3.2.22
94
+ rubygems_version: 3.3.3
112
95
  signing_key:
113
96
  specification_version: 4
114
97
  summary: Intelligent search made easy with Rails and Elasticsearch or OpenSearch
@@ -1,173 +0,0 @@
1
- module Searchkick
2
- class BulkIndexer
3
- attr_reader :index
4
-
5
- def initialize(index)
6
- @index = index
7
- end
8
-
9
- def import_scope(relation, resume: false, method_name: nil, async: false, batch: false, batch_id: nil, full: false, scope: nil)
10
- if scope
11
- relation = relation.send(scope)
12
- elsif relation.respond_to?(:search_import)
13
- relation = relation.search_import
14
- end
15
-
16
- if batch
17
- import_or_update relation.to_a, method_name, async
18
- Searchkick.with_redis { |r| r.srem(batches_key, batch_id) } if batch_id
19
- elsif full && async
20
- full_reindex_async(relation)
21
- elsif relation.respond_to?(:find_in_batches)
22
- if resume
23
- # use total docs instead of max id since there's not a great way
24
- # to get the max _id without scripting since it's a string
25
-
26
- # TODO use primary key and prefix with table name
27
- relation = relation.where("id > ?", index.total_docs)
28
- end
29
-
30
- relation = relation.select("id").except(:includes, :preload) if async
31
-
32
- relation.find_in_batches batch_size: batch_size do |items|
33
- import_or_update items, method_name, async
34
- end
35
- else
36
- each_batch(relation) do |items|
37
- import_or_update items, method_name, async
38
- end
39
- end
40
- end
41
-
42
- def bulk_index(records)
43
- Searchkick.indexer.queue(records.map { |r| RecordData.new(index, r).index_data })
44
- end
45
-
46
- def bulk_delete(records)
47
- Searchkick.indexer.queue(records.reject { |r| r.id.blank? }.map { |r| RecordData.new(index, r).delete_data })
48
- end
49
-
50
- def bulk_update(records, method_name)
51
- Searchkick.indexer.queue(records.map { |r| RecordData.new(index, r).update_data(method_name) })
52
- end
53
-
54
- def batches_left
55
- Searchkick.with_redis { |r| r.scard(batches_key) }
56
- end
57
-
58
- private
59
-
60
- def import_or_update(records, method_name, async)
61
- if records.any?
62
- if async
63
- Searchkick::BulkReindexJob.perform_later(
64
- class_name: records.first.class.searchkick_options[:class_name],
65
- record_ids: records.map(&:id),
66
- index_name: index.name,
67
- method_name: method_name ? method_name.to_s : nil
68
- )
69
- else
70
- records = records.select(&:should_index?)
71
- if records.any?
72
- with_retries do
73
- # call out to index for ActiveSupport notifications
74
- if method_name
75
- index.bulk_update(records, method_name)
76
- else
77
- index.bulk_index(records)
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
84
-
85
- def full_reindex_async(scope)
86
- if scope.respond_to?(:primary_key)
87
- # TODO expire Redis key
88
- primary_key = scope.primary_key
89
-
90
- scope = scope.select(primary_key).except(:includes, :preload)
91
-
92
- starting_id =
93
- begin
94
- scope.minimum(primary_key)
95
- rescue ActiveRecord::StatementInvalid
96
- false
97
- end
98
-
99
- if starting_id.nil?
100
- # no records, do nothing
101
- elsif starting_id.is_a?(Numeric)
102
- max_id = scope.maximum(primary_key)
103
- batches_count = ((max_id - starting_id + 1) / batch_size.to_f).ceil
104
-
105
- batches_count.times do |i|
106
- batch_id = i + 1
107
- min_id = starting_id + (i * batch_size)
108
- bulk_reindex_job scope, batch_id, min_id: min_id, max_id: min_id + batch_size - 1
109
- end
110
- else
111
- scope.find_in_batches(batch_size: batch_size).each_with_index do |batch, i|
112
- batch_id = i + 1
113
-
114
- bulk_reindex_job scope, batch_id, record_ids: batch.map { |record| record.id.to_s }
115
- end
116
- end
117
- else
118
- batch_id = 1
119
- # TODO remove any eager loading
120
- scope = scope.only(:_id) if scope.respond_to?(:only)
121
- each_batch(scope) do |items|
122
- bulk_reindex_job scope, batch_id, record_ids: items.map { |i| i.id.to_s }
123
- batch_id += 1
124
- end
125
- end
126
- end
127
-
128
- def each_batch(scope)
129
- # https://github.com/karmi/tire/blob/master/lib/tire/model/import.rb
130
- # use cursor for Mongoid
131
- items = []
132
- scope.all.each do |item|
133
- items << item
134
- if items.length == batch_size
135
- yield items
136
- items = []
137
- end
138
- end
139
- yield items if items.any?
140
- end
141
-
142
- def bulk_reindex_job(scope, batch_id, options)
143
- Searchkick.with_redis { |r| r.sadd(batches_key, batch_id) }
144
- Searchkick::BulkReindexJob.perform_later(**{
145
- class_name: scope.searchkick_options[:class_name],
146
- index_name: index.name,
147
- batch_id: batch_id
148
- }.merge(options))
149
- end
150
-
151
- def with_retries
152
- retries = 0
153
-
154
- begin
155
- yield
156
- rescue Faraday::ClientError => e
157
- if retries < 1
158
- retries += 1
159
- retry
160
- end
161
- raise e
162
- end
163
- end
164
-
165
- def batches_key
166
- "searchkick:reindex:#{index.name}:batches"
167
- end
168
-
169
- def batch_size
170
- @batch_size ||= index.options[:batch_size] || 1000
171
- end
172
- end
173
- end