searchkick 2.0.1 → 2.0.2

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: 17c7f39c1470d38bc637340ae0031997a0b0134c
4
- data.tar.gz: 0167c2d1448536bf5b2b48120f56e992c22aed8e
3
+ metadata.gz: 01c193778cc86142dca494a2aa0b0ac8c6474d0b
4
+ data.tar.gz: 7ff77b2f385a8004acaf1dabb4935f90d61c26f8
5
5
  SHA512:
6
- metadata.gz: a6bf61245813c31fc922a29c5aa4998df13c9c1f51eba6c248ea1bfbba435f048de37a069d23cad6fc041618a235974895cb7466c08ee24cb14eb39296fc908d
7
- data.tar.gz: b50909d603658acd1c9562470c2fd1de404cffd2f93a22c1ca926003afaaabcf33ea70a14ba69d96e3b94b50af241b91cbd267492c079416e2b9ae500c6cf32e
6
+ metadata.gz: cbeefdbb0c40dfb0c32789e7468d3b570c7014fe9b27a202fca7b310820fd3c223264e360456ea68ca5bb13378e3770f448b3237abb9f61385f22d72942b09b0
7
+ data.tar.gz: 199fb639bcc0d883e87848009e35d2ed0e62e491c5ada17d0eeab3844539513f27c09af46ebb17051f402973dabed86e46e8d6bd08e18b9b3dc43ce3ac2d74e0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 2.0.2
2
+
3
+ - Added `retain` option to `reindex`
4
+ - Added support for attributes in highlight tags
5
+ - Fixed potentially silent errors in reindex job
6
+ - Improved syntax for `boost_by_distance`
7
+
1
8
  ## 2.0.1
2
9
 
3
10
  - Added `search_hit` and `search_highlights` methods to models
data/Gemfile CHANGED
@@ -7,3 +7,4 @@ gem "sqlite3"
7
7
  gem "activerecord", "~> 5.0.0"
8
8
  gem "gemoji-parser"
9
9
  gem "typhoeus"
10
+ gem "activejob"
data/README.md CHANGED
@@ -119,7 +119,7 @@ limit: 20, offset: 40
119
119
  Select
120
120
 
121
121
  ```ruby
122
- select: ["name"]
122
+ select: [:name]
123
123
  ```
124
124
 
125
125
  ### Results
@@ -497,7 +497,7 @@ end
497
497
 
498
498
  ### Analytics
499
499
 
500
- We highly recommend tracking searches and conversions.
500
+ The best starting point to improve your search **by far** is to track searches and conversions.
501
501
 
502
502
  [Searchjoy](https://github.com/ankane/searchjoy) makes it easy.
503
503
 
@@ -815,14 +815,14 @@ Find similar items.
815
815
 
816
816
  ```ruby
817
817
  product = Product.first
818
- product.similar(fields: ["name"], where: {size: "12 oz"})
818
+ product.similar(fields: [:name], where: {size: "12 oz"})
819
819
  ```
820
820
 
821
821
  ### Geospatial Searches
822
822
 
823
823
  ```ruby
824
824
  class City < ActiveRecord::Base
825
- searchkick locations: ["location"]
825
+ searchkick locations: [:location]
826
826
 
827
827
  def search_data
828
828
  attributes.merge location: {lat: latitude, lon: longitude}
@@ -853,13 +853,13 @@ City.search "san", where: {location: {geo_polygon: {points: [{lat: 38, lon: -123
853
853
  Boost results by distance - closer results are boosted more
854
854
 
855
855
  ```ruby
856
- City.search "san", boost_by_distance: {field: :location, origin: {lat: 37, lon: -122}}
856
+ City.search "san", boost_by_distance: {location: {origin: {lat: 37, lon: -122}}}
857
857
  ```
858
858
 
859
859
  Also supports [additional options](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html#_decay_functions)
860
860
 
861
861
  ```ruby
862
- City.search "san", boost_by_distance: {field: :location, origin: {lat: 37, lon: -122}, function: :linear, scale: "30mi", decay: 0.5}
862
+ City.search "san", boost_by_distance: {location: {origin: {lat: 37, lon: -122}, function: "linear", scale: "30mi", decay: 0.5}}
863
863
  ```
864
864
 
865
865
  ### Geo Shapes
data/Rakefile CHANGED
@@ -5,6 +5,7 @@ task default: :test
5
5
  Rake::TestTask.new do |t|
6
6
  t.libs << "test"
7
7
  t.pattern = "test/**/*_test.rb"
8
+ t.warning = false
8
9
  end
9
10
 
10
11
  task :benchmark do
@@ -38,7 +38,7 @@ module Searchkick
38
38
  client.indices.get_settings index: name
39
39
  end
40
40
 
41
- def swap(new_name)
41
+ def promote(new_name)
42
42
  old_indices =
43
43
  begin
44
44
  client.indices.get_alias(name: name).keys
@@ -48,6 +48,7 @@ module Searchkick
48
48
  actions = old_indices.map { |old_name| {remove: {index: old_name, alias: name}} } + [{add: {index: new_name, alias: name}}]
49
49
  client.indices.update_aliases body: {actions: actions}
50
50
  end
51
+ alias_method :swap, :promote
51
52
 
52
53
  # record based
53
54
 
@@ -186,30 +187,30 @@ module Searchkick
186
187
 
187
188
  # https://gist.github.com/jarosan/3124884
188
189
  # http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
189
- def reindex_scope(scope, import: true, resume: false)
190
+ def reindex_scope(scope, import: true, resume: false, retain: false)
190
191
  if resume
191
192
  index_name = all_indices.sort.last
192
193
  raise Searchkick::Error, "No index to resume" unless index_name
193
194
  index = Searchkick::Index.new(index_name)
194
195
  else
195
- clean_indices
196
+ clean_indices unless retain
196
197
 
197
198
  index = create_index(index_options: scope.searchkick_index_options)
198
199
  end
199
200
 
200
201
  # check if alias exists
201
202
  if alias_exists?
202
- # import before swap
203
+ # import before promotion
203
204
  index.import_scope(scope, resume: resume) if import
204
205
 
205
206
  # get existing indices to remove
206
- swap(index.name)
207
- clean_indices
207
+ promote(index.name)
208
+ clean_indices unless retain
208
209
  else
209
210
  delete if exists?
210
- swap(index.name)
211
+ promote(index.name)
211
212
 
212
- # import after swap
213
+ # import after promotion
213
214
  index.import_scope(scope, resume: resume) if import
214
215
  end
215
216
 
@@ -459,17 +459,25 @@ module Searchkick
459
459
 
460
460
  def set_boost_by_distance(custom_filters)
461
461
  boost_by_distance = options[:boost_by_distance] || {}
462
- boost_by_distance = {function: :gauss, scale: "5mi"}.merge(boost_by_distance)
463
- if !boost_by_distance[:field] || !boost_by_distance[:origin]
464
- raise ArgumentError, "boost_by_distance requires :field and :origin"
462
+
463
+ # legacy format
464
+ if boost_by_distance[:field]
465
+ boost_by_distance = {boost_by_distance[:field] => boost_by_distance.except(:field)}
465
466
  end
466
- function_params = boost_by_distance.select { |k, _| [:origin, :scale, :offset, :decay].include?(k) }
467
- function_params[:origin] = location_value(function_params[:origin])
468
- custom_filters << {
469
- boost_by_distance[:function] => {
470
- boost_by_distance[:field] => function_params
467
+
468
+ boost_by_distance.each do |field, attributes|
469
+ attributes = {function: :gauss, scale: "5mi"}.merge(attributes)
470
+ unless attributes[:origin]
471
+ raise ArgumentError, "boost_by_distance requires :origin"
472
+ end
473
+ function_params = attributes.select { |k, _| [:origin, :scale, :offset, :decay].include?(k) }
474
+ function_params[:origin] = location_value(function_params[:origin])
475
+ custom_filters << {
476
+ attributes[:function] => {
477
+ field => function_params
478
+ }
471
479
  }
472
- }
480
+ end
473
481
  end
474
482
 
475
483
  def set_boost_by(multiply_filters, custom_filters)
@@ -542,7 +550,7 @@ module Searchkick
542
550
  if options[:highlight].is_a?(Hash)
543
551
  if (tag = options[:highlight][:tag])
544
552
  payload[:highlight][:pre_tags] = [tag]
545
- payload[:highlight][:post_tags] = [tag.to_s.gsub(/\A</, "</")]
553
+ payload[:highlight][:post_tags] = [tag.to_s.gsub(/\A<(\w+).+/, "</\\1>")]
546
554
  end
547
555
 
548
556
  if (fragment_size = options[:highlight][:fragment_size])
@@ -770,7 +778,9 @@ module Searchkick
770
778
  if below50?
771
779
  {
772
780
  filter: {
773
- and: where_filters(field => value)
781
+ bool: {
782
+ must: where_filters(field => value)
783
+ }
774
784
  },
775
785
  boost_factor: factor
776
786
  }
@@ -1,10 +1,25 @@
1
1
  module Searchkick
2
2
  class ReindexV2Job < ActiveJob::Base
3
+ RECORD_NOT_FOUND_CLASSES = [
4
+ "ActiveRecord::RecordNotFound",
5
+ "Mongoid::Errors::DocumentNotFound",
6
+ "NoBrainer::Error::DocumentNotFound"
7
+ ]
8
+
3
9
  queue_as :searchkick
4
10
 
5
11
  def perform(klass, id)
6
12
  model = klass.constantize
7
- record = model.find(id) rescue nil # TODO fix lazy coding
13
+ record =
14
+ begin
15
+ model.find(id)
16
+ rescue => e
17
+ # check by name rather than rescue directly so we don't need
18
+ # to determine which classes are defined
19
+ raise e unless RECORD_NOT_FOUND_CLASSES.include?(e.class.name)
20
+ nil
21
+ end
22
+
8
23
  index = model.searchkick_index
9
24
  if !record || !record.should_index?
10
25
  # hacky
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "2.0.1"
2
+ VERSION = "2.0.2"
3
3
  end
data/test/boost_test.rb CHANGED
@@ -138,6 +138,24 @@ class BoostTest < Minitest::Test
138
138
  assert_order "san", ["San Francisco", "San Antonio", "San Marino"], boost_by_distance: {field: :location, origin: {lat: 37, lon: -122}, scale: "1000mi"}
139
139
  end
140
140
 
141
+ def test_boost_by_distance_v2
142
+ store [
143
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
144
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000},
145
+ {name: "San Marino", latitude: 43.9333, longitude: 12.4667}
146
+ ]
147
+ assert_order "san", ["San Francisco", "San Antonio", "San Marino"], boost_by_distance: {location: {origin: [37, -122], scale: "1000mi"}}
148
+ end
149
+
150
+ def test_boost_by_distance_v2_hash
151
+ store [
152
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
153
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000},
154
+ {name: "San Marino", latitude: 43.9333, longitude: 12.4667}
155
+ ]
156
+ assert_order "san", ["San Francisco", "San Antonio", "San Marino"], boost_by_distance: {location: {origin: {lat: 37, lon: -122}, scale: "1000mi"}}
157
+ end
158
+
141
159
  def test_boost_by_indices
142
160
  store_names ["Rex"], Animal
143
161
  store_names ["Rexx"], Product
@@ -4,3 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "mongoid", "~> 6.0.0"
7
+ gem "activejob"
@@ -4,3 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "nobrainer", ">= 0.21.0"
7
+ gem "activejob"
@@ -11,6 +11,11 @@ class HighlightTest < Minitest::Test
11
11
  assert_equal "Two Door <strong>Cinema</strong> Club", Product.search("cinema", fields: [:name], highlight: {tag: "<strong>"}).first.search_highlights[:name]
12
12
  end
13
13
 
14
+ def test_tag_class
15
+ store_names ["Two Door Cinema Club"]
16
+ assert_equal "Two Door <strong class='classy'>Cinema</strong> Club", Product.search("cinema", fields: [:name], highlight: {tag: "<strong class='classy'>"}).first.search_highlights[:name]
17
+ end
18
+
14
19
  def test_multiple_fields
15
20
  store [{name: "Two Door Cinema Club", color: "Cinema Orange"}]
16
21
  highlights = Product.search("cinema", fields: [:name, :color], highlight: true).first.search_highlights
data/test/index_test.rb CHANGED
@@ -28,6 +28,13 @@ class IndexTest < Minitest::Test
28
28
  assert !old_index.exists?
29
29
  end
30
30
 
31
+ def test_retain
32
+ Product.reindex
33
+ assert_equal 1, Product.searchkick_index.all_indices.size
34
+ Product.reindex(retain: true)
35
+ assert_equal 2, Product.searchkick_index.all_indices.size
36
+ end
37
+
31
38
  def test_total_docs
32
39
  store_names ["Product A"]
33
40
  assert_equal 1, Product.searchkick_index.total_docs
@@ -107,10 +114,10 @@ class IndexTest < Minitest::Test
107
114
  store_names ["Product A"]
108
115
  raise ActiveRecord::Rollback
109
116
  end
110
- assert_search "product", []
117
+ assert_search "*", []
111
118
  end
112
119
 
113
- def test_analyzed_only
120
+ def test_filterable
114
121
  # skip for 5.0 since it throws
115
122
  # Cannot search on field [alt_description] since it is not indexed.
116
123
  skip unless elasticsearch_below50?
@@ -118,7 +125,7 @@ class IndexTest < Minitest::Test
118
125
  assert_search "*", [], where: {alt_description: "Hello"}
119
126
  end
120
127
 
121
- def test_analyzed_only_large_value
128
+ def test_large_value
122
129
  skip if nobrainer?
123
130
  large_value = 10000.times.map { "hello" }.join(" ")
124
131
  store [{name: "Product A", alt_description: large_value}]
data/test/test_helper.rb CHANGED
@@ -18,7 +18,11 @@ puts "Running against Elasticsearch #{Searchkick.server_version}"
18
18
 
19
19
  I18n.config.enforce_available_locales = true
20
20
 
21
- ActiveJob::Base.logger = nil if defined?(ActiveJob)
21
+ if defined?(ActiveJob)
22
+ ActiveJob::Base.logger = nil
23
+ ActiveJob::Base.queue_adapter = :inline
24
+ end
25
+
22
26
  ActiveSupport::LogSubscriber.logger = Logger.new(STDOUT) if ENV["NOTIFICATIONS"]
23
27
 
24
28
  def elasticsearch_below50?
data/test/where_test.rb CHANGED
@@ -184,6 +184,15 @@ class WhereTest < Minitest::Test
184
184
  assert_search "san", ["San Francisco"], where: {multiple_locations: {near: [37.5, -122.5]}}
185
185
  end
186
186
 
187
+ def test_multiple_locations_with_term_filter
188
+ store [
189
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
190
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000}
191
+ ]
192
+ assert_search "san", [], where: {multiple_locations: {near: [37.5, -122.5]}, name: "San Antonio"}
193
+ assert_search "san", ["San Francisco"], where: {multiple_locations: {near: [37.5, -122.5]}, name: "San Francisco"}
194
+ end
195
+
187
196
  def test_multiple_locations_hash
188
197
  store [
189
198
  {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
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.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-31 00:00:00.000000000 Z
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -185,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
185
  version: '0'
186
186
  requirements: []
187
187
  rubyforge_project:
188
- rubygems_version: 2.5.1
188
+ rubygems_version: 2.6.8
189
189
  signing_key:
190
190
  specification_version: 4
191
191
  summary: Searchkick learns what your users are looking for. As more people search,