searchkick 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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)