searchkick 3.0.3 → 3.1.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
  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