searchkick 4.2.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,8 +5,8 @@ module Searchkick
5
5
 
6
6
  unknown_keywords = options.keys - [:_all, :_type, :batch_size, :callbacks, :case_sensitive, :conversions, :deep_paging, :default_fields,
7
7
  :filterable, :geo_shape, :highlight, :ignore_above, :index_name, :index_prefix, :inheritance, :language,
8
- :locations, :mappings, :match, :merge_mappings, :routing, :searchable, :settings, :similarity,
9
- :special_characters, :stem, :stem_conversions, :suggest, :synonyms, :text_end,
8
+ :locations, :mappings, :match, :merge_mappings, :routing, :searchable, :search_synonyms, :settings, :similarity,
9
+ :special_characters, :stem, :stem_conversions, :stem_exclusion, :stemmer_override, :suggest, :synonyms, :text_end,
10
10
  :text_middle, :text_start, :word, :wordnet, :word_end, :word_middle, :word_start]
11
11
  raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
12
12
 
@@ -41,7 +41,10 @@ module Searchkick
41
41
 
42
42
  class << self
43
43
  def searchkick_search(term = "*", **options, &block)
44
- Searchkick.search(term, {model: self}.merge(options), &block)
44
+ # TODO throw error in next major version
45
+ Searchkick.warn("calling search on a relation is deprecated") if Searchkick.relation?(self)
46
+
47
+ Searchkick.search(term, model: self, **options, &block)
45
48
  end
46
49
  alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
47
50
 
@@ -54,10 +57,11 @@ module Searchkick
54
57
  alias_method :search_index, :searchkick_index unless method_defined?(:search_index)
55
58
 
56
59
  def searchkick_reindex(method_name = nil, **options)
57
- scoped = (respond_to?(:current_scope) && respond_to?(:default_scoped) && current_scope && current_scope.to_sql != default_scoped.to_sql) ||
60
+ # TODO relation = Searchkick.relation?(self)
61
+ relation = (respond_to?(:current_scope) && respond_to?(:default_scoped) && current_scope && current_scope.to_sql != default_scoped.to_sql) ||
58
62
  (respond_to?(:queryable) && queryable != unscoped.with_default_scope)
59
63
 
60
- searchkick_index.reindex(searchkick_klass, method_name, scoped: scoped, **options)
64
+ searchkick_index.reindex(searchkick_klass, method_name, scoped: relation, **options)
61
65
  end
62
66
  alias_method :reindex, :searchkick_reindex unless method_defined?(:reindex)
63
67
 
@@ -79,8 +83,9 @@ module Searchkick
79
83
  RecordIndexer.new(self).reindex(method_name, **options)
80
84
  end unless method_defined?(:reindex)
81
85
 
86
+ # TODO switch to keyword arguments
82
87
  def similar(options = {})
83
- self.class.searchkick_index.similar_record(self, options)
88
+ self.class.searchkick_index.similar_record(self, **options)
84
89
  end unless method_defined?(:similar)
85
90
 
86
91
  def search_data
@@ -350,7 +350,7 @@ module Searchkick
350
350
  field_misspellings = misspellings && (!misspellings_fields || misspellings_fields.include?(base_field(field)))
351
351
 
352
352
  if field == "_all" || field.end_with?(".analyzed")
353
- shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || field_misspellings == false
353
+ shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || field_misspellings == false || (!below73? && !track_total_hits?)
354
354
  qs << shared_options.merge(analyzer: "searchkick_search")
355
355
 
356
356
  # searchkick_search and searchkick_search2 are the same for ukrainian
@@ -522,7 +522,7 @@ module Searchkick
522
522
  # routing
523
523
  @routing = options[:routing] if options[:routing]
524
524
 
525
- if searchkick_options[:deep_paging] && !below70?
525
+ if track_total_hits?
526
526
  payload[:track_total_hits] = true
527
527
  end
528
528
 
@@ -574,7 +574,8 @@ module Searchkick
574
574
 
575
575
  def build_query(query, filters, should, must_not, custom_filters, multiply_filters)
576
576
  if filters.any? || must_not.any? || should.any?
577
- bool = {must: query}
577
+ bool = {}
578
+ bool[:must] = query if query
578
579
  bool[:filter] = filters if filters.any? # where
579
580
  bool[:must_not] = must_not if must_not.any? # exclude
580
581
  bool[:should] = should if should.any? # conversions
@@ -871,6 +872,11 @@ module Searchkick
871
872
  end
872
873
 
873
874
  def where_filters(where)
875
+ # if where.respond_to?(:permitted?) && !where.permitted?
876
+ # # TODO check in more places
877
+ # Searchkick.warn("Passing unpermitted parameters will raise an exception in Searchkick 5")
878
+ # end
879
+
874
880
  filters = []
875
881
  (where || {}).each do |field, value|
876
882
  field = :_id if field.to_s == "id"
@@ -953,10 +959,17 @@ module Searchkick
953
959
  # % matches zero or more characters
954
960
  # _ matches one character
955
961
  # \ is escape character
956
- regex = Regexp.escape(op_value).gsub(/(?<!\\)%/, ".*").gsub(/(?<!\\)_/, ".").gsub("\\%", "%").gsub("\\_", "_")
962
+ # escape Lucene reserved characters
963
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/regexp-syntax.html#regexp-optional-operators
964
+ reserved = %w(. ? + * | { } [ ] ( ) " \\)
965
+ regex = op_value.dup
966
+ reserved.each do |v|
967
+ regex.gsub!(v, "\\" + v)
968
+ end
969
+ regex = regex.gsub(/(?<!\\)%/, ".*").gsub(/(?<!\\)_/, ".").gsub("\\%", "%").gsub("\\_", "_")
957
970
  filters << {regexp: {field => {value: regex}}}
958
971
  when :prefix
959
- filters << {prefix: {field => op_value}}
972
+ filters << {prefix: {field => {value: op_value}}}
960
973
  when :regexp # support for regexp queries without using a regexp ruby object
961
974
  filters << {regexp: {field => {value: op_value}}}
962
975
  when :not, :_not # not equal
@@ -1036,7 +1049,16 @@ module Searchkick
1036
1049
 
1037
1050
  {regexp: {field => {value: source, flags: "NONE"}}}
1038
1051
  else
1039
- {term: {field => value}}
1052
+ # TODO add this for other values
1053
+ if value.as_json.is_a?(Enumerable)
1054
+ # query will fail, but this is better
1055
+ # same message as Active Record
1056
+ # TODO make TypeError
1057
+ # raise InvalidQueryError for backward compatibility
1058
+ raise Searchkick::InvalidQueryError, "can't cast #{value.class.name}"
1059
+ end
1060
+
1061
+ {term: {field => {value: value}}}
1040
1062
  end
1041
1063
  end
1042
1064
 
@@ -1100,6 +1122,14 @@ module Searchkick
1100
1122
  k.sub(/\.(analyzed|word_start|word_middle|word_end|text_start|text_middle|text_end|exact)\z/, "")
1101
1123
  end
1102
1124
 
1125
+ def track_total_hits?
1126
+ (searchkick_options[:deep_paging] && !below70?) || body_options[:track_total_hits]
1127
+ end
1128
+
1129
+ def body_options
1130
+ options[:body_options] || {}
1131
+ end
1132
+
1103
1133
  def below61?
1104
1134
  Searchkick.server_below?("6.1.0")
1105
1135
  end
@@ -1108,6 +1138,10 @@ module Searchkick
1108
1138
  Searchkick.server_below?("7.0.0")
1109
1139
  end
1110
1140
 
1141
+ def below73?
1142
+ Searchkick.server_below?("7.3.0")
1143
+ end
1144
+
1111
1145
  def below75?
1112
1146
  Searchkick.server_below?("7.5.0")
1113
1147
  end
@@ -19,6 +19,7 @@ module Searchkick
19
19
  @results ||= with_hit.map(&:first)
20
20
  end
21
21
 
22
+ # TODO return enumerator like with_score
22
23
  def with_hit
23
24
  @with_hit ||= begin
24
25
  if options[:load]
@@ -211,12 +212,21 @@ module Searchkick
211
212
  end
212
213
  end
213
214
 
215
+ # TODO return enumerator like with_score
214
216
  def with_highlights(multiple: false)
215
217
  with_hit.map do |result, hit|
216
218
  [result, hit_highlights(hit, multiple: multiple)]
217
219
  end
218
220
  end
219
221
 
222
+ def with_score
223
+ return enum_for(:with_score) unless block_given?
224
+
225
+ with_hit.each do |result, hit|
226
+ yield result, hit["_score"]
227
+ end
228
+ end
229
+
220
230
  def misspellings?
221
231
  @options[:misspellings]
222
232
  end
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "4.2.0"
2
+ VERSION = "4.4.1"
3
3
  end
@@ -1,21 +1,22 @@
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']}"
10
- end
11
- else
12
- abort "USAGE: rake searchkick:reindex CLASS=Product"
13
- end
4
+ class_name = ENV["CLASS"]
5
+ abort "USAGE: rake searchkick:reindex CLASS=Product" unless class_name
6
+
7
+ model = class_name.safe_constantize
8
+ abort "Could not find class: #{class_name}" unless model
9
+ abort "#{class_name} is not a searchkick model" unless Searchkick.models.include?(model)
10
+
11
+ puts "Reindexing #{model.name}..."
12
+ model.reindex
13
+ puts "Reindex successful"
14
14
  end
15
15
 
16
16
  namespace :reindex do
17
17
  desc "reindex all models"
18
18
  task all: :environment do
19
+ # eager load models to populate Searchkick.models
19
20
  if Rails.respond_to?(:autoloaders) && Rails.autoloaders.zeitwerk_enabled?
20
21
  # fix for https://github.com/rails/rails/issues/37006
21
22
  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.2.0
4
+ version: 4.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-19 00:00:00.000000000 Z
11
+ date: 2020-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -52,48 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  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
55
  description:
98
56
  email: andrew@chartkick.com
99
57
  executables: []
@@ -101,7 +59,6 @@ extensions: []
101
59
  extra_rdoc_files: []
102
60
  files:
103
61
  - CHANGELOG.md
104
- - CONTRIBUTING.md
105
62
  - LICENSE.txt
106
63
  - README.md
107
64
  - lib/searchkick.rb
@@ -145,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
102
  - !ruby/object:Gem::Version
146
103
  version: '0'
147
104
  requirements: []
148
- rubygems_version: 3.0.3
105
+ rubygems_version: 3.1.2
149
106
  signing_key:
150
107
  specification_version: 4
151
108
  summary: Intelligent search made easy with Rails and Elasticsearch
@@ -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/).
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.