searchkick 4.2.0 → 4.4.1

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.
@@ -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.