searchkick 2.0.1 → 2.0.2
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -0
- data/README.md +6 -6
- data/Rakefile +1 -0
- data/lib/searchkick/index.rb +9 -8
- data/lib/searchkick/query.rb +21 -11
- data/lib/searchkick/reindex_v2_job.rb +16 -1
- data/lib/searchkick/version.rb +1 -1
- data/test/boost_test.rb +18 -0
- data/test/gemfiles/mongoid6.gemfile +1 -0
- data/test/gemfiles/nobrainer.gemfile +1 -0
- data/test/highlight_test.rb +5 -0
- data/test/index_test.rb +10 -3
- data/test/test_helper.rb +5 -1
- data/test/where_test.rb +9 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01c193778cc86142dca494a2aa0b0ac8c6474d0b
|
4
|
+
data.tar.gz: 7ff77b2f385a8004acaf1dabb4935f90d61c26f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/README.md
CHANGED
@@ -119,7 +119,7 @@ limit: 20, offset: 40
|
|
119
119
|
Select
|
120
120
|
|
121
121
|
```ruby
|
122
|
-
select: [
|
122
|
+
select: [:name]
|
123
123
|
```
|
124
124
|
|
125
125
|
### Results
|
@@ -497,7 +497,7 @@ end
|
|
497
497
|
|
498
498
|
### Analytics
|
499
499
|
|
500
|
-
|
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: [
|
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: [
|
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: {
|
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: {
|
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
data/lib/searchkick/index.rb
CHANGED
@@ -38,7 +38,7 @@ module Searchkick
|
|
38
38
|
client.indices.get_settings index: name
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
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
|
203
|
+
# import before promotion
|
203
204
|
index.import_scope(scope, resume: resume) if import
|
204
205
|
|
205
206
|
# get existing indices to remove
|
206
|
-
|
207
|
-
clean_indices
|
207
|
+
promote(index.name)
|
208
|
+
clean_indices unless retain
|
208
209
|
else
|
209
210
|
delete if exists?
|
210
|
-
|
211
|
+
promote(index.name)
|
211
212
|
|
212
|
-
# import after
|
213
|
+
# import after promotion
|
213
214
|
index.import_scope(scope, resume: resume) if import
|
214
215
|
end
|
215
216
|
|
data/lib/searchkick/query.rb
CHANGED
@@ -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
|
-
|
463
|
-
|
464
|
-
|
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
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
boost_by_distance
|
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
|
-
|
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 =
|
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
|
data/lib/searchkick/version.rb
CHANGED
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
|
data/test/highlight_test.rb
CHANGED
@@ -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 "
|
117
|
+
assert_search "*", []
|
111
118
|
end
|
112
119
|
|
113
|
-
def
|
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
|
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
|
-
|
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.
|
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:
|
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.
|
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,
|