searchkick 1.5.1 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3fa47681f8cf22fcc751a944326bfbd16f437a55
4
- data.tar.gz: 270f29030b412fb937c33c04e00069982c48e3c9
3
+ metadata.gz: 06b1b68ac3cb57317a8fdd0c94e3988ffed37852
4
+ data.tar.gz: b5a51c3c2126b88749bd114c0355a9e593f2cdfa
5
5
  SHA512:
6
- metadata.gz: a721f818961659f9d3337573c52da0414db81040d223830f5ca05d8fda59035f4580de17bab411331ea7939bf98795fe60a8d25bd2fa8375b25370f74cee4551
7
- data.tar.gz: 5ba8046de966775866b9015f3506aa4d9d6a6c2e8a83536adc847a31b94754a395fec203530367b95bed518f481bff689e6c52d80f0c34758ca72908d888eee8
6
+ metadata.gz: b520ec977fec7ac5a65aa6a5aacd9fb4c6142a079385bd63dce20b28ccd59f7cf84993daa4e1f68f8befba383ceb2246ce2a78b38846ec43e7d4432bb9587b40
7
+ data.tar.gz: b0032add9b2f7ed647ecbc9f2db958f5f240a0725b3990ea24697290e8b61c5e2d6afdf8777742fafaa2fbb33407f0d62f6480239522099869a9bccaf4b9092e
data/.travis.yml CHANGED
@@ -15,13 +15,6 @@ notifications:
15
15
  gemfile:
16
16
  - Gemfile
17
17
  - test/gemfiles/activerecord42.gemfile
18
- - test/gemfiles/activerecord41.gemfile
19
- - test/gemfiles/activerecord40.gemfile
20
- - test/gemfiles/activerecord32.gemfile
21
- - test/gemfiles/activerecord31.gemfile
22
- - test/gemfiles/mongoid2.gemfile
23
- - test/gemfiles/mongoid3.gemfile
24
- - test/gemfiles/mongoid4.gemfile
25
18
  - test/gemfiles/mongoid5.gemfile
26
19
  - test/gemfiles/mongoid6.gemfile
27
20
  env:
@@ -29,12 +22,6 @@ env:
29
22
  jdk: oraclejdk8
30
23
  matrix:
31
24
  include:
32
- - gemfile: Gemfile
33
- env: ELASTICSEARCH_VERSION=1.0.0
34
- jdk: oraclejdk7
35
- - gemfile: Gemfile
36
- env: ELASTICSEARCH_VERSION=1.7.0
37
- jdk: oraclejdk7
38
25
  - gemfile: Gemfile
39
26
  env: ELASTICSEARCH_VERSION=2.0.0
40
27
  jdk: oraclejdk7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 2.0.0
2
+
3
+ - Added support for `reindex` on associations
4
+
5
+ Breaking changes
6
+
7
+ - Removed support for Elasticsearch 1 as it reaches [end of life](https://www.elastic.co/support/eol)
8
+ - Removed facets, legacy options, and legacy methods
9
+ - Invalid options now throw an `ArgumentError`
10
+ - Renamed `select_v2` to `select` (legacy `select` no longer available)
11
+ - The `_all` field is disabled if `searchable` option is used (for performance)
12
+ - The `partial_reindex(:method_name)` method has been replaced with `reindex(:method_name)`
13
+ - The `unsearchable` and `only_analyzed` options have been removed in favor of `searchable` and `filterable`
14
+ - `load: false` no longer returns an array in Elasticsearch 2
15
+
1
16
  ## 1.5.1
2
17
 
3
18
  - Added `client_options`
data/README.md CHANGED
@@ -27,6 +27,8 @@ Plus:
27
27
 
28
28
  [![Build Status](https://travis-ci.org/ankane/searchkick.svg?branch=master)](https://travis-ci.org/ankane/searchkick)
29
29
 
30
+ **Searchkick 2.0 was just released!** See [notable changes](#200).
31
+
30
32
  ## Get Started
31
33
 
32
34
  [Install Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html). For Homebrew, use:
@@ -44,6 +46,8 @@ Add this line to your application’s Gemfile:
44
46
  gem 'searchkick'
45
47
  ```
46
48
 
49
+ The latest version works with Elasticsearch 2 and 5. For Elasticsearch 1, use version 1.5.1 and [this readme](https://github.com/ankane/searchkick/blob/v1.5.1/README.md).
50
+
47
51
  Add searchkick to models you want to search.
48
52
 
49
53
  ```ruby
@@ -115,7 +119,7 @@ limit: 20, offset: 40
115
119
  Select
116
120
 
117
121
  ```ruby
118
- select_v2: ["name"]
122
+ select: ["name"]
119
123
  ```
120
124
 
121
125
  ### Results
@@ -759,53 +763,6 @@ Product.search "pear", aggs: {products_per_year: {date_histogram: {field: :creat
759
763
  aggs: {date_field: {date_ranges: date_ranges}}
760
764
  ```
761
765
 
762
- ### Facets [deprecated]
763
-
764
- Facets have been deprecated in favor of aggregations as of Searchkick 1.0. See [how to upgrade](#moving-from-facets).
765
-
766
- ```ruby
767
- products = Product.search "chuck taylor", facets: [:product_type, :gender, :brand]
768
- p products.facets
769
- ```
770
-
771
- By default, `where` conditions are not applied to facets (for backward compatibility).
772
-
773
- ```ruby
774
- Product.search "wingtips", where: {color: "brandy"}, facets: [:size]
775
- # facets *not* filtered by color :(
776
- ```
777
-
778
- Change this with:
779
-
780
- ```ruby
781
- Product.search "wingtips", where: {color: "brandy"}, facets: [:size], smart_facets: true
782
- ```
783
-
784
- or set `where` conditions for each facet separately:
785
-
786
- ```ruby
787
- Product.search "wingtips", facets: {size: {where: {color: "brandy"}}}
788
- ```
789
-
790
- Limit
791
-
792
- ```ruby
793
- Product.search "apples", facets: {store_id: {limit: 10}}
794
- ```
795
-
796
- Ranges
797
-
798
- ```ruby
799
- price_ranges = [{to: 20}, {from: 20, to: 50}, {from: 50}]
800
- Product.search "*", facets: {price: {ranges: price_ranges}}
801
- ```
802
-
803
- Use the `stats` option to get to max, min, mean, and total scores for each facet
804
-
805
- ```ruby
806
- Product.search "*", facets: {store_id: {stats: true}}
807
- ```
808
-
809
766
  ### Highlight
810
767
 
811
768
  Specify which fields to index with highlighting.
@@ -1001,7 +958,7 @@ Product.search("soap", explain: true).response
1001
958
  See how Elasticsearch tokenizes your queries with:
1002
959
 
1003
960
  ```ruby
1004
- Product.searchkick_index.tokens("Dish Washer Soap", analyzer: "default_index")
961
+ Product.searchkick_index.tokens("Dish Washer Soap", analyzer: "searchkick_index")
1005
962
  # ["dish", "dishwash", "washer", "washersoap", "soap"]
1006
963
 
1007
964
  Product.searchkick_index.tokens("dishwasher soap", analyzer: "searchkick_search")
@@ -1297,11 +1254,6 @@ product.reindex_async
1297
1254
  Reindex more than one record without recreating the index
1298
1255
 
1299
1256
  ```ruby
1300
- # do this ...
1301
- some_company.products.each { |p| p.reindex }
1302
- # or this ...
1303
- Product.searchkick_index.import(some_company.products)
1304
- # don't do the following as it will recreate the index with some_company's products only
1305
1257
  some_company.products.reindex
1306
1258
  ```
1307
1259
 
@@ -1531,6 +1483,21 @@ View the [changelog](https://github.com/ankane/searchkick/blob/master/CHANGELOG.
1531
1483
 
1532
1484
  Important notes are listed below.
1533
1485
 
1486
+ ### 2.0.0
1487
+
1488
+ - Added support for `reindex` on associations
1489
+
1490
+ #### Breaking Changes
1491
+
1492
+ - Removed support for Elasticsearch 1 as it reaches [end of life](https://www.elastic.co/support/eol)
1493
+ - Removed facets, legacy options, and legacy methods
1494
+ - Invalid options now throw an `ArgumentError`
1495
+ - Renamed `select_v2` to `select` (legacy `select` no longer available)
1496
+ - The `_all` field is disabled if `searchable` option is used (for performance)
1497
+ - The `partial_reindex(:method_name)` method has been replaced with `reindex(:method_name)`
1498
+ - The `unsearchable` and `only_analyzed` options have been removed in favor of `searchable` and `filterable`
1499
+ - `load: false` no longer returns an array in Elasticsearch 2
1500
+
1534
1501
  ### 1.0.0
1535
1502
 
1536
1503
  - Added support for Elasticsearch 2.0
data/lib/searchkick.rb CHANGED
@@ -7,7 +7,6 @@ require "searchkick/index"
7
7
  require "searchkick/indexer"
8
8
  require "searchkick/results"
9
9
  require "searchkick/query"
10
- require "searchkick/reindex_job"
11
10
  require "searchkick/model"
12
11
  require "searchkick/tasks"
13
12
  require "searchkick/middleware"
@@ -75,7 +74,7 @@ module Searchkick
75
74
  Gem::Version.new(server_version.sub("-", ".")) < Gem::Version.new(version.sub("-", "."))
76
75
  end
77
76
 
78
- def self.search(term = nil, options = {}, &block)
77
+ def self.search(term = "*", **options, &block)
79
78
  query = Searchkick::Query.new(nil, term, options)
80
79
  block.call(query.body) if block
81
80
  if options[:execute] == false
@@ -92,7 +91,7 @@ module Searchkick
92
91
  query.handle_response(responses[i])
93
92
  end
94
93
  end
95
- nil
94
+ queries
96
95
  end
97
96
 
98
97
  # callbacks
@@ -105,17 +105,15 @@ module Searchkick
105
105
  if Searchkick.callbacks_value.nil?
106
106
  if defined?(Searchkick::ReindexV2Job)
107
107
  Searchkick::ReindexV2Job.perform_later(record.class.name, record.id.to_s)
108
- elsif defined?(Delayed::Job)
109
- Delayed::Job.enqueue Searchkick::ReindexJob.new(record.class.name, record.id.to_s)
110
108
  else
111
- raise Searchkick::Error, "Job adapter not found"
109
+ raise Searchkick::Error, "Active Job not found"
112
110
  end
113
111
  else
114
112
  reindex_record(record)
115
113
  end
116
114
  end
117
115
 
118
- def similar_record(record, options = {})
116
+ def similar_record(record, **options)
119
117
  like_text = retrieve(record).to_hash
120
118
  .keep_if { |k, _| !options[:fields] || options[:fields].map(&:to_s).include?(k) }
121
119
  .values.compact.join(" ")
@@ -133,7 +131,7 @@ module Searchkick
133
131
 
134
132
  # search
135
133
 
136
- def search_model(searchkick_klass, term = nil, options = {}, &block)
134
+ def search_model(searchkick_klass, term = "*", **options, &block)
137
135
  query = Searchkick::Query.new(searchkick_klass, term, options)
138
136
  yield(query.body) if block
139
137
  if options[:execute] == false
@@ -145,21 +143,21 @@ module Searchkick
145
143
 
146
144
  # reindex
147
145
 
148
- def create_index(options = {})
149
- index_options = options[:index_options] || self.index_options
146
+ def create_index(index_options: nil)
147
+ index_options ||= self.index_options
150
148
  index = Searchkick::Index.new("#{name}_#{Time.now.strftime('%Y%m%d%H%M%S%L')}", @options)
151
149
  index.create(index_options)
152
150
  index
153
151
  end
154
152
 
155
- def all_indices(options = {})
153
+ def all_indices(unaliased: false)
156
154
  indices =
157
155
  begin
158
156
  client.indices.get_aliases
159
157
  rescue Elasticsearch::Transport::Transport::Errors::NotFound
160
158
  {}
161
159
  end
162
- indices = indices.select { |_k, v| v.empty? || v["aliases"].empty? } if options[:unaliased]
160
+ indices = indices.select { |_k, v| v.empty? || v["aliases"].empty? } if unaliased
163
161
  indices.select { |k, _v| k =~ /\A#{Regexp.escape(name)}_\d{14,17}\z/ }.keys
164
162
  end
165
163
 
@@ -187,10 +185,7 @@ module Searchkick
187
185
 
188
186
  # https://gist.github.com/jarosan/3124884
189
187
  # http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
190
- def reindex_scope(scope, options = {})
191
- skip_import = options[:import] == false
192
- resume = options[:resume]
193
-
188
+ def reindex_scope(scope, import: true, resume: false)
194
189
  if resume
195
190
  index_name = all_indices.sort.last
196
191
  raise Searchkick::Error, "No index to resume" unless index_name
@@ -204,7 +199,7 @@ module Searchkick
204
199
  # check if alias exists
205
200
  if alias_exists?
206
201
  # import before swap
207
- index.import_scope(scope, resume: resume) unless skip_import
202
+ index.import_scope(scope, resume: resume) if import
208
203
 
209
204
  # get existing indices to remove
210
205
  swap(index.name)
@@ -214,7 +209,7 @@ module Searchkick
214
209
  swap(index.name)
215
210
 
216
211
  # import after swap
217
- index.import_scope(scope, resume: resume) unless skip_import
212
+ index.import_scope(scope, resume: resume) if import
218
213
  end
219
214
 
220
215
  index.refresh
@@ -222,14 +217,13 @@ module Searchkick
222
217
  true
223
218
  end
224
219
 
225
- def import_scope(scope, options = {})
220
+ def import_scope(scope, resume: false, method_name: nil)
226
221
  batch_size = @options[:batch_size] || 1000
227
- method_name = options[:method_name]
228
222
 
229
223
  # use scope for import
230
224
  scope = scope.search_import if scope.respond_to?(:search_import)
231
225
  if scope.respond_to?(:find_in_batches)
232
- if options[:resume]
226
+ if resume
233
227
  # use total docs instead of max id since there's not a great way
234
228
  # to get the max _id without scripting since it's a string
235
229
 
@@ -12,7 +12,7 @@ module Searchkick
12
12
  below22 = Searchkick.server_below?("2.2.0")
13
13
  below50 = Searchkick.server_below?("5.0.0-alpha1")
14
14
  default_type = below50 ? "string" : "text"
15
- default_analyzer = below50 ? :default_index : :default
15
+ default_analyzer = :searchkick_index
16
16
  keyword_mapping =
17
17
  if below50
18
18
  {
@@ -58,11 +58,6 @@ module Searchkick
58
58
  filter: ["standard", "lowercase", "asciifolding", "searchkick_stemmer"]
59
59
  },
60
60
  # https://github.com/leschenko/elasticsearch_autocomplete/blob/master/lib/elasticsearch_autocomplete/analyzers.rb
61
- searchkick_autocomplete_index: {
62
- type: "custom",
63
- tokenizer: "searchkick_autocomplete_ngram",
64
- filter: ["lowercase", "asciifolding"]
65
- },
66
61
  searchkick_autocomplete_search: {
67
62
  type: "custom",
68
63
  tokenizer: "keyword",
@@ -149,13 +144,6 @@ module Searchkick
149
144
  type: "mapping",
150
145
  mappings: ["&=> and "]
151
146
  }
152
- },
153
- tokenizer: {
154
- searchkick_autocomplete_ngram: {
155
- type: "edgeNGram",
156
- min_gram: 1,
157
- max_gram: 50
158
- }
159
147
  }
160
148
  }
161
149
  }
@@ -233,7 +221,7 @@ module Searchkick
233
221
  end
234
222
 
235
223
  mapping_options = Hash[
236
- [:autocomplete, :suggest, :word, :text_start, :text_middle, :text_end, :word_start, :word_middle, :word_end, :highlight, :searchable, :filterable, :only_analyzed]
224
+ [:suggest, :word, :text_start, :text_middle, :text_end, :word_start, :word_middle, :word_end, :highlight, :searchable, :filterable]
237
225
  .map { |type| [type, (options[type] || []).map(&:to_s)] }
238
226
  ]
239
227
 
@@ -242,7 +230,7 @@ module Searchkick
242
230
  mapping_options.values.flatten.uniq.each do |field|
243
231
  fields = {}
244
232
 
245
- if mapping_options[:only_analyzed].include?(field) || (options.key?(:filterable) && !mapping_options[:filterable].include?(field))
233
+ if options.key?(:filterable) && !mapping_options[:filterable].include?(field)
246
234
  fields[field] = {type: default_type, index: "no"}
247
235
  else
248
236
  fields[field] = keyword_mapping
@@ -257,7 +245,7 @@ module Searchkick
257
245
  end
258
246
  end
259
247
 
260
- mapping_options.except(:highlight, :searchable, :filterable, :only_analyzed, :word).each do |type, f|
248
+ mapping_options.except(:highlight, :searchable, :filterable, :word).each do |type, f|
261
249
  if options[:match] == type || f.include?(field)
262
250
  fields[type] = {type: default_type, index: "analyzed", analyzer: "searchkick_#{type}_index"}
263
251
  end
@@ -286,13 +274,6 @@ module Searchkick
286
274
  mapping[field] = shape_options.merge(type: "geo_shape")
287
275
  end
288
276
 
289
- (options[:unsearchable] || []).map(&:to_s).each do |field|
290
- mapping[field] = {
291
- type: default_type,
292
- index: "no"
293
- }
294
- end
295
-
296
277
  routing = {}
297
278
  if options[:routing]
298
279
  routing = {required: true}
@@ -336,8 +317,7 @@ module Searchkick
336
317
  dynamic_fields["{name}"].merge(fields: dynamic_fields.except("{name}"))
337
318
  end
338
319
 
339
- # TODO make dynamic
340
- all_enabled = true
320
+ all_enabled = !options[:searchable] || options[:searchable].to_a.include?("_all")
341
321
 
342
322
  mappings = {
343
323
  _default_: {
@@ -1,8 +1,13 @@
1
1
  module Searchkick
2
- module Reindex; end # legacy for Searchjoy
3
-
4
2
  module Model
5
- def searchkick(options = {})
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
+
6
11
  raise "Only call searchkick once per model" if respond_to?(:searchkick_index)
7
12
 
8
13
  Searchkick.models << self
@@ -20,7 +25,7 @@ module Searchkick
20
25
  [options[:index_prefix], model_name.plural, Searchkick.env].compact.join("_")
21
26
 
22
27
  class << self
23
- def searchkick_search(term = nil, options = {}, &block)
28
+ def searchkick_search(term = "*", **options, &block)
24
29
  searchkick_index.search_model(self, term, options, &block)
25
30
  end
26
31
  alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
@@ -43,51 +48,32 @@ module Searchkick
43
48
  class_variable_get(:@@searchkick_callbacks) && Searchkick.callbacks?
44
49
  end
45
50
 
46
- def searchkick_reindex(method_name = nil, **options)
51
+ def searchkick_reindex(method_name = nil, full: false, **options)
52
+ scoped = (respond_to?(:current_scope) && respond_to?(:default_scoped) && current_scope && current_scope.to_sql != default_scoped.to_sql) ||
53
+ (respond_to?(:queryable) && queryable != unscoped.with_default_scope)
54
+
55
+ refresh = options.fetch(:refresh, !scoped)
56
+
47
57
  if method_name
58
+ # update
48
59
  searchkick_index.import_scope(searchkick_klass, method_name: method_name)
49
- searchkick_index.refresh
50
- true
60
+ searchkick_index.refresh if refresh
61
+ elsif scoped && !full
62
+ # reindex association
63
+ searchkick_index.import_scope(searchkick_klass)
64
+ searchkick_index.refresh if refresh
51
65
  else
52
- unless options[:accept_danger]
53
- if (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
- raise Searchkick::DangerousOperation, "Only call reindex on models, not relations. Pass `accept_danger: true` if this is your intention."
56
- end
57
- end
66
+ # full reindex
58
67
  searchkick_index.reindex_scope(searchkick_klass, options)
59
68
  end
69
+ true
60
70
  end
61
71
  alias_method :reindex, :searchkick_reindex unless method_defined?(:reindex)
62
72
 
63
- def searchkick_partial_reindex(method_name)
64
- searchkick_reindex(method_name)
65
- end
66
- alias_method :partial_reindex, :searchkick_partial_reindex unless method_defined?(:partial_reindex)
67
-
68
- def clean_indices
69
- searchkick_index.clean_indices
70
- end
71
-
72
- def searchkick_import(options = {})
73
- (options[:index] || searchkick_index).import_scope(searchkick_klass)
74
- end
75
-
76
- def searchkick_create_index
77
- searchkick_index.create_index
78
- end
79
-
80
73
  def searchkick_index_options
81
74
  searchkick_index.index_options
82
75
  end
83
-
84
- def searchkick_debug
85
- warn "Use debug option with search method instead"
86
-
87
- nil # do not return anything, as this is strictly used for manual debugging
88
- end
89
76
  end
90
- extend Searchkick::Reindex # legacy for Searchjoy
91
77
 
92
78
  callback_name = callbacks == :async ? :reindex_async : :reindex
93
79
  if respond_to?(:after_commit)
@@ -97,24 +83,19 @@ module Searchkick
97
83
  after_destroy callback_name, if: proc { self.class.search_callbacks? }
98
84
  end
99
85
 
100
- def reindex(method_name = nil, **options)
86
+ def reindex(method_name = nil, refresh: false)
101
87
  if method_name
102
88
  self.class.searchkick_index.bulk_update([self], method_name)
103
89
  else
104
90
  self.class.searchkick_index.reindex_record(self)
105
91
  end
106
- self.class.searchkick_index.refresh if options[:refresh]
92
+ self.class.searchkick_index.refresh if refresh
107
93
  end unless method_defined?(:reindex)
108
94
 
109
95
  def reindex_async
110
96
  self.class.searchkick_index.reindex_record_async(self)
111
97
  end unless method_defined?(:reindex_async)
112
98
 
113
- def partial_reindex(method_name)
114
- reindex(method_name, refresh: true)
115
- true
116
- end unless method_defined?(:partial_reindex)
117
-
118
99
  def similar(options = {})
119
100
  self.class.searchkick_index.similar_record(self, options)
120
101
  end unless method_defined?(:similar)