searchkick-hooopo 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +35 -0
- data/CHANGELOG.md +491 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +1908 -0
- data/Rakefile +20 -0
- data/benchmark/Gemfile +23 -0
- data/benchmark/benchmark.rb +97 -0
- data/lib/searchkick/bulk_reindex_job.rb +17 -0
- data/lib/searchkick/index.rb +500 -0
- data/lib/searchkick/index_options.rb +333 -0
- data/lib/searchkick/indexer.rb +28 -0
- data/lib/searchkick/logging.rb +242 -0
- data/lib/searchkick/middleware.rb +12 -0
- data/lib/searchkick/model.rb +156 -0
- data/lib/searchkick/process_batch_job.rb +23 -0
- data/lib/searchkick/process_queue_job.rb +23 -0
- data/lib/searchkick/query.rb +901 -0
- data/lib/searchkick/reindex_queue.rb +38 -0
- data/lib/searchkick/reindex_v2_job.rb +39 -0
- data/lib/searchkick/results.rb +216 -0
- data/lib/searchkick/tasks.rb +33 -0
- data/lib/searchkick/version.rb +3 -0
- data/lib/searchkick.rb +215 -0
- data/searchkick.gemspec +28 -0
- data/test/aggs_test.rb +197 -0
- data/test/autocomplete_test.rb +75 -0
- data/test/boost_test.rb +175 -0
- data/test/callbacks_test.rb +59 -0
- data/test/ci/before_install.sh +17 -0
- data/test/errors_test.rb +19 -0
- data/test/gemfiles/activerecord31.gemfile +7 -0
- data/test/gemfiles/activerecord32.gemfile +7 -0
- data/test/gemfiles/activerecord40.gemfile +8 -0
- data/test/gemfiles/activerecord41.gemfile +8 -0
- data/test/gemfiles/activerecord42.gemfile +7 -0
- data/test/gemfiles/activerecord50.gemfile +7 -0
- data/test/gemfiles/apartment.gemfile +8 -0
- data/test/gemfiles/cequel.gemfile +8 -0
- data/test/gemfiles/mongoid2.gemfile +7 -0
- data/test/gemfiles/mongoid3.gemfile +6 -0
- data/test/gemfiles/mongoid4.gemfile +7 -0
- data/test/gemfiles/mongoid5.gemfile +7 -0
- data/test/gemfiles/mongoid6.gemfile +8 -0
- data/test/gemfiles/nobrainer.gemfile +8 -0
- data/test/gemfiles/parallel_tests.gemfile +8 -0
- data/test/geo_shape_test.rb +172 -0
- data/test/highlight_test.rb +78 -0
- data/test/index_test.rb +153 -0
- data/test/inheritance_test.rb +83 -0
- data/test/marshal_test.rb +8 -0
- data/test/match_test.rb +276 -0
- data/test/misspellings_test.rb +56 -0
- data/test/model_test.rb +42 -0
- data/test/multi_search_test.rb +22 -0
- data/test/multi_tenancy_test.rb +22 -0
- data/test/order_test.rb +46 -0
- data/test/pagination_test.rb +53 -0
- data/test/partial_reindex_test.rb +58 -0
- data/test/query_test.rb +35 -0
- data/test/records_test.rb +10 -0
- data/test/reindex_test.rb +52 -0
- data/test/reindex_v2_job_test.rb +32 -0
- data/test/routing_test.rb +23 -0
- data/test/should_index_test.rb +32 -0
- data/test/similar_test.rb +28 -0
- data/test/sql_test.rb +198 -0
- data/test/suggest_test.rb +85 -0
- data/test/synonyms_test.rb +67 -0
- data/test/test_helper.rb +527 -0
- data/test/where_test.rb +223 -0
- metadata +250 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
module Searchkick
|
2
|
+
module Model
|
3
|
+
def searchkick(**options)
|
4
|
+
unknown_keywords = options.keys - [:batch_size, :callbacks, :conversions,
|
5
|
+
:filterable, :geo_shape, :highlight, :ignore_above, :index_name, :index_prefix, :language,
|
6
|
+
:locations, :mappings, :match, :merge_mappings, :routing, :searchable, :settings, :similarity,
|
7
|
+
:special_characters, :stem_conversions, :suggest, :synonyms, :text_end,
|
8
|
+
:text_middle, :text_start, :word, :wordnet, :word_end, :word_middle, :word_start]
|
9
|
+
raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
|
10
|
+
|
11
|
+
raise "Only call searchkick once per model" if respond_to?(:searchkick_index)
|
12
|
+
|
13
|
+
Searchkick.models << self
|
14
|
+
|
15
|
+
class_eval do
|
16
|
+
cattr_reader :searchkick_options, :searchkick_klass
|
17
|
+
|
18
|
+
callbacks = options.key?(:callbacks) ? options[:callbacks] : true
|
19
|
+
|
20
|
+
class_variable_set :@@searchkick_options, options.dup
|
21
|
+
class_variable_set :@@searchkick_klass, self
|
22
|
+
class_variable_set :@@searchkick_callbacks, callbacks
|
23
|
+
class_variable_set :@@searchkick_index, options[:index_name] ||
|
24
|
+
(options[:index_prefix].respond_to?(:call) && proc { [options[:index_prefix].call, model_name.plural, Searchkick.env, Searchkick.index_suffix].compact.join("_") }) ||
|
25
|
+
[options[:index_prefix], model_name.plural, Searchkick.env, Searchkick.index_suffix].compact.join("_")
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def searchkick_search(term = "*", **options, &block)
|
29
|
+
Searchkick.search(term, {model: self}.merge(options), &block)
|
30
|
+
end
|
31
|
+
alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
|
32
|
+
|
33
|
+
def searchkick_index
|
34
|
+
index = class_variable_get :@@searchkick_index
|
35
|
+
index = index.call if index.respond_to? :call
|
36
|
+
Searchkick::Index.new(index, searchkick_options)
|
37
|
+
end
|
38
|
+
alias_method :search_index, :searchkick_index unless method_defined?(:search_index)
|
39
|
+
|
40
|
+
def enable_search_callbacks
|
41
|
+
class_variable_set :@@searchkick_callbacks, true
|
42
|
+
end
|
43
|
+
|
44
|
+
def disable_search_callbacks
|
45
|
+
class_variable_set :@@searchkick_callbacks, false
|
46
|
+
end
|
47
|
+
|
48
|
+
def search_callbacks?
|
49
|
+
class_variable_get(:@@searchkick_callbacks) && Searchkick.callbacks?
|
50
|
+
end
|
51
|
+
|
52
|
+
def searchkick_reindex(method_name = nil, full: false, **options)
|
53
|
+
scoped = (respond_to?(:current_scope) && respond_to?(:default_scoped) && current_scope && current_scope.to_sql != default_scoped.to_sql) ||
|
54
|
+
(respond_to?(:queryable) && queryable != unscoped.with_default_scope)
|
55
|
+
|
56
|
+
refresh = options.fetch(:refresh, !scoped)
|
57
|
+
|
58
|
+
if method_name
|
59
|
+
# update
|
60
|
+
searchkick_index.import_scope(searchkick_klass, method_name: method_name)
|
61
|
+
searchkick_index.refresh if refresh
|
62
|
+
true
|
63
|
+
elsif scoped && !full
|
64
|
+
# reindex association
|
65
|
+
searchkick_index.import_scope(searchkick_klass)
|
66
|
+
searchkick_index.refresh if refresh
|
67
|
+
true
|
68
|
+
else
|
69
|
+
# full reindex
|
70
|
+
searchkick_index.reindex_scope(searchkick_klass, options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
alias_method :reindex, :searchkick_reindex unless method_defined?(:reindex)
|
74
|
+
|
75
|
+
def searchkick_index_options
|
76
|
+
searchkick_index.index_options
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
callback_name = callbacks == :async ? :reindex_async : :reindex
|
81
|
+
if respond_to?(:after_commit)
|
82
|
+
after_commit callback_name, if: proc { self.class.search_callbacks? }
|
83
|
+
elsif respond_to?(:after_save)
|
84
|
+
after_save callback_name, if: proc { self.class.search_callbacks? }
|
85
|
+
after_destroy callback_name, if: proc { self.class.search_callbacks? }
|
86
|
+
end
|
87
|
+
|
88
|
+
def reindex(method_name = nil, refresh: false, async: false, mode: nil)
|
89
|
+
klass_options = self.class.searchkick_index.options
|
90
|
+
|
91
|
+
if mode.nil?
|
92
|
+
mode =
|
93
|
+
if async
|
94
|
+
:async
|
95
|
+
elsif Searchkick.callbacks_value
|
96
|
+
Searchkick.callbacks_value
|
97
|
+
elsif klass_options.key?(:callbacks) && klass_options[:callbacks] != :async
|
98
|
+
# TODO remove 2nd condition in next major version
|
99
|
+
klass_options[:callbacks]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
case mode
|
104
|
+
when :queue
|
105
|
+
if method_name
|
106
|
+
raise Searchkick::Error, "Partial reindex not supported with queue option"
|
107
|
+
else
|
108
|
+
self.class.searchkick_index.reindex_queue.push(id.to_s)
|
109
|
+
end
|
110
|
+
when :async
|
111
|
+
if method_name
|
112
|
+
# TODO support Mongoid and NoBrainer and non-id primary keys
|
113
|
+
Searchkick::BulkReindexJob.perform_later(
|
114
|
+
class_name: self.class.name,
|
115
|
+
record_ids: [id.to_s],
|
116
|
+
method_name: method_name ? method_name.to_s : nil
|
117
|
+
)
|
118
|
+
else
|
119
|
+
self.class.searchkick_index.reindex_record_async(self)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
if method_name
|
123
|
+
self.class.searchkick_index.update_record(self, method_name)
|
124
|
+
else
|
125
|
+
self.class.searchkick_index.reindex_record(self)
|
126
|
+
end
|
127
|
+
self.class.searchkick_index.refresh if refresh
|
128
|
+
end
|
129
|
+
end unless method_defined?(:reindex)
|
130
|
+
|
131
|
+
# TODO remove this method in next major version
|
132
|
+
def reindex_async
|
133
|
+
reindex(async: true)
|
134
|
+
end unless method_defined?(:reindex_async)
|
135
|
+
|
136
|
+
def similar(options = {})
|
137
|
+
self.class.searchkick_index.similar_record(self, options)
|
138
|
+
end unless method_defined?(:similar)
|
139
|
+
|
140
|
+
def search_data
|
141
|
+
respond_to?(:to_hash) ? to_hash : serializable_hash
|
142
|
+
end unless method_defined?(:search_data)
|
143
|
+
|
144
|
+
def should_index?
|
145
|
+
true
|
146
|
+
end unless method_defined?(:should_index?)
|
147
|
+
|
148
|
+
if defined?(Cequel) && self < Cequel::Record && !method_defined?(:destroyed?)
|
149
|
+
def destroyed?
|
150
|
+
transient?
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Searchkick
|
2
|
+
class ProcessBatchJob < ActiveJob::Base
|
3
|
+
queue_as :searchkick
|
4
|
+
|
5
|
+
def perform(class_name:, record_ids:)
|
6
|
+
klass = class_name.constantize
|
7
|
+
scope = Searchkick.load_records(klass, record_ids)
|
8
|
+
scope = scope.search_import if scope.respond_to?(:search_import)
|
9
|
+
records = scope.select(&:should_index?)
|
10
|
+
|
11
|
+
# determine which records to delete
|
12
|
+
delete_ids = record_ids - records.map { |r| r.id.to_s }
|
13
|
+
delete_records = delete_ids.map { |id| m = klass.new; m.id = id; m }
|
14
|
+
|
15
|
+
# bulk reindex
|
16
|
+
index = klass.searchkick_index
|
17
|
+
Searchkick.callbacks(:bulk) do
|
18
|
+
index.bulk_index(records) if records.any?
|
19
|
+
index.bulk_delete(delete_records) if delete_records.any?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Searchkick
|
2
|
+
class ProcessQueueJob < ActiveJob::Base
|
3
|
+
queue_as :searchkick
|
4
|
+
|
5
|
+
def perform(class_name:)
|
6
|
+
model = class_name.constantize
|
7
|
+
|
8
|
+
limit = model.searchkick_index.options[:batch_size] || 1000
|
9
|
+
record_ids = model.searchkick_index.reindex_queue.reserve(limit: limit)
|
10
|
+
if record_ids.any?
|
11
|
+
Searchkick::ProcessBatchJob.perform_later(
|
12
|
+
class_name: model.name,
|
13
|
+
record_ids: record_ids
|
14
|
+
)
|
15
|
+
# TODO when moving to reliable queuing, mark as complete
|
16
|
+
|
17
|
+
if record_ids.size == limit
|
18
|
+
Searchkick::ProcessQueueJob.perform_later(class_name: class_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|