searchkick 3.0.3 → 3.1.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
  SHA256:
3
- metadata.gz: eb9e19c9a72da12faf603c3fddbdd2fac429058a0774553c0afb874f0a1bc0de
4
- data.tar.gz: a80890252fd573c07fa1f63b5cdbbaaec7dd1848ad0941e09160e973a0ad8ea2
3
+ metadata.gz: 2353bcf562a8f814430f473f0356e65cc8275c5bb21267fc46b96004c8edd27c
4
+ data.tar.gz: 5c570d53bc6d728a183de8ffb22056909ac748adebc6b436d2b8de785c3965fd
5
5
  SHA512:
6
- metadata.gz: 1faf5e1622ec798d6ffca925d7831c55265973d8e58260a7dd8f98b249bd20cb9a3a903c9a12a870dfcd4eb723caf21459a259bd197e84d99771fed96a2d65ed
7
- data.tar.gz: 796a19163cbc93d9fdbd235d29bbd86beedc3c583ea214c91406b6252f9d778d4f8ee710ae3ad1f1f85d91a7ab30571c3a01567ce8486a042d8e8d4ce3a63635
6
+ metadata.gz: c0aa2359ff46b280d0cc87fe57cf809760de414e3e40d7f5cb3c33cd43124ef3ad7aba824aa87f43390a188983ae2c1dfecba141ddaa19ab27a1cb50f6368447
7
+ data.tar.gz: bbf49af1a70667bdfa24ebee0227b0fa2b8d1738cfcbb860680d597a245d6860b1e5b56b978fc6b4aa9aa96e41bd20392e6e99116b7e69ff4a233a5480003709
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 3.1.0
2
+
3
+ - Added `:inline` as alias for `true` for `callbacks` and `mode` options
4
+ - Friendlier error message for bad mapping with partial matches
5
+ - Warn when records in search index do not exist in database
6
+ - Easier merging for `merge_mapping`
7
+ - Fixed `with_hit` and `with_highlights` when records in search index do not exist in database
8
+ - Fixed error with highlights and match all
9
+
1
10
  ## 3.0.3
2
11
 
3
12
  - Added support for pagination with `body` option
data/README.md CHANGED
@@ -507,7 +507,7 @@ For large data sets, try [parallel reindexing](#parallel-reindexing).
507
507
 
508
508
  There are four strategies for keeping the index synced with your database.
509
509
 
510
- 1. Immediate (default)
510
+ 1. Inline (default)
511
511
 
512
512
  Anytime a record is inserted, updated, or deleted
513
513
 
@@ -1790,12 +1790,12 @@ describe Product, search: true do
1790
1790
  end
1791
1791
  ```
1792
1792
 
1793
- ### Factory Girl
1793
+ ### Factory Bot
1794
1794
 
1795
1795
  Use a trait and an after `create` hook for each indexed model:
1796
1796
 
1797
1797
  ```ruby
1798
- FactoryGirl.define do
1798
+ FactoryBot.define do
1799
1799
  factory :product do
1800
1800
  # ...
1801
1801
 
@@ -1810,7 +1810,7 @@ FactoryGirl.define do
1810
1810
  end
1811
1811
 
1812
1812
  # use it
1813
- FactoryGirl.create(:product, :some_trait, :reindex, some_attribute: "foo")
1813
+ FactoryBot.create(:product, :some_trait, :reindex, some_attribute: "foo")
1814
1814
  ```
1815
1815
 
1816
1816
  ### Parallel Tests
data/lib/searchkick.rb CHANGED
@@ -26,13 +26,6 @@ rescue LoadError
26
26
  end
27
27
  require "searchkick/tasks" if defined?(Rake)
28
28
 
29
- begin
30
- require "rake"
31
- rescue LoadError
32
- # do nothing
33
- end
34
- require "searchkick/tasks" if defined?(Rake)
35
-
36
29
  # background jobs
37
30
  begin
38
31
  require "active_job"
@@ -159,10 +159,9 @@ module Searchkick
159
159
  .keep_if { |k, _| !options[:fields] || options[:fields].map(&:to_s).include?(k) }
160
160
  .values.compact.join(" ")
161
161
 
162
- # TODO deep merge method
163
162
  options[:where] ||= {}
164
163
  options[:where][:_id] ||= {}
165
- options[:where][:_id][:not] = record.id.to_s
164
+ options[:where][:_id][:not] = Array(options[:where][:_id][:not]) + [record.id.to_s]
166
165
  options[:per_page] ||= 10
167
166
  options[:similar] = true
168
167
 
@@ -225,7 +225,7 @@ module Searchkick
225
225
  }
226
226
  end
227
227
 
228
- settings.deep_merge!(options[:settings] || {})
228
+ settings = settings.symbolize_keys.deep_merge((options[:settings] || {}).symbolize_keys)
229
229
 
230
230
  # synonyms
231
231
  synonyms = options[:synonyms] || []
@@ -311,10 +311,10 @@ module Searchkick
311
311
 
312
312
  if !options[:searchable] || mapping_options[:searchable].include?(field)
313
313
  if word
314
- fields["analyzed"] = analyzed_field_options
314
+ fields[:analyzed] = analyzed_field_options
315
315
 
316
316
  if mapping_options[:highlight].include?(field)
317
- fields["analyzed"][:term_vector] = "with_positions_offsets"
317
+ fields[:analyzed][:term_vector] = "with_positions_offsets"
318
318
  end
319
319
  end
320
320
 
@@ -373,7 +373,7 @@ module Searchkick
373
373
  end
374
374
 
375
375
  if word
376
- dynamic_fields["analyzed"] = analyzed_field_options
376
+ dynamic_fields[:analyzed] = analyzed_field_options
377
377
  end
378
378
  end
379
379
 
@@ -402,7 +402,7 @@ module Searchkick
402
402
  mappings[index_type][:_all] = all_enabled ? analyzed_field_options : {enabled: false}
403
403
  end
404
404
 
405
- mappings = mappings.deep_merge(options[:mappings] || {})
405
+ mappings = mappings.symbolize_keys.deep_merge((options[:mappings] || {}).symbolize_keys)
406
406
  end
407
407
 
408
408
  {
@@ -16,8 +16,8 @@ module Searchkick
16
16
 
17
17
  options[:_type] ||= -> { searchkick_index.klass_document_type(self, true) }
18
18
 
19
- callbacks = options.key?(:callbacks) ? options[:callbacks] : true
20
- unless [true, false, :async, :queue].include?(callbacks)
19
+ callbacks = options.key?(:callbacks) ? options[:callbacks] : :inline
20
+ unless [:inline, true, false, :async, :queue].include?(callbacks)
21
21
  raise ArgumentError, "Invalid value for callbacks"
22
22
  end
23
23
 
@@ -111,6 +111,7 @@ module Searchkick
111
111
  model_includes: options[:model_includes],
112
112
  json: !@json.nil?,
113
113
  match_suffix: @match_suffix,
114
+ highlight: options[:highlight],
114
115
  highlighted_fields: @highlighted_fields || [],
115
116
  misspellings: @misspellings,
116
117
  term: term,
@@ -188,7 +189,7 @@ module Searchkick
188
189
  )
189
190
 
190
191
  raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 5 or greater"
191
- elsif e.message.include?("[multi_match] analyzer [searchkick_search] not found")
192
+ elsif e.message =~ /analyzer \[searchkick_.+\] not found/
192
193
  raise InvalidQueryError, "Bad mapping - run #{reindex_command}"
193
194
  else
194
195
  raise InvalidQueryError, e.message
@@ -583,7 +584,8 @@ module Searchkick
583
584
  unless attributes[:origin]
584
585
  raise ArgumentError, "boost_by_distance requires :origin"
585
586
  end
586
- function_params = attributes.select { |k, _| [:origin, :scale, :offset, :decay].include?(k) }
587
+
588
+ function_params = attributes.except(:factor, :function)
587
589
  function_params[:origin] = location_value(function_params[:origin])
588
590
  custom_filters << {
589
591
  weight: attributes[:factor] || 1,
@@ -8,7 +8,7 @@ module Searchkick
8
8
  end
9
9
 
10
10
  def reindex(method_name = nil, refresh: false, mode: nil)
11
- unless [true, nil, :async, :queue].include?(mode)
11
+ unless [:inline, true, nil, :async, :queue].include?(mode)
12
12
  raise ArgumentError, "Invalid value for mode"
13
13
  end
14
14
 
@@ -31,7 +31,7 @@ module Searchkick
31
31
  record.id.to_s,
32
32
  method_name ? method_name.to_s : nil
33
33
  )
34
- else # bulk, true
34
+ else # bulk, inline/true/nil
35
35
  reindex_record(method_name)
36
36
 
37
37
  index.refresh if refresh
@@ -30,7 +30,7 @@ module Searchkick
30
30
  record.id = id
31
31
  end
32
32
 
33
- RecordIndexer.new(record).reindex(method_name, mode: true)
33
+ RecordIndexer.new(record).reindex(method_name, mode: :inline)
34
34
  end
35
35
  end
36
36
  end
@@ -16,7 +16,11 @@ module Searchkick
16
16
  end
17
17
 
18
18
  def results
19
- @results ||= begin
19
+ @results ||= with_hit.map(&:first)
20
+ end
21
+
22
+ def with_hit
23
+ @with_hit ||= begin
20
24
  if options[:load]
21
25
  # results can have different types
22
26
  results = {}
@@ -26,19 +30,31 @@ module Searchkick
26
30
  results[type] = results_query(klass, grouped_hits).to_a.index_by { |r| r.id.to_s }
27
31
  end
28
32
 
33
+ missing_ids = []
34
+
29
35
  # sort
30
- hits.map do |hit|
31
- result = results[hit["_type"]][hit["_id"].to_s]
32
- if result && !(options[:load].is_a?(Hash) && options[:load][:dumpable])
33
- if hit["highlight"] && !result.respond_to?(:search_highlights)
34
- highlights = Hash[hit["highlight"].map { |k, v| [(options[:json] ? k : k.sub(/\.#{@options[:match_suffix]}\z/, "")).to_sym, v.first] }]
35
- result.define_singleton_method(:search_highlights) do
36
- highlights
36
+ results =
37
+ hits.map do |hit|
38
+ result = results[hit["_type"]][hit["_id"].to_s]
39
+ if result && !(options[:load].is_a?(Hash) && options[:load][:dumpable])
40
+ if (hit["highlight"] || options[:highlight]) && !result.respond_to?(:search_highlights)
41
+ highlights = hit_highlights(hit)
42
+ result.define_singleton_method(:search_highlights) do
43
+ highlights
44
+ end
37
45
  end
38
46
  end
47
+ [result, hit]
48
+ end.select do |result, hit|
49
+ missing_ids << hit["_id"] unless result
50
+ result
39
51
  end
40
- result
41
- end.compact
52
+
53
+ if missing_ids.any?
54
+ warn "[searchkick] WARNING: Records in search index do not exist in database: #{missing_ids.join(", ")}"
55
+ end
56
+
57
+ results
42
58
  else
43
59
  hits.map do |hit|
44
60
  result =
@@ -50,15 +66,15 @@ module Searchkick
50
66
  hit
51
67
  end
52
68
 
53
- if hit["highlight"]
54
- highlight = Hash[hit["highlight"].map { |k, v| [base_field(k), v.first] }]
69
+ if hit["highlight"] || options[:highlight]
70
+ highlight = Hash[hit["highlight"].to_a.map { |k, v| [base_field(k), v.first] }]
55
71
  options[:highlighted_fields].map { |k| base_field(k) }.each do |k|
56
72
  result["highlighted_#{k}"] ||= (highlight[k] || result[k])
57
73
  end
58
74
  end
59
75
 
60
76
  result["id"] ||= result["_id"] # needed for legacy reasons
61
- HashWrapper.new(result)
77
+ [HashWrapper.new(result), hit]
62
78
  end
63
79
  end
64
80
  end
@@ -172,18 +188,16 @@ module Searchkick
172
188
  end
173
189
  end
174
190
 
175
- def with_hit
176
- results.zip(hits)
177
- end
178
-
179
191
  def highlights(multiple: false)
180
192
  hits.map do |hit|
181
- Hash[hit["highlight"].map { |k, v| [(options[:json] ? k : k.sub(/\.#{@options[:match_suffix]}\z/, "")).to_sym, multiple ? v : v.first] }]
193
+ hit_highlights(hit, multiple: multiple)
182
194
  end
183
195
  end
184
196
 
185
197
  def with_highlights(multiple: false)
186
- results.zip(highlights(multiple: multiple))
198
+ with_hit do |result, hit|
199
+ [result, hit_highlights(hit, multiple: multiple)]
200
+ end
187
201
  end
188
202
 
189
203
  def misspellings?
@@ -231,5 +245,13 @@ module Searchkick
231
245
  def base_field(k)
232
246
  k.sub(/\.(analyzed|word_start|word_middle|word_end|text_start|text_middle|text_end|exact)\z/, "")
233
247
  end
248
+
249
+ def hit_highlights(hit, multiple: false)
250
+ if hit["highlight"]
251
+ Hash[hit["highlight"].map { |k, v| [(options[:json] ? k : k.sub(/\.#{@options[:match_suffix]}\z/, "")).to_sym, multiple ? v : v.first] }]
252
+ else
253
+ {}
254
+ end
255
+ end
234
256
  end
235
257
  end
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "3.0.3"
2
+ VERSION = "3.1.0"
3
3
  end
@@ -91,4 +91,19 @@ class HighlightTest < Minitest::Test
91
91
  store_names ["Two Door Cinema Club"]
92
92
  assert_equal "Two Door <em>Cinema</em> Club", Product.search("cinema", highlight: true).first.search_highlights[:name]
93
93
  end
94
+
95
+ def test_match_all
96
+ store_names ["Two Door Cinema Club"]
97
+ assert_nil Product.search("*", highlight: true).highlights.first[:name]
98
+ end
99
+
100
+ def test_match_all_load_false
101
+ store_names ["Two Door Cinema Club"]
102
+ assert_nil Product.search("*", highlight: true, load: false).highlights.first[:name]
103
+ end
104
+
105
+ def test_match_all_search_highlights
106
+ store_names ["Two Door Cinema Club"]
107
+ assert_nil Product.search("*", highlight: true).first.search_highlights[:name]
108
+ end
94
109
  end
data/test/index_test.rb CHANGED
@@ -85,7 +85,9 @@ class IndexTest < Minitest::Test
85
85
  def test_record_not_found
86
86
  store_names ["Product A", "Product B"]
87
87
  Product.where(name: "Product A").delete_all
88
- assert_search "product", ["Product B"]
88
+ assert_output nil, /\[searchkick\] WARNING: Records in search index do not exist in database/ do
89
+ assert_search "product", ["Product B"]
90
+ end
89
91
  ensure
90
92
  Product.reindex
91
93
  end
@@ -93,7 +95,8 @@ class IndexTest < Minitest::Test
93
95
  def test_bad_mapping
94
96
  Product.searchkick_index.delete
95
97
  store_names ["Product A"]
96
- assert_raises(Searchkick::InvalidQueryError) { Product.search "test" }
98
+ error = assert_raises(Searchkick::InvalidQueryError) { Product.search "test" }
99
+ assert_equal "Bad mapping - run Product.reindex", error.message
97
100
  ensure
98
101
  Product.reindex
99
102
  end
data/test/routing_test.rb CHANGED
@@ -8,7 +8,7 @@ class RoutingTest < Minitest::Test
8
8
 
9
9
  def test_routing_mappings
10
10
  index_options = Store.searchkick_index.index_options
11
- assert_equal index_options[:mappings]["store"][:_routing], required: true
11
+ assert_equal index_options[:mappings][:store][:_routing], required: true
12
12
  end
13
13
 
14
14
  def test_routing_correct_node
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: 3.0.3
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-23 00:00:00.000000000 Z
11
+ date: 2018-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel