searchkick 4.4.0 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/searchkick.rb CHANGED
@@ -1,29 +1,41 @@
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"
9
- require "searchkick/index"
10
- require "searchkick/indexer"
11
- require "searchkick/hash_wrapper"
12
- require "searchkick/middleware"
13
- require "searchkick/model"
14
- require "searchkick/multi_search"
15
- require "searchkick/query"
16
- require "searchkick/reindex_queue"
17
- require "searchkick/record_data"
18
- require "searchkick/record_indexer"
19
- require "searchkick/results"
20
- require "searchkick/version"
13
+ require_relative "searchkick/controller_runtime"
14
+ require_relative "searchkick/index"
15
+ require_relative "searchkick/index_cache"
16
+ require_relative "searchkick/index_options"
17
+ require_relative "searchkick/indexer"
18
+ require_relative "searchkick/hash_wrapper"
19
+ require_relative "searchkick/log_subscriber"
20
+ require_relative "searchkick/model"
21
+ require_relative "searchkick/multi_search"
22
+ require_relative "searchkick/query"
23
+ require_relative "searchkick/reindex_queue"
24
+ require_relative "searchkick/record_data"
25
+ require_relative "searchkick/record_indexer"
26
+ require_relative "searchkick/relation"
27
+ require_relative "searchkick/relation_indexer"
28
+ require_relative "searchkick/results"
29
+ require_relative "searchkick/version"
30
+ require_relative "searchkick/where"
21
31
 
22
32
  # integrations
23
- require "searchkick/railtie" if defined?(Rails)
24
- require "searchkick/logging" if defined?(ActiveSupport::Notifications)
33
+ require_relative "searchkick/railtie" if defined?(Rails)
25
34
 
26
35
  module Searchkick
36
+ # requires faraday
37
+ autoload :Middleware, "searchkick/middleware"
38
+
27
39
  # background jobs
28
40
  autoload :BulkReindexJob, "searchkick/bulk_reindex_job"
29
41
  autoload :ProcessBatchJob, "searchkick/process_batch_job"
@@ -33,18 +45,21 @@ module Searchkick
33
45
  # errors
34
46
  class Error < StandardError; end
35
47
  class MissingIndexError < Error; end
36
- class UnsupportedVersionError < Error; end
37
- class InvalidQueryError < Elasticsearch::Transport::Transport::Errors::BadRequest; end
48
+ class UnsupportedVersionError < Error
49
+ def message
50
+ "This version of Searchkick requires Elasticsearch 7+ or OpenSearch 1+"
51
+ end
52
+ end
53
+ class InvalidQueryError < Error; end
38
54
  class DangerousOperation < Error; end
39
55
  class ImportError < Error; end
40
56
 
41
57
  class << self
42
- attr_accessor :search_method_name, :wordnet_path, :timeout, :models, :client_options, :redis, :index_prefix, :index_suffix, :queue_name, :model_options
58
+ attr_accessor :search_method_name, :timeout, :models, :client_options, :redis, :index_prefix, :index_suffix, :queue_name, :model_options, :client_type
43
59
  attr_writer :client, :env, :search_timeout
44
60
  attr_reader :aws_credentials
45
61
  end
46
62
  self.search_method_name = :search
47
- self.wordnet_path = "/var/lib/wn_s.pl"
48
63
  self.timeout = 10
49
64
  self.models = []
50
65
  self.client_options = {}
@@ -53,15 +68,45 @@ module Searchkick
53
68
 
54
69
  def self.client
55
70
  @client ||= begin
56
- require "typhoeus/adapters/faraday" if defined?(Typhoeus) && Gem::Version.new(Faraday::VERSION) < Gem::Version.new("0.14.0")
57
-
58
- Elasticsearch::Client.new({
59
- url: ENV["ELASTICSEARCH_URL"],
60
- transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
61
- retry_on_failure: 2
62
- }.deep_merge(client_options)) do |f|
63
- f.use Searchkick::Middleware
64
- f.request signer_middleware_key, signer_middleware_aws_params if aws_credentials
71
+ client_type =
72
+ if self.client_type
73
+ self.client_type
74
+ elsif defined?(OpenSearch::Client) && defined?(Elasticsearch::Client)
75
+ raise Error, "Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch"
76
+ elsif defined?(OpenSearch::Client)
77
+ :opensearch
78
+ elsif defined?(Elasticsearch::Client)
79
+ :elasticsearch
80
+ else
81
+ raise Error, "No client found - install the `elasticsearch` or `opensearch-ruby` gem"
82
+ end
83
+
84
+ # check after client to ensure faraday is installed
85
+ # TODO remove in Searchkick 6
86
+ if defined?(Typhoeus) && Gem::Version.new(Faraday::VERSION) < Gem::Version.new("0.14.0")
87
+ require "typhoeus/adapters/faraday"
88
+ end
89
+
90
+ if client_type == :opensearch
91
+ OpenSearch::Client.new({
92
+ url: ENV["OPENSEARCH_URL"],
93
+ transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
94
+ retry_on_failure: 2
95
+ }.deep_merge(client_options)) do |f|
96
+ f.use Searchkick::Middleware
97
+ f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
98
+ end
99
+ else
100
+ raise Error, "The `elasticsearch` gem must be 7+" if Elasticsearch::VERSION.to_i < 7
101
+
102
+ Elasticsearch::Client.new({
103
+ url: ENV["ELASTICSEARCH_URL"],
104
+ transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
105
+ retry_on_failure: 2
106
+ }.deep_merge(client_options)) do |f|
107
+ f.use Searchkick::Middleware
108
+ f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
109
+ end
65
110
  end
66
111
  end
67
112
  end
@@ -74,20 +119,26 @@ module Searchkick
74
119
  (defined?(@search_timeout) && @search_timeout) || timeout
75
120
  end
76
121
 
77
- def self.server_version
78
- @server_version ||= client.info["version"]["number"]
122
+ # private
123
+ def self.server_info
124
+ @server_info ||= client.info
79
125
  end
80
126
 
81
- def self.server_below?(version)
82
- Gem::Version.new(server_version.split("-")[0]) < Gem::Version.new(version.split("-")[0])
127
+ def self.server_version
128
+ @server_version ||= server_info["version"]["number"]
83
129
  end
84
130
 
85
- # memoize for performance
86
- def self.server_below7?
87
- unless defined?(@server_below7)
88
- @server_below7 = server_below?("7.0.0")
131
+ def self.opensearch?
132
+ unless defined?(@opensearch)
133
+ @opensearch = server_info["version"]["distribution"] == "opensearch"
89
134
  end
90
- @server_below7
135
+ @opensearch
136
+ end
137
+
138
+ # TODO always check true version in Searchkick 6
139
+ def self.server_below?(version, true_version = false)
140
+ server_version = !true_version && opensearch? ? "7.10.2" : self.server_version
141
+ Gem::Version.new(server_version.split("-")[0]) < Gem::Version.new(version.split("-")[0])
91
142
  end
92
143
 
93
144
  def self.search(term = "*", model: nil, **options, &block)
@@ -115,17 +166,27 @@ module Searchkick
115
166
  end
116
167
  end
117
168
 
118
- options = options.merge(block: block) if block
119
- query = Searchkick::Query.new(klass, term, **options)
169
+ # TODO remove in Searchkick 6
120
170
  if options[:execute] == false
121
- query
122
- else
123
- query.execute
171
+ Searchkick.warn("The execute option is no longer needed")
172
+ options.delete(:execute)
124
173
  end
174
+
175
+ options = options.merge(block: block) if block
176
+ Relation.new(klass, term, **options)
125
177
  end
126
178
 
127
179
  def self.multi_search(queries)
128
- Searchkick::MultiSearch.new(queries).perform
180
+ return if queries.empty?
181
+
182
+ queries = queries.map { |q| q.send(:query) }
183
+ event = {
184
+ name: "Multi Search",
185
+ body: queries.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join,
186
+ }
187
+ ActiveSupport::Notifications.instrument("multi_search.searchkick", event) do
188
+ MultiSearch.new(queries).perform
189
+ end
129
190
  end
130
191
 
131
192
  # callbacks
@@ -146,13 +207,25 @@ module Searchkick
146
207
  end
147
208
  end
148
209
 
149
- def self.callbacks(value = nil)
210
+ # message is private
211
+ def self.callbacks(value = nil, message: nil)
150
212
  if block_given?
151
213
  previous_value = callbacks_value
152
214
  begin
153
215
  self.callbacks_value = value
154
216
  result = yield
155
- indexer.perform if callbacks_value == :bulk
217
+ if callbacks_value == :bulk && indexer.queued_items.any?
218
+ event = {}
219
+ if message
220
+ message.call(event)
221
+ else
222
+ event[:name] = "Bulk"
223
+ event[:count] = indexer.queued_items.size
224
+ end
225
+ ActiveSupport::Notifications.instrument("request.searchkick", event) do
226
+ indexer.perform
227
+ end
228
+ end
156
229
  result
157
230
  ensure
158
231
  self.callbacks_value = previous_value
@@ -163,25 +236,20 @@ module Searchkick
163
236
  end
164
237
 
165
238
  def self.aws_credentials=(creds)
166
- begin
167
- require "faraday_middleware/aws_signers_v4"
168
- rescue LoadError
169
- require "faraday_middleware/aws_sigv4"
170
- end
239
+ require "faraday_middleware/aws_sigv4"
240
+
171
241
  @aws_credentials = creds
172
242
  @client = nil # reset client
173
243
  end
174
244
 
175
245
  def self.reindex_status(index_name)
176
- if redis
177
- batches_left = Searchkick::Index.new(index_name).batches_left
178
- {
179
- completed: batches_left == 0,
180
- batches_left: batches_left
181
- }
182
- else
183
- raise Searchkick::Error, "Redis not configured"
184
- end
246
+ raise Error, "Redis not configured" unless redis
247
+
248
+ batches_left = Index.new(index_name).batches_left
249
+ {
250
+ completed: batches_left == 0,
251
+ batches_left: batches_left
252
+ }
185
253
  end
186
254
 
187
255
  def self.with_redis
@@ -201,29 +269,41 @@ module Searchkick
201
269
  end
202
270
 
203
271
  # private
204
- def self.load_records(records, ids)
205
- records =
206
- if records.respond_to?(:primary_key)
207
- # ActiveRecord
208
- records.where(records.primary_key => ids) if records.primary_key
209
- elsif records.respond_to?(:queryable)
210
- # Mongoid 3+
211
- records.queryable.for_ids(ids)
212
- elsif records.respond_to?(:unscoped) && :id.respond_to?(:in)
213
- # Nobrainer
214
- records.unscoped.where(:id.in => ids)
215
- elsif records.respond_to?(:key_column_names)
216
- records.where(records.key_column_names.first => ids)
272
+ def self.load_records(relation, ids)
273
+ relation =
274
+ if relation.respond_to?(:primary_key)
275
+ primary_key = relation.primary_key
276
+ raise Error, "Need primary key to load records" if !primary_key
277
+
278
+ relation.where(primary_key => ids)
279
+ elsif relation.respond_to?(:queryable)
280
+ relation.queryable.for_ids(ids)
217
281
  end
218
282
 
219
- raise Searchkick::Error, "Not sure how to load records" if !records
283
+ raise Error, "Not sure how to load records" if !relation
220
284
 
221
- records
285
+ relation
286
+ end
287
+
288
+ # public (for reindexing conversions)
289
+ def self.load_model(class_name, allow_child: false)
290
+ model = class_name.safe_constantize
291
+ raise Error, "Could not find class: #{class_name}" unless model
292
+ if allow_child
293
+ unless model.respond_to?(:searchkick_klass)
294
+ raise Error, "#{class_name} is not a searchkick model"
295
+ end
296
+ else
297
+ unless Searchkick.models.include?(model)
298
+ raise Error, "#{class_name} is not a searchkick model"
299
+ end
300
+ end
301
+ model
222
302
  end
223
303
 
224
304
  # private
225
305
  def self.indexer
226
- Thread.current[:searchkick_indexer] ||= Searchkick::Indexer.new
306
+ Thread.current[:searchkick_indexer] ||= Indexer.new
227
307
  end
228
308
 
229
309
  # private
@@ -236,22 +316,9 @@ module Searchkick
236
316
  Thread.current[:searchkick_callbacks_enabled] = value
237
317
  end
238
318
 
239
- # private
240
- def self.signer_middleware_key
241
- defined?(FaradayMiddleware::AwsSignersV4) ? :aws_signers_v4 : :aws_sigv4
242
- end
243
-
244
319
  # private
245
320
  def self.signer_middleware_aws_params
246
- if signer_middleware_key == :aws_sigv4
247
- {service: "es", region: "us-east-1"}.merge(aws_credentials)
248
- else
249
- {
250
- credentials: aws_credentials[:credentials] || Aws::Credentials.new(aws_credentials[:access_key_id], aws_credentials[:secret_access_key]),
251
- service_name: "es",
252
- region: aws_credentials[:region] || "us-east-1"
253
- }
254
- end
321
+ {service: "es", region: "us-east-1"}.merge(aws_credentials)
255
322
  end
256
323
 
257
324
  # private
@@ -261,16 +328,55 @@ module Searchkick
261
328
  def self.relation?(klass)
262
329
  if klass.respond_to?(:current_scope)
263
330
  !klass.current_scope.nil?
264
- elsif defined?(Mongoid::Threaded)
265
- !Mongoid::Threaded.current_scope(klass).nil?
331
+ else
332
+ klass.is_a?(Mongoid::Criteria) || !Mongoid::Threaded.current_scope(klass).nil?
266
333
  end
267
334
  end
268
- end
269
335
 
270
- # TODO find better ActiveModel hook
271
- require "active_model/callbacks"
272
- ActiveModel::Callbacks.include(Searchkick::Model)
336
+ # private
337
+ def self.scope(model)
338
+ # safety check to make sure used properly in code
339
+ raise Error, "Cannot scope relation" if relation?(model)
340
+
341
+ if model.searchkick_options[:unscope]
342
+ model.unscoped
343
+ else
344
+ model
345
+ end
346
+ end
347
+
348
+ # private
349
+ def self.not_found_error?(e)
350
+ (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Errors::NotFound)) ||
351
+ (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Errors::NotFound)) ||
352
+ (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Errors::NotFound))
353
+ end
354
+
355
+ # private
356
+ def self.transport_error?(e)
357
+ (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Error)) ||
358
+ (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Error)) ||
359
+ (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Error))
360
+ end
361
+
362
+ # private
363
+ def self.not_allowed_error?(e)
364
+ (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Errors::MethodNotAllowed)) ||
365
+ (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Errors::MethodNotAllowed)) ||
366
+ (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Errors::MethodNotAllowed))
367
+ end
368
+ end
273
369
 
274
370
  ActiveSupport.on_load(:active_record) do
275
371
  extend Searchkick::Model
276
372
  end
373
+
374
+ ActiveSupport.on_load(:mongoid) do
375
+ Mongoid::Document::ClassMethods.include Searchkick::Model
376
+ end
377
+
378
+ ActiveSupport.on_load(:action_controller) do
379
+ include Searchkick::ControllerRuntime
380
+ end
381
+
382
+ Searchkick::LogSubscriber.attach_to :searchkick
@@ -1,21 +1,25 @@
1
1
  namespace :searchkick do
2
- desc "reindex model"
2
+ desc "reindex a model (specify CLASS)"
3
3
  task reindex: :environment do
4
- if ENV["CLASS"]
5
- klass = ENV["CLASS"].constantize rescue nil
6
- if klass
7
- klass.reindex
8
- else
9
- abort "Could not find class: #{ENV['CLASS']}"
4
+ class_name = ENV["CLASS"]
5
+ abort "USAGE: rake searchkick:reindex CLASS=Product" unless class_name
6
+
7
+ model =
8
+ begin
9
+ Searchkick.load_model(class_name)
10
+ rescue Searchkick::Error => e
11
+ abort e.message
10
12
  end
11
- else
12
- abort "USAGE: rake searchkick:reindex CLASS=Product"
13
- end
13
+
14
+ puts "Reindexing #{model.name}..."
15
+ model.reindex
16
+ puts "Reindex successful"
14
17
  end
15
18
 
16
19
  namespace :reindex do
17
20
  desc "reindex all models"
18
21
  task all: :environment do
22
+ # eager load models to populate Searchkick.models
19
23
  if Rails.respond_to?(:autoloaders) && Rails.autoloaders.zeitwerk_enabled?
20
24
  # fix for https://github.com/rails/rails/issues/37006
21
25
  Zeitwerk::Loader.eager_load_all
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.4.0
4
+ version: 5.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-18 00:00:00.000000000 Z
11
+ date: 2023-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -16,28 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '6.1'
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
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '6'
26
+ version: '6.1'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: hashie
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,80 +38,24 @@ dependencies:
52
38
  - - ">="
53
39
  - !ruby/object:Gem::Version
54
40
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: bundler
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: minitest
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: elasticsearch-xpack
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: 7.8.0.pre
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: 7.8.0.pre
111
- description:
112
- email: andrew@chartkick.com
41
+ description:
42
+ email: andrew@ankane.org
113
43
  executables: []
114
44
  extensions: []
115
45
  extra_rdoc_files: []
116
46
  files:
117
47
  - CHANGELOG.md
118
- - CONTRIBUTING.md
119
48
  - LICENSE.txt
120
49
  - README.md
121
50
  - lib/searchkick.rb
122
- - lib/searchkick/bulk_indexer.rb
123
51
  - lib/searchkick/bulk_reindex_job.rb
52
+ - lib/searchkick/controller_runtime.rb
124
53
  - lib/searchkick/hash_wrapper.rb
125
54
  - lib/searchkick/index.rb
55
+ - lib/searchkick/index_cache.rb
126
56
  - lib/searchkick/index_options.rb
127
57
  - lib/searchkick/indexer.rb
128
- - lib/searchkick/logging.rb
58
+ - lib/searchkick/log_subscriber.rb
129
59
  - lib/searchkick/middleware.rb
130
60
  - lib/searchkick/model.rb
131
61
  - lib/searchkick/multi_search.rb
@@ -137,14 +67,17 @@ files:
137
67
  - lib/searchkick/record_indexer.rb
138
68
  - lib/searchkick/reindex_queue.rb
139
69
  - lib/searchkick/reindex_v2_job.rb
70
+ - lib/searchkick/relation.rb
71
+ - lib/searchkick/relation_indexer.rb
140
72
  - lib/searchkick/results.rb
141
73
  - lib/searchkick/version.rb
74
+ - lib/searchkick/where.rb
142
75
  - lib/tasks/searchkick.rake
143
76
  homepage: https://github.com/ankane/searchkick
144
77
  licenses:
145
78
  - MIT
146
79
  metadata: {}
147
- post_install_message:
80
+ post_install_message:
148
81
  rdoc_options: []
149
82
  require_paths:
150
83
  - lib
@@ -152,15 +85,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
85
  requirements:
153
86
  - - ">="
154
87
  - !ruby/object:Gem::Version
155
- version: '2.4'
88
+ version: '3'
156
89
  required_rubygems_version: !ruby/object:Gem::Requirement
157
90
  requirements:
158
91
  - - ">="
159
92
  - !ruby/object:Gem::Version
160
93
  version: '0'
161
94
  requirements: []
162
- rubygems_version: 3.1.2
163
- signing_key:
95
+ rubygems_version: 3.4.10
96
+ signing_key:
164
97
  specification_version: 4
165
- summary: Intelligent search made easy with Rails and Elasticsearch
98
+ summary: Intelligent search made easy with Rails and Elasticsearch or OpenSearch
166
99
  test_files: []
data/CONTRIBUTING.md DELETED
@@ -1,53 +0,0 @@
1
- # Contributing
2
-
3
- First, thanks for wanting to contribute. You’re awesome! :heart:
4
-
5
- ## Help
6
-
7
- We’re not able to provide support through GitHub Issues. If you’re looking for help with your code, try posting on [Stack Overflow](https://stackoverflow.com/questions/tagged/searchkick).
8
-
9
- All features should be documented. If you don’t see a feature in the docs, assume it doesn’t exist.
10
-
11
- ## Bugs
12
-
13
- Think you’ve discovered a bug?
14
-
15
- 1. Search existing issues to see if it’s been reported.
16
- 2. Try the `master` branch to make sure it hasn’t been fixed.
17
-
18
- ```rb
19
- gem "searchkick", github: "ankane/searchkick"
20
- ```
21
-
22
- 3. Try the `debug` option when searching. This can reveal useful info.
23
-
24
- ```ruby
25
- Product.search("something", debug: true)
26
- ```
27
-
28
- If the above steps don’t help, create an issue.
29
-
30
- - Recreate the problem by forking [this gist](https://gist.github.com/ankane/f80b0923d9ae2c077f41997f7b704e5c). Include a link to your gist and the output in the issue.
31
- - For exceptions, include the complete backtrace.
32
-
33
- ## New Features
34
-
35
- If you’d like to discuss a new feature, create an issue and start the title with `[Idea]`.
36
-
37
- ## Pull Requests
38
-
39
- Fork the project and create a pull request. A few tips:
40
-
41
- - Keep changes to a minimum. If you have multiple features or fixes, submit multiple pull requests.
42
- - Follow the existing style. The code should read like it’s written by a single person.
43
- - Add one or more tests if possible. Make sure existing tests pass with:
44
-
45
- ```sh
46
- bundle exec rake test
47
- ```
48
-
49
- Feel free to open an issue to get feedback on your idea before spending too much time on it.
50
-
51
- ---
52
-
53
- This contributing guide is released under [CCO](https://creativecommons.org/publicdomain/zero/1.0/) (public domain). Use it for your own project without attribution.