searchkick 2.3.0 → 2.3.1

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