algoliasearch-rails 1.20.4 → 2.2.2
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 +5 -5
- data/{ChangeLog → CHANGELOG.MD} +175 -1
- data/Gemfile +14 -37
- data/Gemfile.lock +189 -164
- data/LICENSE +6 -7
- data/README.md +118 -45
- data/algoliasearch-rails.gemspec +7 -7
- data/lib/algoliasearch/configuration.rb +28 -2
- data/lib/algoliasearch/pagination/kaminari.rb +1 -1
- data/lib/algoliasearch/tasks/algoliasearch.rake +6 -1
- data/lib/algoliasearch/utilities.rb +18 -1
- data/lib/algoliasearch/version.rb +1 -1
- data/lib/algoliasearch-rails.rb +216 -116
- data/spec/spec_helper.rb +20 -4
- metadata +11 -33
- data/.travis.yml +0 -55
data/lib/algoliasearch-rails.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'algolia'
|
2
2
|
|
3
3
|
require 'algoliasearch/version'
|
4
4
|
require 'algoliasearch/utilities'
|
@@ -47,34 +47,60 @@ module AlgoliaSearch
|
|
47
47
|
end
|
48
48
|
|
49
49
|
class IndexSettings
|
50
|
+
DEFAULT_BATCH_SIZE = 1000
|
50
51
|
|
51
52
|
# AlgoliaSearch settings
|
52
|
-
OPTIONS = [
|
53
|
-
|
54
|
-
:
|
55
|
-
|
56
|
-
:ranking, :customRanking, :
|
57
|
-
|
58
|
-
:
|
59
|
-
|
60
|
-
:
|
61
|
-
:
|
62
|
-
|
63
|
-
:paginationLimitedTo
|
53
|
+
OPTIONS = [
|
54
|
+
# Attributes
|
55
|
+
:searchableAttributes, :attributesForFaceting, :unretrievableAttributes, :attributesToRetrieve,
|
56
|
+
# Ranking
|
57
|
+
:ranking, :customRanking, :relevancyStrictness, # Replicas are handled via `add_replica`
|
58
|
+
# Faceting
|
59
|
+
:maxValuesPerFacet, :sortFacetValuesBy,
|
60
|
+
# Highlighting / Snippeting
|
61
|
+
:attributesToHighlight, :attributesToSnippet, :highlightPreTag, :highlightPostTag,
|
62
|
+
:snippetEllipsisText, :restrictHighlightAndSnippetArrays,
|
63
|
+
# Pagination
|
64
|
+
:hitsPerPage, :paginationLimitedTo,
|
65
|
+
# Typo
|
66
|
+
:minWordSizefor1Typo, :minWordSizefor2Typos, :typoTolerance, :allowTyposOnNumericTokens,
|
67
|
+
:disableTypoToleranceOnAttributes, :disableTypoToleranceOnWords, :separatorsToIndex,
|
68
|
+
# Language
|
69
|
+
:ignorePlurals, :removeStopWords, :camelCaseAttributes, :decompoundedAttributes,
|
70
|
+
:keepDiacriticsOnCharacters, :queryLanguages, :indexLanguages,
|
71
|
+
# Query Rules
|
72
|
+
:enableRules,
|
73
|
+
# Query Strategy
|
74
|
+
:queryType, :removeWordsIfNoResults, :advancedSyntax, :optionalWords,
|
75
|
+
:disablePrefixOnAttributes, :disableExactOnAttributes, :exactOnSingleWordQuery, :alternativesAsExact,
|
76
|
+
# Performance
|
77
|
+
:numericAttributesForFiltering, :allowCompressionOfIntegerArray,
|
78
|
+
# Advanced
|
79
|
+
:attributeForDistinct, :distinct, :replaceSynonymsInHighlight, :minProximity, :responseFields,
|
80
|
+
:maxFacetHits,
|
81
|
+
|
82
|
+
# Rails-specific
|
83
|
+
:synonyms, :placeholders, :altCorrections,
|
84
|
+
]
|
64
85
|
OPTIONS.each do |k|
|
65
86
|
define_method k do |v|
|
66
87
|
instance_variable_set("@#{k}", v)
|
67
88
|
end
|
68
89
|
end
|
69
90
|
|
70
|
-
def initialize(options, block)
|
91
|
+
def initialize(options, &block)
|
71
92
|
@options = options
|
72
|
-
instance_exec(&block) if
|
93
|
+
instance_exec(&block) if block_given?
|
94
|
+
end
|
95
|
+
|
96
|
+
def use_serializer(serializer)
|
97
|
+
@serializer = serializer
|
98
|
+
# instance_variable_set("@serializer", serializer)
|
73
99
|
end
|
74
100
|
|
75
101
|
def attribute(*names, &block)
|
76
102
|
raise ArgumentError.new('Cannot pass multiple attribute names if block given') if block_given? and names.length > 1
|
77
|
-
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:
|
103
|
+
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:replica]
|
78
104
|
@attributes ||= {}
|
79
105
|
names.flatten.each do |name|
|
80
106
|
@attributes[name.to_s] = block_given? ? Proc.new { |o| o.instance_eval(&block) } : Proc.new { |o| o.send(name) }
|
@@ -84,7 +110,7 @@ module AlgoliaSearch
|
|
84
110
|
|
85
111
|
def add_attribute(*names, &block)
|
86
112
|
raise ArgumentError.new('Cannot pass multiple attribute names if block given') if block_given? and names.length > 1
|
87
|
-
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:
|
113
|
+
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:replica]
|
88
114
|
@additional_attributes ||= {}
|
89
115
|
names.each do |name|
|
90
116
|
@additional_attributes[name.to_s] = block_given? ? Proc.new { |o| o.instance_eval(&block) } : Proc.new { |o| o.send(name) }
|
@@ -97,7 +123,7 @@ module AlgoliaSearch
|
|
97
123
|
end
|
98
124
|
|
99
125
|
def is_sequel?(object)
|
100
|
-
defined?(::Sequel) && object.class < ::Sequel::Model
|
126
|
+
defined?(::Sequel) && defined?(::Sequel::Model) && object.class < ::Sequel::Model
|
101
127
|
end
|
102
128
|
|
103
129
|
def is_active_record?(object)
|
@@ -118,15 +144,7 @@ module AlgoliaSearch
|
|
118
144
|
end
|
119
145
|
|
120
146
|
def get_attribute_names(object)
|
121
|
-
|
122
|
-
get_default_attributes(object).keys
|
123
|
-
else
|
124
|
-
@attributes.keys
|
125
|
-
end
|
126
|
-
|
127
|
-
res += @additional_attributes.keys if @additional_attributes
|
128
|
-
|
129
|
-
res
|
147
|
+
get_attributes(object).keys
|
130
148
|
end
|
131
149
|
|
132
150
|
def attributes_to_hash(attributes, object)
|
@@ -138,19 +156,27 @@ module AlgoliaSearch
|
|
138
156
|
end
|
139
157
|
|
140
158
|
def get_attributes(object)
|
141
|
-
|
142
|
-
|
159
|
+
# If a serializer is set, we ignore attributes
|
160
|
+
# everything should be done via the serializer
|
161
|
+
if not @serializer.nil?
|
162
|
+
attributes = @serializer.new(object).attributes
|
143
163
|
else
|
144
|
-
if
|
145
|
-
|
146
|
-
|
147
|
-
end
|
164
|
+
if @attributes.nil? || @attributes.length == 0
|
165
|
+
# no `attribute ...` have been configured, use the default attributes of the model
|
166
|
+
attributes = get_default_attributes(object)
|
148
167
|
else
|
149
|
-
|
168
|
+
# at least 1 `attribute ...` has been configured, therefore use ONLY the one configured
|
169
|
+
if is_active_record?(object)
|
170
|
+
object.class.unscoped do
|
171
|
+
attributes = attributes_to_hash(@attributes, object)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
attributes = attributes_to_hash(@attributes, object)
|
175
|
+
end
|
150
176
|
end
|
151
177
|
end
|
152
178
|
|
153
|
-
attributes.merge!(attributes_to_hash(@additional_attributes, object))
|
179
|
+
attributes.merge!(attributes_to_hash(@additional_attributes, object)) if @additional_attributes
|
154
180
|
|
155
181
|
if @options[:sanitize]
|
156
182
|
sanitizer = begin
|
@@ -185,7 +211,7 @@ module AlgoliaSearch
|
|
185
211
|
def encode_attributes(v)
|
186
212
|
case v
|
187
213
|
when String
|
188
|
-
v.force_encoding('utf-8')
|
214
|
+
v.dup.force_encoding('utf-8')
|
189
215
|
when Hash
|
190
216
|
v.each { |key, value| v[key] = encode_attributes(value) }
|
191
217
|
when Array
|
@@ -195,15 +221,15 @@ module AlgoliaSearch
|
|
195
221
|
end
|
196
222
|
end
|
197
223
|
|
198
|
-
def geoloc(lat_attr, lng_attr)
|
199
|
-
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:
|
224
|
+
def geoloc(lat_attr = nil, lng_attr = nil, &block)
|
225
|
+
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:replica]
|
200
226
|
add_attribute :_geoloc do |o|
|
201
|
-
{ :lat => o.send(lat_attr).to_f, :lng => o.send(lng_attr).to_f }
|
227
|
+
block_given? ? o.instance_eval(&block) : { :lat => o.send(lat_attr).to_f, :lng => o.send(lng_attr).to_f }
|
202
228
|
end
|
203
229
|
end
|
204
230
|
|
205
231
|
def tags(*args, &block)
|
206
|
-
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:
|
232
|
+
raise ArgumentError.new('Cannot specify additional attributes on a replica index') if @options[:replica]
|
207
233
|
add_attribute :_tags do |o|
|
208
234
|
v = block_given? ? o.instance_eval(&block) : args
|
209
235
|
v.is_a?(Array) ? v : [v]
|
@@ -220,16 +246,12 @@ module AlgoliaSearch
|
|
220
246
|
v = get_setting(k)
|
221
247
|
settings[k] = v if !v.nil?
|
222
248
|
end
|
223
|
-
|
224
|
-
|
225
|
-
name = opts[:index_name]
|
226
|
-
name = "#{name}_#{Rails.env.to_s}" if opts[:per_environment]
|
227
|
-
name
|
228
|
-
end
|
229
|
-
settings.delete(:slaves) if settings[:slaves].empty?
|
249
|
+
|
250
|
+
if !@options[:replica]
|
230
251
|
settings[:replicas] = additional_indexes.select { |opts, s| opts[:replica] }.map do |opts, s|
|
231
252
|
name = opts[:index_name]
|
232
253
|
name = "#{name}_#{Rails.env.to_s}" if opts[:per_environment]
|
254
|
+
name = "virtual(#{name})" if opts[:virtual]
|
233
255
|
name
|
234
256
|
end
|
235
257
|
settings.delete(:replicas) if settings[:replicas].empty?
|
@@ -238,27 +260,20 @@ module AlgoliaSearch
|
|
238
260
|
end
|
239
261
|
|
240
262
|
def add_index(index_name, options = {}, &block)
|
241
|
-
raise ArgumentError.new('Cannot specify additional index on a replica index') if @options[:
|
263
|
+
raise ArgumentError.new('Cannot specify additional index on a replica index') if @options[:replica]
|
242
264
|
raise ArgumentError.new('No block given') if !block_given?
|
243
265
|
raise ArgumentError.new('Options auto_index and auto_remove cannot be set on nested indexes') if options[:auto_index] || options[:auto_remove]
|
244
266
|
@additional_indexes ||= {}
|
245
|
-
raise MixedSlavesAndReplicas.new('Cannot mix slaves and replicas in the same configuration (add_slave is deprecated)') if (options[:slave] && @additional_indexes.any? { |opts, _| opts[:replica] }) || (options[:replica] && @additional_indexes.any? { |opts, _| opts[:slave] })
|
246
267
|
options[:index_name] = index_name
|
247
|
-
@additional_indexes[options] = IndexSettings.new(options,
|
268
|
+
@additional_indexes[options] = IndexSettings.new(options, &block)
|
248
269
|
end
|
249
270
|
|
250
271
|
def add_replica(index_name, options = {}, &block)
|
251
|
-
raise ArgumentError.new('Cannot specify additional replicas on a replica index') if @options[:
|
272
|
+
raise ArgumentError.new('Cannot specify additional replicas on a replica index') if @options[:replica]
|
252
273
|
raise ArgumentError.new('No block given') if !block_given?
|
253
274
|
add_index(index_name, options.merge({ :replica => true, :primary_settings => self }), &block)
|
254
275
|
end
|
255
276
|
|
256
|
-
def add_slave(index_name, options = {}, &block)
|
257
|
-
raise ArgumentError.new('Cannot specify additional slaves on a slave index') if @options[:slave] || @options[:replica]
|
258
|
-
raise ArgumentError.new('No block given') if !block_given?
|
259
|
-
add_index(index_name, options.merge({ :slave => true, :primary_settings => self }), &block)
|
260
|
-
end
|
261
|
-
|
262
277
|
def additional_indexes
|
263
278
|
@additional_indexes || {}
|
264
279
|
end
|
@@ -276,11 +291,11 @@ module AlgoliaSearch
|
|
276
291
|
# are correctly logged or thrown depending on the `raise_on_failure` option
|
277
292
|
class SafeIndex
|
278
293
|
def initialize(name, raise_on_failure)
|
279
|
-
@index =
|
294
|
+
@index = AlgoliaSearch.client.init_index(name)
|
280
295
|
@raise_on_failure = raise_on_failure.nil? || raise_on_failure
|
281
296
|
end
|
282
297
|
|
283
|
-
::Algolia::Index.instance_methods(false).each do |m|
|
298
|
+
::Algolia::Search::Index.instance_methods(false).each do |m|
|
284
299
|
define_method(m) do |*args, &block|
|
285
300
|
SafeIndex.log_or_throw(m, @raise_on_failure) do
|
286
301
|
@index.send(m, *args, &block)
|
@@ -301,7 +316,7 @@ module AlgoliaSearch
|
|
301
316
|
SafeIndex.log_or_throw(:get_settings, @raise_on_failure) do
|
302
317
|
begin
|
303
318
|
@index.get_settings(*args)
|
304
|
-
rescue Algolia::
|
319
|
+
rescue Algolia::AlgoliaHttpError => e
|
305
320
|
return {} if e.code == 404 # not fatal
|
306
321
|
raise e
|
307
322
|
end
|
@@ -311,7 +326,7 @@ module AlgoliaSearch
|
|
311
326
|
# expose move as well
|
312
327
|
def self.move_index(old_name, new_name)
|
313
328
|
SafeIndex.log_or_throw(:move_index, true) do
|
314
|
-
|
329
|
+
AlgoliaSearch.client.move_index(old_name, new_name)
|
315
330
|
end
|
316
331
|
end
|
317
332
|
|
@@ -361,13 +376,13 @@ module AlgoliaSearch
|
|
361
376
|
end
|
362
377
|
|
363
378
|
def algoliasearch(options = {}, &block)
|
364
|
-
self.algoliasearch_settings = IndexSettings.new(options,
|
379
|
+
self.algoliasearch_settings = IndexSettings.new(options, &block)
|
365
380
|
self.algoliasearch_options = { :type => algolia_full_const_get(model_name.to_s), :per_page => algoliasearch_settings.get_setting(:hitsPerPage) || 10, :page => 1 }.merge(options)
|
366
381
|
|
367
382
|
attr_accessor :highlight_result, :snippet_result
|
368
383
|
|
369
384
|
if options[:synchronous] == true
|
370
|
-
if defined?(::Sequel) && self < Sequel::Model
|
385
|
+
if defined?(::Sequel) && defined?(::Sequel::Model) && self < Sequel::Model
|
371
386
|
class_eval do
|
372
387
|
copy_after_validation = instance_method(:after_validation)
|
373
388
|
define_method(:after_validation) do |*args|
|
@@ -398,11 +413,10 @@ module AlgoliaSearch
|
|
398
413
|
end
|
399
414
|
end
|
400
415
|
unless options[:auto_index] == false
|
401
|
-
if defined?(::Sequel) && self < Sequel::Model
|
416
|
+
if defined?(::Sequel) && defined?(::Sequel::Model) && self < Sequel::Model
|
402
417
|
class_eval do
|
403
418
|
copy_after_validation = instance_method(:after_validation)
|
404
419
|
copy_before_save = instance_method(:before_save)
|
405
|
-
copy_after_commit = instance_method(:after_commit)
|
406
420
|
|
407
421
|
define_method(:after_validation) do |*args|
|
408
422
|
super(*args)
|
@@ -416,10 +430,23 @@ module AlgoliaSearch
|
|
416
430
|
super(*args)
|
417
431
|
end
|
418
432
|
|
419
|
-
|
420
|
-
|
421
|
-
copy_after_commit
|
422
|
-
|
433
|
+
sequel_version = Gem::Version.new(Sequel.version)
|
434
|
+
if sequel_version >= Gem::Version.new('4.0.0') && sequel_version < Gem::Version.new('5.0.0')
|
435
|
+
copy_after_commit = instance_method(:after_commit)
|
436
|
+
define_method(:after_commit) do |*args|
|
437
|
+
super(*args)
|
438
|
+
copy_after_commit.bind(self).call
|
439
|
+
algolia_perform_index_tasks
|
440
|
+
end
|
441
|
+
else
|
442
|
+
copy_after_save = instance_method(:after_save)
|
443
|
+
define_method(:after_save) do |*args|
|
444
|
+
super(*args)
|
445
|
+
copy_after_save.bind(self).call
|
446
|
+
self.db.after_commit do
|
447
|
+
algolia_perform_index_tasks
|
448
|
+
end
|
449
|
+
end
|
423
450
|
end
|
424
451
|
end
|
425
452
|
else
|
@@ -433,7 +460,7 @@ module AlgoliaSearch
|
|
433
460
|
end
|
434
461
|
end
|
435
462
|
unless options[:auto_remove] == false
|
436
|
-
if defined?(::Sequel) && self < Sequel::Model
|
463
|
+
if defined?(::Sequel) && defined?(::Sequel::Model) && self < Sequel::Model
|
437
464
|
class_eval do
|
438
465
|
copy_after_destroy = instance_method(:after_destroy)
|
439
466
|
|
@@ -466,12 +493,12 @@ module AlgoliaSearch
|
|
466
493
|
Thread.current["algolia_without_auto_index_scope_for_#{self.model_name}"]
|
467
494
|
end
|
468
495
|
|
469
|
-
def algolia_reindex!(batch_size =
|
496
|
+
def algolia_reindex!(batch_size = AlgoliaSearch::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false)
|
470
497
|
return if algolia_without_auto_index_scope
|
471
498
|
algolia_configurations.each do |options, settings|
|
472
499
|
next if algolia_indexing_disabled?(options)
|
473
500
|
index = algolia_ensure_init(options, settings)
|
474
|
-
next if options[:
|
501
|
+
next if options[:replica]
|
475
502
|
last_task = nil
|
476
503
|
|
477
504
|
algolia_find_in_batches(batch_size) do |group|
|
@@ -491,38 +518,44 @@ module AlgoliaSearch
|
|
491
518
|
end
|
492
519
|
last_task = index.save_objects(objects)
|
493
520
|
end
|
494
|
-
index.wait_task(last_task["taskID"]) if last_task and (synchronous || options[:synchronous])
|
521
|
+
index.wait_task(last_task.raw_response["taskID"]) if last_task and (synchronous || options[:synchronous])
|
495
522
|
end
|
496
523
|
nil
|
497
524
|
end
|
498
525
|
|
499
526
|
# reindex whole database using a extra temporary index + move operation
|
500
|
-
def algolia_reindex(batch_size =
|
527
|
+
def algolia_reindex(batch_size = AlgoliaSearch::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false)
|
501
528
|
return if algolia_without_auto_index_scope
|
502
529
|
algolia_configurations.each do |options, settings|
|
503
530
|
next if algolia_indexing_disabled?(options)
|
504
|
-
next if options[:
|
531
|
+
next if options[:replica]
|
505
532
|
|
506
533
|
# fetch the master settings
|
507
534
|
master_index = algolia_ensure_init(options, settings)
|
508
535
|
master_settings = master_index.get_settings rescue {} # if master doesn't exist yet
|
536
|
+
master_exists = master_settings != {}
|
509
537
|
master_settings.merge!(JSON.parse(settings.to_settings.to_json)) # convert symbols to strings
|
510
538
|
|
511
539
|
# remove the replicas of the temporary index
|
512
|
-
master_settings.delete :slaves
|
513
|
-
master_settings.delete 'slaves'
|
514
540
|
master_settings.delete :replicas
|
515
541
|
master_settings.delete 'replicas'
|
516
542
|
|
517
543
|
# init temporary index
|
518
|
-
|
519
|
-
|
544
|
+
src_index_name = algolia_index_name(options)
|
545
|
+
tmp_index_name = "#{src_index_name}.tmp"
|
546
|
+
tmp_options = options.merge({ :index_name => tmp_index_name })
|
520
547
|
tmp_options.delete(:per_environment) # already included in the temporary index_name
|
521
548
|
tmp_settings = settings.dup
|
522
|
-
|
549
|
+
|
550
|
+
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)
|
555
|
+
end
|
523
556
|
|
524
557
|
algolia_find_in_batches(batch_size) do |group|
|
525
|
-
if algolia_conditional_index?(
|
558
|
+
if algolia_conditional_index?(options)
|
526
559
|
# select only indexable objects
|
527
560
|
group = group.select { |o| algolia_indexable?(o, tmp_options) }
|
528
561
|
end
|
@@ -530,19 +563,36 @@ module AlgoliaSearch
|
|
530
563
|
tmp_index.save_objects(objects)
|
531
564
|
end
|
532
565
|
|
533
|
-
move_task = SafeIndex.move_index(tmp_index.name,
|
534
|
-
master_index.wait_task(move_task["taskID"]) if synchronous || options[:synchronous]
|
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]
|
535
568
|
end
|
536
569
|
nil
|
537
570
|
end
|
538
571
|
|
572
|
+
def algolia_set_settings(synchronous = false)
|
573
|
+
algolia_configurations.each do |options, settings|
|
574
|
+
if options[:primary_settings] && options[:inherit]
|
575
|
+
primary = options[:primary_settings].to_settings
|
576
|
+
primary.delete :replicas
|
577
|
+
primary.delete 'replicas'
|
578
|
+
final_settings = primary.merge(settings.to_settings)
|
579
|
+
else
|
580
|
+
final_settings = settings.to_settings
|
581
|
+
end
|
582
|
+
|
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
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
539
589
|
def algolia_index_objects(objects, synchronous = false)
|
540
590
|
algolia_configurations.each do |options, settings|
|
541
591
|
next if algolia_indexing_disabled?(options)
|
542
592
|
index = algolia_ensure_init(options, settings)
|
543
|
-
next if options[:
|
593
|
+
next if options[:replica]
|
544
594
|
task = index.save_objects(objects.map { |o| settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o, options) })
|
545
|
-
index.wait_task(task["taskID"]) if synchronous || options[:synchronous]
|
595
|
+
index.wait_task(task.raw_response["taskID"]) if synchronous || options[:synchronous]
|
546
596
|
end
|
547
597
|
end
|
548
598
|
|
@@ -552,13 +602,13 @@ module AlgoliaSearch
|
|
552
602
|
next if algolia_indexing_disabled?(options)
|
553
603
|
object_id = algolia_object_id_of(object, options)
|
554
604
|
index = algolia_ensure_init(options, settings)
|
555
|
-
next if options[:
|
605
|
+
next if options[:replica]
|
556
606
|
if algolia_indexable?(object, options)
|
557
607
|
raise ArgumentError.new("Cannot index a record with a blank objectID") if object_id.blank?
|
558
608
|
if synchronous || options[:synchronous]
|
559
|
-
index.
|
609
|
+
index.save_object!(settings.get_attributes(object).merge 'objectID' => algolia_object_id_of(object, options))
|
560
610
|
else
|
561
|
-
index.
|
611
|
+
index.save_object(settings.get_attributes(object).merge 'objectID' => algolia_object_id_of(object, options))
|
562
612
|
end
|
563
613
|
elsif algolia_conditional_index?(options) && !object_id.blank?
|
564
614
|
# remove non-indexable objects
|
@@ -579,7 +629,7 @@ module AlgoliaSearch
|
|
579
629
|
algolia_configurations.each do |options, settings|
|
580
630
|
next if algolia_indexing_disabled?(options)
|
581
631
|
index = algolia_ensure_init(options, settings)
|
582
|
-
next if options[:
|
632
|
+
next if options[:replica]
|
583
633
|
if synchronous || options[:synchronous]
|
584
634
|
index.delete_object!(object_id)
|
585
635
|
else
|
@@ -593,8 +643,8 @@ module AlgoliaSearch
|
|
593
643
|
algolia_configurations.each do |options, settings|
|
594
644
|
next if algolia_indexing_disabled?(options)
|
595
645
|
index = algolia_ensure_init(options, settings)
|
596
|
-
next if options[:
|
597
|
-
synchronous || options[:synchronous] ? index.
|
646
|
+
next if options[:replica]
|
647
|
+
synchronous || options[:synchronous] ? index.clear_objects! : index.clear_objects
|
598
648
|
@algolia_indexes[settings] = nil
|
599
649
|
end
|
600
650
|
nil
|
@@ -603,8 +653,6 @@ module AlgoliaSearch
|
|
603
653
|
def algolia_raw_search(q, params = {})
|
604
654
|
index_name = params.delete(:index) ||
|
605
655
|
params.delete('index') ||
|
606
|
-
params.delete(:slave) ||
|
607
|
-
params.delete('slave') ||
|
608
656
|
params.delete(:replica) ||
|
609
657
|
params.delete('replica')
|
610
658
|
index = algolia_index(index_name)
|
@@ -669,13 +717,11 @@ module AlgoliaSearch
|
|
669
717
|
def algolia_search_for_facet_values(facet, text, params = {})
|
670
718
|
index_name = params.delete(:index) ||
|
671
719
|
params.delete('index') ||
|
672
|
-
params.delete(:slave) ||
|
673
|
-
params.delete('slave') ||
|
674
720
|
params.delete(:replica) ||
|
675
721
|
params.delete('replicas')
|
676
722
|
index = algolia_index(index_name)
|
677
723
|
query = Hash[params.map { |k, v| [k.to_s, v.to_s] }]
|
678
|
-
index.
|
724
|
+
index.search_for_facet_values(facet, text, query)['facetHits']
|
679
725
|
end
|
680
726
|
|
681
727
|
# deprecated (renaming)
|
@@ -699,19 +745,22 @@ module AlgoliaSearch
|
|
699
745
|
end
|
700
746
|
|
701
747
|
def algolia_must_reindex?(object)
|
748
|
+
# use +algolia_dirty?+ method if implemented
|
749
|
+
return object.send(:algolia_dirty?) if (object.respond_to?(:algolia_dirty?))
|
750
|
+
# Loop over each index to see if a attribute used in records has changed
|
702
751
|
algolia_configurations.each do |options, settings|
|
703
|
-
next if options
|
752
|
+
next if algolia_indexing_disabled?(options)
|
753
|
+
next if options[:replica]
|
704
754
|
return true if algolia_object_id_changed?(object, options)
|
705
755
|
settings.get_attribute_names(object).each do |k|
|
706
|
-
|
707
|
-
return true if !object.respond_to?(changed_method) || object.send(changed_method)
|
756
|
+
return true if algolia_attribute_changed?(object, k)
|
757
|
+
# return true if !object.respond_to?(changed_method) || object.send(changed_method)
|
708
758
|
end
|
709
759
|
[options[:if], options[:unless]].each do |condition|
|
710
760
|
case condition
|
711
761
|
when nil
|
712
762
|
when String, Symbol
|
713
|
-
|
714
|
-
return true if !object.respond_to?(changed_method) || object.send(changed_method)
|
763
|
+
return true if algolia_attribute_changed?(object, condition)
|
715
764
|
else
|
716
765
|
# if the :if, :unless condition is a anything else,
|
717
766
|
# we have no idea whether we should reindex or not
|
@@ -720,6 +769,7 @@ module AlgoliaSearch
|
|
720
769
|
end
|
721
770
|
end
|
722
771
|
end
|
772
|
+
# By default, we don't reindex
|
723
773
|
return false
|
724
774
|
end
|
725
775
|
|
@@ -737,19 +787,21 @@ module AlgoliaSearch
|
|
737
787
|
|
738
788
|
@algolia_indexes[settings] = SafeIndex.new(algolia_index_name(options), algoliasearch_options[:raise_on_failure])
|
739
789
|
|
740
|
-
current_settings = @algolia_indexes[settings].get_settings(:getVersion => 1) rescue nil # if the index doesn't exist
|
741
|
-
|
742
790
|
index_settings ||= settings.to_settings
|
743
791
|
index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
|
792
|
+
replicas = index_settings.delete(:replicas) ||
|
793
|
+
index_settings.delete('replicas')
|
794
|
+
index_settings[:replicas] = replicas unless replicas.nil? || options[:inherit]
|
744
795
|
|
745
|
-
if
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
796
|
+
options[:check_settings] = true if options[:check_settings].nil?
|
797
|
+
|
798
|
+
current_settings = if options[:check_settings] && !algolia_indexing_disabled?(options)
|
799
|
+
@algolia_indexes[settings].get_settings(:getVersion => 1) rescue nil # if the index doesn't exist
|
800
|
+
end
|
801
|
+
|
802
|
+
if !algolia_indexing_disabled?(options) && options[:check_settings] && algoliasearch_settings_changed?(current_settings, index_settings)
|
803
|
+
set_settings_method = options[:synchronous] ? :set_settings! : :set_settings
|
804
|
+
@algolia_indexes[settings].send(set_settings_method, index_settings)
|
753
805
|
end
|
754
806
|
|
755
807
|
@algolia_indexes[settings]
|
@@ -785,8 +837,8 @@ module AlgoliaSearch
|
|
785
837
|
end
|
786
838
|
|
787
839
|
def algolia_object_id_changed?(o, options = nil)
|
788
|
-
|
789
|
-
|
840
|
+
changed = algolia_attribute_changed?(o, algolia_object_id_method(options))
|
841
|
+
changed.nil? ? false : changed
|
790
842
|
end
|
791
843
|
|
792
844
|
def algoliasearch_settings_changed?(prev, current)
|
@@ -796,6 +848,8 @@ module AlgoliaSearch
|
|
796
848
|
if v.is_a?(Array) and prev_v.is_a?(Array)
|
797
849
|
# compare array of strings, avoiding symbols VS strings comparison
|
798
850
|
return true if v.map { |x| x.to_s } != prev_v.map { |x| x.to_s }
|
851
|
+
elsif v.blank? # blank? check is needed to compare [] and null
|
852
|
+
return true unless prev_v.blank?
|
799
853
|
else
|
800
854
|
return true if prev_v != v
|
801
855
|
end
|
@@ -864,7 +918,7 @@ module AlgoliaSearch
|
|
864
918
|
def algolia_find_in_batches(batch_size, &block)
|
865
919
|
if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches)
|
866
920
|
find_in_batches(:batch_size => batch_size, &block)
|
867
|
-
elsif defined?(::Sequel) && self < Sequel::Model
|
921
|
+
elsif defined?(::Sequel) && defined?(::Sequel::Model) && self < Sequel::Model
|
868
922
|
dataset.extension(:pagination).each_page(batch_size, &block)
|
869
923
|
else
|
870
924
|
# don't worry, mongoid has its own underlying cursor/streaming mechanism
|
@@ -879,6 +933,50 @@ module AlgoliaSearch
|
|
879
933
|
yield items unless items.empty?
|
880
934
|
end
|
881
935
|
end
|
936
|
+
|
937
|
+
def algolia_attribute_changed?(object, attr_name)
|
938
|
+
# if one of two method is implemented, we return its result
|
939
|
+
# true/false means whether it has changed or not
|
940
|
+
# +#{attr_name}_changed?+ always defined for automatic attributes but deprecated after Rails 5.2
|
941
|
+
# +will_save_change_to_#{attr_name}?+ should be use instead for Rails 5.2+, also defined for automatic attributes.
|
942
|
+
# If none of the method are defined, it's a dynamic attribute
|
943
|
+
|
944
|
+
method_name = "#{attr_name}_changed?"
|
945
|
+
if object.respond_to?(method_name)
|
946
|
+
# If +#{attr_name}_changed?+ respond we want to see if the method is user defined or if it's automatically
|
947
|
+
# defined by Rails.
|
948
|
+
# If it's user-defined, we call it.
|
949
|
+
# If it's automatic we check ActiveRecord version to see if this method is deprecated
|
950
|
+
# and try to call +will_save_change_to_#{attr_name}?+ instead.
|
951
|
+
# See: https://github.com/algolia/algoliasearch-rails/pull/338
|
952
|
+
# This feature is not compatible with Ruby 1.8
|
953
|
+
# In this case, we always call #{attr_name}_changed?
|
954
|
+
if Object.const_defined?(:RUBY_VERSION) && RUBY_VERSION.to_f < 1.9
|
955
|
+
return object.send(method_name)
|
956
|
+
end
|
957
|
+
unless automatic_changed_method?(object, method_name) && automatic_changed_method_deprecated?
|
958
|
+
return object.send(method_name)
|
959
|
+
end
|
960
|
+
end
|
961
|
+
|
962
|
+
if object.respond_to?("will_save_change_to_#{attr_name}?")
|
963
|
+
return object.send("will_save_change_to_#{attr_name}?")
|
964
|
+
end
|
965
|
+
|
966
|
+
# We don't know if the attribute has changed, so conservatively assume it has
|
967
|
+
true
|
968
|
+
end
|
969
|
+
|
970
|
+
def automatic_changed_method?(object, method_name)
|
971
|
+
raise ArgumentError.new("Method #{method_name} doesn't exist on #{object.class.name}") unless object.respond_to?(method_name)
|
972
|
+
file = object.method(method_name).source_location[0]
|
973
|
+
file.end_with?("active_model/attribute_methods.rb")
|
974
|
+
end
|
975
|
+
|
976
|
+
def automatic_changed_method_deprecated?
|
977
|
+
(defined?(::ActiveRecord) && ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 1) ||
|
978
|
+
(defined?(::ActiveRecord) && ActiveRecord::VERSION::MAJOR > 5)
|
979
|
+
end
|
882
980
|
end
|
883
981
|
|
884
982
|
# these are the instance methods included
|
@@ -930,8 +1028,10 @@ module AlgoliaSearch
|
|
930
1028
|
end
|
931
1029
|
|
932
1030
|
def algolia_mark_must_reindex
|
933
|
-
|
934
|
-
|
1031
|
+
# algolia_must_reindex flag is reset after every commit as part. If we must reindex at any point in
|
1032
|
+
# a stransaction, keep flag set until it is explicitly unset
|
1033
|
+
@algolia_must_reindex ||=
|
1034
|
+
if defined?(::Sequel) && defined?(::Sequel::Model) && is_a?(Sequel::Model)
|
935
1035
|
new? || self.class.algolia_must_reindex?(self)
|
936
1036
|
else
|
937
1037
|
new_record? || self.class.algolia_must_reindex?(self)
|