searchkick 2.3.0 → 2.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92b9786d3ffe96d4accab83ae00accbaa5b2ffb3
4
- data.tar.gz: 9b60f5ab8f709d9138cbe4823b14d29a06e4aa77
3
+ metadata.gz: 75ff5cc71f668744faa82225c281a7a636d2a774
4
+ data.tar.gz: 3acfb221ce185cd58e4e56907a4b175556c1da53
5
5
  SHA512:
6
- metadata.gz: f8e3324376e90052bbf854fe3d04200857d06ac6cde06bea08ee2fe573462de243d558ed1d0592d92b7794d5c1fd761f396812c4ea09ff5fce1e30cdac2119a8
7
- data.tar.gz: c5bc1d098f59c17945af081727ef92cd7d297cfebe3f0acd1c5401dd95bca71a208c10208b07f7d473e9e387fc37f4643c8dc9b373dec2006c4507f30c598808
6
+ metadata.gz: c329befd3fdad5beb444e0db1c6dabc138ace231cb66f6f8e6abe880bd06fb949aca2ab59fbd135e2516c052d753e743aadac8269a4904fcfc968d7bd6ce626c
7
+ data.tar.gz: 9943486403fc4a18e6fbf1a2ba3dcda15a85517afaacd8717ffa88dd2d47badb803c85eae9b0aac80b075e6afba2896404ffaeddb22d69c4546a0a8a1e579c4a
@@ -1,3 +1,10 @@
1
+ ## 2.3.1
2
+
3
+ - Added support for `reindex(async: true)` for non-numeric primary keys
4
+ - Added `conversions_term` option
5
+ - Added support for passing fields to `suggest` option
6
+ - Fixed `page_view_entries` for Kaminari
7
+
1
8
  ## 2.3.0
2
9
 
3
10
  - Fixed analyzer on dynamically mapped fields
data/Gemfile CHANGED
@@ -10,3 +10,7 @@ gem "typhoeus"
10
10
  gem "activejob"
11
11
  gem "redis"
12
12
  gem "connection_pool"
13
+
14
+ # kaminari
15
+ gem "actionpack"
16
+ gem "kaminari"
data/README.md CHANGED
@@ -629,7 +629,7 @@ end
629
629
  Reindex and search with:
630
630
 
631
631
  ```ruby
632
- Movie.search "jurassic pa", match: :word_start
632
+ Movie.search "jurassic pa", fields: [:title], match: :word_start
633
633
  ```
634
634
 
635
635
  Typically, you want to use a JavaScript library like [typeahead.js](http://twitter.github.io/typeahead.js/) or [jQuery UI](http://jqueryui.com/autocomplete/).
@@ -1177,14 +1177,16 @@ end
1177
1177
 
1178
1178
  ### Filterable Fields
1179
1179
 
1180
- By default, all fields are filterable (can be used in `where` option). Speed up indexing and reduce index size by only making some fields filterable.
1180
+ By default, all string fields are filterable (can be used in `where` option). Speed up indexing and reduce index size by only making some fields filterable.
1181
1181
 
1182
1182
  ```ruby
1183
1183
  class Product < ActiveRecord::Base
1184
- searchkick filterable: [:store_id]
1184
+ searchkick filterable: [:brand]
1185
1185
  end
1186
1186
  ```
1187
1187
 
1188
+ **Note:** Non-string fields will always be filterable and should not be passed to this option.
1189
+
1188
1190
  ### Parallel Reindexing
1189
1191
 
1190
1192
  For large data sets, you can use background jobs to parallelize reindexing.
@@ -1563,6 +1565,12 @@ class Product < ActiveRecord::Base
1563
1565
  end
1564
1566
  ```
1565
1567
 
1568
+ Use a different term for boosting by conversions
1569
+
1570
+ ```ruby
1571
+ Product.search("banana", conversions_term: "organic banana")
1572
+ ```
1573
+
1566
1574
  Multiple conversion fields
1567
1575
 
1568
1576
  ```ruby
@@ -420,14 +420,25 @@ module Searchkick
420
420
  if scope.respond_to?(:primary_key)
421
421
  # TODO expire Redis key
422
422
  primary_key = scope.primary_key
423
- starting_id = scope.minimum(primary_key) || 0
424
- max_id = scope.maximum(primary_key) || 0
425
- batches_count = ((max_id - starting_id + 1) / batch_size.to_f).ceil
426
-
427
- batches_count.times do |i|
428
- batch_id = i + 1
429
- min_id = starting_id + (i * batch_size)
430
- bulk_reindex_job scope, batch_id, min_id: min_id, max_id: min_id + batch_size - 1
423
+
424
+ starting_id = scope.minimum(primary_key)
425
+ if starting_id.nil?
426
+ # no records, do nothing
427
+ elsif starting_id.is_a?(Numeric)
428
+ max_id = scope.maximum(primary_key)
429
+ batches_count = ((max_id - starting_id + 1) / batch_size.to_f).ceil
430
+
431
+ batches_count.times do |i|
432
+ batch_id = i + 1
433
+ min_id = starting_id + (i * batch_size)
434
+ bulk_reindex_job scope, batch_id, min_id: min_id, max_id: min_id + batch_size - 1
435
+ end
436
+ else
437
+ scope.find_in_batches(batch_size: batch_size).each_with_index do |batch, i|
438
+ batch_id = i + 1
439
+
440
+ bulk_reindex_job scope, batch_id, record_ids: batch.map { |record| record.id.to_s }
441
+ end
431
442
  end
432
443
  else
433
444
  batch_id = 1
@@ -16,7 +16,7 @@ module Searchkick
16
16
 
17
17
  def initialize(klass, term = "*", **options)
18
18
  unknown_keywords = options.keys - [:aggs, :body, :body_options, :boost,
19
- :boost_by, :boost_by_distance, :boost_where, :conversions, :debug, :emoji, :exclude, :execute, :explain,
19
+ :boost_by, :boost_by_distance, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :execute, :explain,
20
20
  :fields, :highlight, :includes, :index_name, :indices_boost, :limit, :load,
21
21
  :match, :misspellings, :offset, :operator, :order, :padding, :page, :per_page, :profile,
22
22
  :request_params, :routing, :select, :similar, :smart_aggs, :suggest, :track, :type, :where]
@@ -381,7 +381,7 @@ module Searchkick
381
381
  boost_mode: "replace",
382
382
  query: {
383
383
  match: {
384
- "#{conversions_field}.query" => term
384
+ "#{conversions_field}.query" => options[:conversions_term] || term
385
385
  }
386
386
  }
387
387
  }.merge(script_score)
@@ -447,7 +447,7 @@ module Searchkick
447
447
  set_aggregations(payload) if options[:aggs]
448
448
 
449
449
  # suggestions
450
- set_suggestions(payload) if options[:suggest]
450
+ set_suggestions(payload, options[:suggest]) if options[:suggest]
451
451
 
452
452
  # highlight
453
453
  set_highlights(payload, fields) if options[:highlight]
@@ -575,12 +575,18 @@ module Searchkick
575
575
  payload[:indices_boost] = indices_boost
576
576
  end
577
577
 
578
- def set_suggestions(payload)
579
- suggest_fields = (searchkick_options[:suggest] || []).map(&:to_s)
578
+ def set_suggestions(payload, suggest)
579
+ suggest_fields = nil
580
580
 
581
- # intersection
582
- if options[:fields]
583
- suggest_fields &= options[:fields].map { |v| (v.is_a?(Hash) ? v.keys.first : v).to_s.split("^", 2).first }
581
+ if suggest.is_a?(Array)
582
+ suggest_fields = suggest
583
+ else
584
+ suggest_fields = (searchkick_options[:suggest] || []).map(&:to_s)
585
+
586
+ # intersection
587
+ if options[:fields]
588
+ suggest_fields &= options[:fields].map { |v| (v.is_a?(Hash) ? v.keys.first : v).to_s.split("^", 2).first }
589
+ end
584
590
  end
585
591
 
586
592
  if suggest_fields.any?
@@ -592,6 +598,8 @@ module Searchkick
592
598
  }
593
599
  }
594
600
  end
601
+ else
602
+ raise ArgumentError, "Must pass fields to suggest option"
595
603
  end
596
604
  end
597
605
 
@@ -127,8 +127,14 @@ module Searchkick
127
127
  klass.model_name
128
128
  end
129
129
 
130
- def entry_name
131
- model_name.human.downcase
130
+ def entry_name(options = {})
131
+ if options.empty?
132
+ # backward compatibility
133
+ model_name.human.downcase
134
+ else
135
+ default = options[:count] == 1 ? model_name.human : model_name.human.pluralize
136
+ model_name.human(options.reverse_merge(default: default))
137
+ end
132
138
  end
133
139
 
134
140
  def total_count
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "2.3.0"
2
+ VERSION = "2.3.1"
3
3
  end
@@ -28,6 +28,18 @@ class BoostTest < Minitest::Test
28
28
  assert_order "speaker", ["Speaker A", "Speaker B", "Speaker C"], {conversions: "conversions_b"}, Speaker
29
29
  end
30
30
 
31
+ def test_multiple_conversions_with_boost_term
32
+ store [
33
+ {name: "Speaker A", conversions_a: {"speaker" => 4, "speaker_1" => 1}},
34
+ {name: "Speaker B", conversions_a: {"speaker" => 3, "speaker_1" => 2}},
35
+ {name: "Speaker C", conversions_a: {"speaker" => 2, "speaker_1" => 3}},
36
+ {name: "Speaker D", conversions_a: {"speaker" => 1, "speaker_1" => 4}}
37
+ ], Speaker
38
+
39
+ assert_order "speaker", ["Speaker A", "Speaker B", "Speaker C", "Speaker D"], {conversions: "conversions_a"}, Speaker
40
+ assert_order "speaker", ["Speaker D", "Speaker C", "Speaker B", "Speaker A"], {conversions: "conversions_a", conversions_term: "speaker_1"}, Speaker
41
+ end
42
+
31
43
  def test_conversions_stemmed
32
44
  store [
33
45
  {name: "Tomato A", conversions: {"tomato" => 1, "tomatos" => 1, "Tomatoes" => 1}},
@@ -6,3 +6,7 @@ gemspec path: "../../"
6
6
  gem "mongoid", "~> 6.0.0"
7
7
  gem "activejob"
8
8
  gem "redis"
9
+
10
+ # kaminari
11
+ gem "actionpack"
12
+ gem "kaminari"
@@ -132,6 +132,11 @@ class IndexTest < Minitest::Test
132
132
  assert_search "*", [], where: {alt_description: "Hello"}
133
133
  end
134
134
 
135
+ def test_filterable_non_string
136
+ store [{name: "Product A", store_id: 1}]
137
+ assert_search "*", ["Product A"], where: {store_id: 1}
138
+ end
139
+
135
140
  def test_large_value
136
141
  skip if nobrainer?
137
142
  large_value = 1000.times.map { "hello" }.join(" ")
@@ -50,4 +50,21 @@ class PaginationTest < Minitest::Test
50
50
  assert_equal 1, products.current_page
51
51
  assert products.first_page?
52
52
  end
53
+
54
+ def test_kaminari
55
+ skip unless defined?(Kaminari)
56
+
57
+ require "action_view"
58
+
59
+ I18n.load_path = Dir["test/support/kaminari.yml"]
60
+ I18n.backend.load_translations
61
+
62
+ view = ActionView::Base.new
63
+
64
+ store_names ["Product A"]
65
+ assert_equal "Displaying <b>1</b> product", view.page_entries_info(Product.search("product"))
66
+
67
+ store_names ["Product B"]
68
+ assert_equal "Displaying <b>all 2</b> products", view.page_entries_info(Product.search("product"))
69
+ end
53
70
  end
@@ -39,6 +39,18 @@ class ReindexTest < Minitest::Test
39
39
  assert_search "product", ["Product A"]
40
40
  end
41
41
 
42
+ def test_async_non_integer_pk
43
+ skip if !defined?(ActiveJob)
44
+
45
+ Sku.create(id: SecureRandom.hex, name: "Test")
46
+ reindex = Sku.reindex(async: true)
47
+ assert_search "sku", [], conversions: false
48
+
49
+ index = Searchkick::Index.new(reindex[:index_name])
50
+ index.refresh
51
+ assert_equal 1, index.total_docs
52
+ end
53
+
42
54
  def test_refresh_interval
43
55
  reindex = Product.reindex(refresh_interval: "30s", async: true, import: false)
44
56
  index = Searchkick::Index.new(reindex[:index_name])
@@ -67,6 +67,16 @@ class SuggestTest < Minitest::Test
67
67
  assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [{"name^2" => :word_start}]
68
68
  end
69
69
 
70
+ def test_multiple_models
71
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
72
+ assert_equal "how big is a tiger shark", Searchkick.search("How Big is a Tigre Shar", suggest: [:name]).suggestions.first
73
+ end
74
+
75
+ def test_multiple_models_no_fields
76
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
77
+ assert_raises(ArgumentError) { Searchkick.search("How Big is a Tigre Shar", suggest: true) }
78
+ end
79
+
70
80
  protected
71
81
 
72
82
  def assert_suggest(term, expected, options = {})
@@ -0,0 +1,21 @@
1
+ en:
2
+ views:
3
+ pagination:
4
+ first: "&laquo; First"
5
+ last: "Last &raquo;"
6
+ previous: "&lsaquo; Prev"
7
+ next: "Next &rsaquo;"
8
+ truncate: "&hellip;"
9
+ helpers:
10
+ page_entries_info:
11
+ entry:
12
+ zero: "entries"
13
+ one: "entry"
14
+ other: "entries"
15
+ one_page:
16
+ display_entries:
17
+ zero: "No %{entry_name} found"
18
+ one: "Displaying <b>1</b> %{entry_name}"
19
+ other: "Displaying <b>all %{count}</b> %{entry_name}"
20
+ more_pages:
21
+ display_entries: "Displaying %{entry_name} <b>%{first}&nbsp;-&nbsp;%{last}</b> of <b>%{total}</b> in total"
@@ -111,6 +111,12 @@ if defined?(Mongoid)
111
111
 
112
112
  class Cat < Animal
113
113
  end
114
+
115
+ class Sku
116
+ include Mongoid::Document
117
+
118
+ field :name
119
+ end
114
120
  elsif defined?(NoBrainer)
115
121
  NoBrainer.configure do |config|
116
122
  config.app_name = :searchkick
@@ -171,6 +177,13 @@ elsif defined?(NoBrainer)
171
177
 
172
178
  class Cat < Animal
173
179
  end
180
+
181
+ class Sku
182
+ include NoBrainer::Document
183
+
184
+ field :id, type: String
185
+ field :name, type: String
186
+ end
174
187
  elsif defined?(Cequel)
175
188
  cequel =
176
189
  Cequel.connect(
@@ -252,6 +265,13 @@ elsif defined?(Cequel)
252
265
  class Cat < Animal
253
266
  end
254
267
 
268
+ class Sku
269
+ include Cequel::Record
270
+
271
+ key :id, :uuid
272
+ column :name, :text
273
+ end
274
+
255
275
  [Product, Store, Region, Speaker, Animal].each(&:synchronize_schema)
256
276
  else
257
277
  require "active_record"
@@ -339,6 +359,10 @@ else
339
359
  t.string :type
340
360
  end
341
361
 
362
+ ActiveRecord::Migration.create_table :skus, id: :uuid do |t|
363
+ t.string :name
364
+ end
365
+
342
366
  class Product < ActiveRecord::Base
343
367
  belongs_to :store
344
368
  end
@@ -361,6 +385,9 @@ else
361
385
 
362
386
  class Cat < Animal
363
387
  end
388
+
389
+ class Sku < ActiveRecord::Base
390
+ end
364
391
  end
365
392
 
366
393
  class Product
@@ -477,6 +504,10 @@ class Animal
477
504
  # wordnet: true
478
505
  end
479
506
 
507
+ class Sku
508
+ searchkick callbacks: defined?(ActiveJob) ? :async : true
509
+ end
510
+
480
511
  Product.searchkick_index.delete if Product.searchkick_index.exists?
481
512
  Product.reindex
482
513
  Product.reindex # run twice for both index paths
@@ -493,6 +524,7 @@ class Minitest::Test
493
524
  Store.destroy_all
494
525
  Animal.destroy_all
495
526
  Speaker.destroy_all
527
+ Sku.destroy_all
496
528
  end
497
529
 
498
530
  protected
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: 2.3.0
4
+ version: 2.3.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: 2017-05-07 00:00:00.000000000 Z
11
+ date: 2017-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -170,6 +170,7 @@ files:
170
170
  - test/similar_test.rb
171
171
  - test/sql_test.rb
172
172
  - test/suggest_test.rb
173
+ - test/support/kaminari.yml
173
174
  - test/synonyms_test.rb
174
175
  - test/test_helper.rb
175
176
  - test/where_test.rb
@@ -245,6 +246,7 @@ test_files:
245
246
  - test/similar_test.rb
246
247
  - test/sql_test.rb
247
248
  - test/suggest_test.rb
249
+ - test/support/kaminari.yml
248
250
  - test/synonyms_test.rb
249
251
  - test/test_helper.rb
250
252
  - test/where_test.rb