searchkick 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +51 -0
- data/README.md +37 -23
- data/lib/searchkick/bulk_reindex_job.rb +1 -1
- data/lib/searchkick/hash_wrapper.rb +12 -0
- data/lib/searchkick/index_options.rb +2 -1
- data/lib/searchkick/process_batch_job.rb +1 -1
- data/lib/searchkick/process_queue_job.rb +1 -1
- data/lib/searchkick/query.rb +42 -14
- data/lib/searchkick/results.rb +12 -2
- data/lib/searchkick/version.rb +1 -1
- data/lib/searchkick.rb +27 -7
- data/test/aggs_test.rb +20 -0
- data/test/boost_test.rb +11 -0
- data/test/ci/before_install.sh +1 -1
- data/test/multi_search_test.rb +8 -0
- data/test/sql_test.rb +7 -0
- data/test/suggest_test.rb +5 -0
- data/test/test_helper.rb +1 -1
- data/test/where_test.rb +16 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48b011f59eaeb3f3b2213e20bf2206d4963f7bb0
|
4
|
+
data.tar.gz: d3ffbd07eda7ca733915c251ce8b1e1c9c146e44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 910eaa5810045780efabe7874e0c4e6bb669ad30c3f1bead3c37ab1f817e21a5e73c55acfcf7ffeacc63be95e163ddb5470b4912967379062afab45413d9d8ea
|
7
|
+
data.tar.gz: eae561c188d98071116695b8997f95ac31f1d0229fc56f968193aaac423020ee1219344568c61e2e1d7ddeee94f583189300845b5f5927a7122ab2cf5305f127
|
data/.travis.yml
CHANGED
@@ -20,7 +20,7 @@ gemfile:
|
|
20
20
|
- test/gemfiles/mongoid5.gemfile
|
21
21
|
- test/gemfiles/mongoid6.gemfile
|
22
22
|
env:
|
23
|
-
- ELASTICSEARCH_VERSION=6.
|
23
|
+
- ELASTICSEARCH_VERSION=6.2.1
|
24
24
|
jdk: oraclejdk8
|
25
25
|
matrix:
|
26
26
|
include:
|
@@ -36,3 +36,6 @@ matrix:
|
|
36
36
|
- gemfile: Gemfile
|
37
37
|
env: ELASTICSEARCH_VERSION=5.6.4
|
38
38
|
jdk: oraclejdk8
|
39
|
+
- gemfile: Gemfile
|
40
|
+
env: ELASTICSEARCH_VERSION=6.0.0
|
41
|
+
jdk: oraclejdk8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 2.5.0
|
2
|
+
|
3
|
+
- Try requests 3 times before raising error
|
4
|
+
- Better exception when trying to access results for failed multi-search query
|
5
|
+
- More efficient aggregations with `where` clauses
|
6
|
+
- Added support for `faraday_middleware-aws-sigv4`
|
7
|
+
- Added `credentials` option to `aws_credentials`
|
8
|
+
- Added `modifier` option to `boost_by`
|
9
|
+
- Added `scope_results` option
|
10
|
+
- Added `factor` option to `boost_by_distance`
|
11
|
+
|
1
12
|
## 2.4.0
|
2
13
|
|
3
14
|
- Fixed `similar` for Elasticsearch 6
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
First, thanks for wanting to contribute. You’re awesome! :heart:
|
4
|
+
|
5
|
+
## Questions
|
6
|
+
|
7
|
+
Use [Stack Overflow](https://stackoverflow.com/) with the tag `searchkick`.
|
8
|
+
|
9
|
+
## Feature Requests
|
10
|
+
|
11
|
+
Create an issue. Start the title with `[Idea]`.
|
12
|
+
|
13
|
+
## Issues
|
14
|
+
|
15
|
+
Think you’ve discovered an issue?
|
16
|
+
|
17
|
+
1. Search existing issues to see if it’s been reported.
|
18
|
+
2. Try the `master` branch to make sure it hasn’t been fixed.
|
19
|
+
|
20
|
+
```rb
|
21
|
+
gem "searchkick", github: "ankane/searchkick"
|
22
|
+
```
|
23
|
+
|
24
|
+
3. Try the `debug` option when searching. This can reveal useful info.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
Product.search("something", debug: true)
|
28
|
+
```
|
29
|
+
|
30
|
+
If the above steps don’t help, create an issue.
|
31
|
+
|
32
|
+
- For incorrect search results, recreate the problem by forking [this gist](https://gist.github.com/ankane/f80b0923d9ae2c077f41997f7b704e5c). Include a link to your gist and the output in the issue.
|
33
|
+
- For exceptions, include the complete backtrace.
|
34
|
+
|
35
|
+
## Pull Requests
|
36
|
+
|
37
|
+
Fork the project and create a pull request. A few tips:
|
38
|
+
|
39
|
+
- Keep changes to a minimum. If you have multiple features or fixes, submit multiple pull requests.
|
40
|
+
- Follow the existing style. The code should read like it’s written by a single person.
|
41
|
+
- Add one or more tests if possible. Make sure existing tests pass with:
|
42
|
+
|
43
|
+
```sh
|
44
|
+
bundle exec rake test
|
45
|
+
```
|
46
|
+
|
47
|
+
Feel free to open an issue to get feedback on your idea before spending too much time on it.
|
48
|
+
|
49
|
+
---
|
50
|
+
|
51
|
+
This contributing guide is released under [CCO](https://creativecommons.org/publicdomain/zero/1.0/) (public domain). Use it for your own project without attribution.
|
data/README.md
CHANGED
@@ -299,6 +299,8 @@ end
|
|
299
299
|
|
300
300
|
Call `Product.reindex` after changing synonyms.
|
301
301
|
|
302
|
+
Synonyms cannot be more than two words at the moment.
|
303
|
+
|
302
304
|
To read synonyms from a file, use:
|
303
305
|
|
304
306
|
```ruby
|
@@ -759,6 +761,12 @@ Date histogram
|
|
759
761
|
Product.search "pear", aggs: {products_per_year: {date_histogram: {field: :created_at, interval: :year}}}
|
760
762
|
```
|
761
763
|
|
764
|
+
For other aggregation types, including sub-aggregations, use `body_options`:
|
765
|
+
|
766
|
+
```ruby
|
767
|
+
Product.search "orange", body_options: {aggs: {price: {histogram: {field: :price, interval: 10}}}
|
768
|
+
```
|
769
|
+
|
762
770
|
#### Moving From Facets
|
763
771
|
|
764
772
|
1. Replace `facets` with `aggs` in searches. **Note:** Stats facets are not supported at this time.
|
@@ -895,6 +903,8 @@ Bounded by a box
|
|
895
903
|
Restaurant.search "sushi", where: {location: {top_left: {lat: 38, lon: -123}, bottom_right: {lat: 37, lon: -122}}}
|
896
904
|
```
|
897
905
|
|
906
|
+
**Note:** `top_right` and `bottom_left` also work
|
907
|
+
|
898
908
|
Bounded by a polygon
|
899
909
|
|
900
910
|
```ruby
|
@@ -1047,20 +1057,32 @@ Searchkick uses `ENV["ELASTICSEARCH_URL"]` for the Elasticsearch server. This de
|
|
1047
1057
|
|
1048
1058
|
### Heroku
|
1049
1059
|
|
1050
|
-
Choose an add-on: [
|
1060
|
+
Choose an add-on: [Bonsai](https://elements.heroku.com/addons/bonsai) or [Elastic Cloud](https://elements.heroku.com/addons/foundelasticsearch). [SearchBox](https://elements.heroku.com/addons/searchbox) does not work at the moment.
|
1051
1061
|
|
1052
|
-
|
1053
|
-
# SearchBox
|
1054
|
-
heroku addons:create searchbox:starter
|
1055
|
-
heroku config:set ELASTICSEARCH_URL=`heroku config:get SEARCHBOX_URL`
|
1062
|
+
For Bonsai:
|
1056
1063
|
|
1057
|
-
|
1064
|
+
```sh
|
1058
1065
|
heroku addons:create bonsai
|
1059
1066
|
heroku config:set ELASTICSEARCH_URL=`heroku config:get BONSAI_URL`
|
1067
|
+
```
|
1068
|
+
|
1069
|
+
For Found:
|
1060
1070
|
|
1061
|
-
|
1071
|
+
```sh
|
1062
1072
|
heroku addons:create foundelasticsearch
|
1063
|
-
heroku
|
1073
|
+
heroku addons:open foundelasticsearch
|
1074
|
+
```
|
1075
|
+
|
1076
|
+
Visit the Shield page and reset your password. You’ll need to add the username and password to your url. Get the existing url with:
|
1077
|
+
|
1078
|
+
```sh
|
1079
|
+
heroku config:get FOUNDELASTICSEARCH_URL
|
1080
|
+
```
|
1081
|
+
|
1082
|
+
And add `elastic:password@` right after `https://`:
|
1083
|
+
|
1084
|
+
```sh
|
1085
|
+
heroku config:set ELASTICSEARCH_URL=https://elastic:password@12345.us-east-1.aws.found.io
|
1064
1086
|
```
|
1065
1087
|
|
1066
1088
|
Then deploy and reindex:
|
@@ -1415,7 +1437,7 @@ class Product < ApplicationRecord
|
|
1415
1437
|
searchkick mappings: {
|
1416
1438
|
product: {
|
1417
1439
|
properties: {
|
1418
|
-
name: {type: "
|
1440
|
+
name: {type: "keyword"}
|
1419
1441
|
}
|
1420
1442
|
}
|
1421
1443
|
}
|
@@ -1506,20 +1528,6 @@ To query nested data, use dot notation.
|
|
1506
1528
|
User.search "san", fields: ["address.city"], where: {"address.zip_code" => 12345}
|
1507
1529
|
```
|
1508
1530
|
|
1509
|
-
## Search Concepts
|
1510
|
-
|
1511
|
-
### Precision and Recall
|
1512
|
-
|
1513
|
-
[Precision and recall](https://en.wikipedia.org/wiki/Precision_and_recall) are two key concepts in search (also known as *information retrieval*). To help illustrate, let’s walk through an example.
|
1514
|
-
|
1515
|
-
You have a store with 16 types of apples. A user searches for `apples` gets 10 results. 8 of the results are for apples, and 2 are for apple juice.
|
1516
|
-
|
1517
|
-
**Precision** is the fraction of documents in the results that are relevant. There are 10 results and 8 are relevant, so precision is 80%.
|
1518
|
-
|
1519
|
-
**Recall** is the fraction of relevant documents in the results out of all relevant documents. There are 16 apples and only 8 in the results, so recall is 50%.
|
1520
|
-
|
1521
|
-
There’s typically a trade-off between the two. As you tweak your search to increase precision (not return irrelevant documents), there’s are greater chance a relevant document also isn’t returned, which decreases recall. The opposite also applies. As you try to increase recall (return a higher number of relevent documents), there’s a greater chance you also return an irrelevant document, decreasing precision.
|
1522
|
-
|
1523
1531
|
## Reference
|
1524
1532
|
|
1525
1533
|
Reindex one record
|
@@ -1658,6 +1666,12 @@ Eager load different associations by model
|
|
1658
1666
|
Searchkick.search("*", index_name: [Product, Store], model_includes: {Product => [:store], Store => [:product]})
|
1659
1667
|
```
|
1660
1668
|
|
1669
|
+
Run additional scopes on results [master]
|
1670
|
+
|
1671
|
+
```ruby
|
1672
|
+
Product.search "milk", scope_results: ->(r) { r.with_attached_images }
|
1673
|
+
```
|
1674
|
+
|
1661
1675
|
Specify default fields to search
|
1662
1676
|
|
1663
1677
|
```ruby
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Searchkick
|
2
2
|
class BulkReindexJob < ActiveJob::Base
|
3
|
-
queue_as
|
3
|
+
queue_as { Searchkick.queue_name }
|
4
4
|
|
5
5
|
def perform(class_name:, record_ids: nil, index_name: nil, method_name: nil, batch_id: nil, min_id: nil, max_id: nil)
|
6
6
|
klass = class_name.constantize
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Searchkick
|
2
|
+
# Subclass of `Hashie::Mash` to wrap Hash-like structures
|
3
|
+
# (responses from Elasticsearch)
|
4
|
+
#
|
5
|
+
# The primary goal of the subclass is to disable the
|
6
|
+
# warning being printed by Hashie for re-defined
|
7
|
+
# methods, such as `sort`.
|
8
|
+
#
|
9
|
+
class HashWrapper < ::Hashie::Mash
|
10
|
+
disable_warnings if respond_to?(:disable_warnings)
|
11
|
+
end
|
12
|
+
end
|
@@ -174,7 +174,8 @@ module Searchkick
|
|
174
174
|
if synonyms.any?
|
175
175
|
settings[:analysis][:filter][:searchkick_synonym] = {
|
176
176
|
type: "synonym",
|
177
|
-
|
177
|
+
# only remove a single space from synonyms so three-word synonyms will fail noisily instead of silently
|
178
|
+
synonyms: synonyms.select { |s| s.size > 1 }.map { |s| s.is_a?(Array) ? s.map { |s| s.sub(/\s+/, "") }.join(",") : s }.map(&:downcase)
|
178
179
|
}
|
179
180
|
# choosing a place for the synonym filter when stemming is not easy
|
180
181
|
# https://groups.google.com/forum/#!topic/elasticsearch/p7qcQlgHdB8
|
data/lib/searchkick/query.rb
CHANGED
@@ -19,7 +19,7 @@ module Searchkick
|
|
19
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, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile,
|
22
|
-
:request_params, :routing, :select, :similar, :smart_aggs, :suggest, :track, :type, :where]
|
22
|
+
:request_params, :routing, :scope_results, :select, :similar, :smart_aggs, :suggest, :track, :type, :where]
|
23
23
|
raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
|
24
24
|
|
25
25
|
term = term.to_s
|
@@ -98,7 +98,7 @@ module Searchkick
|
|
98
98
|
# no easy way to tell which host the client will use
|
99
99
|
host = Searchkick.client.transport.hosts.first
|
100
100
|
credentials = host[:user] || host[:password] ? "#{host[:user]}:#{host[:password]}@" : nil
|
101
|
-
"curl #{host[:protocol]}://#{credentials}#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?pretty -d '#{query[:body].to_json}'"
|
101
|
+
"curl #{host[:protocol]}://#{credentials}#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?pretty -H 'Content-Type: application/json' -d '#{query[:body].to_json}'"
|
102
102
|
end
|
103
103
|
|
104
104
|
def handle_response(response)
|
@@ -112,7 +112,9 @@ module Searchkick
|
|
112
112
|
json: !@json.nil?,
|
113
113
|
match_suffix: @match_suffix,
|
114
114
|
highlighted_fields: @highlighted_fields || [],
|
115
|
-
misspellings: @misspellings
|
115
|
+
misspellings: @misspellings,
|
116
|
+
term: term,
|
117
|
+
scope_results: options[:scope_results]
|
116
118
|
}
|
117
119
|
|
118
120
|
if options[:debug]
|
@@ -455,12 +457,16 @@ module Searchkick
|
|
455
457
|
where[:type] = [options[:type] || klass].flatten.map { |v| searchkick_index.klass_document_type(v, true) }
|
456
458
|
end
|
457
459
|
|
458
|
-
# filters
|
460
|
+
# start everything as efficient filters
|
461
|
+
# move to post_filters as aggs demand
|
459
462
|
filters = where_filters(where)
|
460
|
-
|
463
|
+
post_filters = []
|
461
464
|
|
462
465
|
# aggregations
|
463
|
-
set_aggregations(payload) if options[:aggs]
|
466
|
+
set_aggregations(payload, filters, post_filters) if options[:aggs]
|
467
|
+
|
468
|
+
# filters
|
469
|
+
set_filters(payload, filters, post_filters)
|
464
470
|
|
465
471
|
# suggestions
|
466
472
|
set_suggestions(payload, options[:suggest]) if options[:suggest]
|
@@ -545,6 +551,7 @@ module Searchkick
|
|
545
551
|
function_params = attributes.select { |k, _| [:origin, :scale, :offset, :decay].include?(k) }
|
546
552
|
function_params[:origin] = location_value(function_params[:origin])
|
547
553
|
custom_filters << {
|
554
|
+
weight: attributes[:factor] || 1,
|
548
555
|
attributes[:function] => {
|
549
556
|
field => function_params
|
550
557
|
}
|
@@ -653,12 +660,11 @@ module Searchkick
|
|
653
660
|
@highlighted_fields = payload[:highlight][:fields].keys
|
654
661
|
end
|
655
662
|
|
656
|
-
def set_aggregations(payload)
|
663
|
+
def set_aggregations(payload, filters, post_filters)
|
657
664
|
aggs = options[:aggs]
|
658
665
|
payload[:aggs] = {}
|
659
666
|
|
660
667
|
aggs = Hash[aggs.map { |f| [f, {}] }] if aggs.is_a?(Array) # convert to more advanced syntax
|
661
|
-
|
662
668
|
aggs.each do |field, agg_options|
|
663
669
|
size = agg_options[:limit] ? agg_options[:limit] : 1_000
|
664
670
|
shared_agg_options = agg_options.slice(:order, :min_doc_count)
|
@@ -703,6 +709,17 @@ module Searchkick
|
|
703
709
|
where = {}
|
704
710
|
where = (options[:where] || {}).reject { |k| k == field } unless options[:smart_aggs] == false
|
705
711
|
agg_filters = where_filters(where.merge(agg_options[:where] || {}))
|
712
|
+
|
713
|
+
# only do one level comparison for simplicity
|
714
|
+
filters.select! do |filter|
|
715
|
+
if agg_filters.include?(filter)
|
716
|
+
true
|
717
|
+
else
|
718
|
+
post_filters << filter
|
719
|
+
false
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
706
723
|
if agg_filters.any?
|
707
724
|
payload[:aggs][field] = {
|
708
725
|
filter: {
|
@@ -718,14 +735,16 @@ module Searchkick
|
|
718
735
|
end
|
719
736
|
end
|
720
737
|
|
721
|
-
def set_filters(payload, filters)
|
722
|
-
if
|
738
|
+
def set_filters(payload, filters, post_filters)
|
739
|
+
if post_filters.any?
|
723
740
|
payload[:post_filter] = {
|
724
741
|
bool: {
|
725
|
-
filter:
|
742
|
+
filter: post_filters
|
726
743
|
}
|
727
744
|
}
|
728
|
-
|
745
|
+
end
|
746
|
+
|
747
|
+
if filters.any?
|
729
748
|
# more efficient query if no aggs
|
730
749
|
payload[:query] = {
|
731
750
|
bool: {
|
@@ -769,7 +788,7 @@ module Searchkick
|
|
769
788
|
if value.is_a?(Hash)
|
770
789
|
value.each do |op, op_value|
|
771
790
|
case op
|
772
|
-
when :within, :bottom_right
|
791
|
+
when :within, :bottom_right, :bottom_left
|
773
792
|
# do nothing
|
774
793
|
when :near
|
775
794
|
filters << {
|
@@ -804,6 +823,15 @@ module Searchkick
|
|
804
823
|
}
|
805
824
|
}
|
806
825
|
}
|
826
|
+
when :top_right
|
827
|
+
filters << {
|
828
|
+
geo_bounding_box: {
|
829
|
+
field => {
|
830
|
+
top_right: location_value(op_value),
|
831
|
+
bottom_left: location_value(value[:bottom_left])
|
832
|
+
}
|
833
|
+
}
|
834
|
+
}
|
807
835
|
when :regexp # support for regexp queries without using a regexp ruby object
|
808
836
|
filters << {regexp: {field => {value: op_value}}}
|
809
837
|
when :not # not equal
|
@@ -886,7 +914,7 @@ module Searchkick
|
|
886
914
|
field_value_factor: {
|
887
915
|
field: field,
|
888
916
|
factor: value[:factor].to_f,
|
889
|
-
modifier: log ? "ln2p" : nil
|
917
|
+
modifier: value[:modifier] || (log ? "ln2p" : nil)
|
890
918
|
}
|
891
919
|
}
|
892
920
|
|
data/lib/searchkick/results.rb
CHANGED
@@ -68,7 +68,7 @@ module Searchkick
|
|
68
68
|
end
|
69
69
|
|
70
70
|
result["id"] ||= result["_id"] # needed for legacy reasons
|
71
|
-
|
71
|
+
HashWrapper.new(result)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -77,6 +77,8 @@ module Searchkick
|
|
77
77
|
def suggestions
|
78
78
|
if response["suggest"]
|
79
79
|
response["suggest"].values.flat_map { |v| v.first["options"] }.sort_by { |o| -o["score"] }.map { |o| o["text"] }.uniq
|
80
|
+
elsif options[:term] == "*"
|
81
|
+
[]
|
80
82
|
else
|
81
83
|
raise "Pass `suggest: true` to the search method for suggestions"
|
82
84
|
end
|
@@ -187,7 +189,11 @@ module Searchkick
|
|
187
189
|
end
|
188
190
|
|
189
191
|
def hits
|
190
|
-
|
192
|
+
if error
|
193
|
+
raise Searchkick::Error, "Query error - use the error method to view it"
|
194
|
+
else
|
195
|
+
@response["hits"]["hits"]
|
196
|
+
end
|
191
197
|
end
|
192
198
|
|
193
199
|
def misspellings?
|
@@ -215,6 +221,10 @@ module Searchkick
|
|
215
221
|
end
|
216
222
|
end
|
217
223
|
|
224
|
+
if options[:scope_results]
|
225
|
+
records = options[:scope_results].call(records)
|
226
|
+
end
|
227
|
+
|
218
228
|
Searchkick.load_records(records, ids)
|
219
229
|
end
|
220
230
|
|
data/lib/searchkick/version.rb
CHANGED
data/lib/searchkick.rb
CHANGED
@@ -6,6 +6,7 @@ require "searchkick/index_options"
|
|
6
6
|
require "searchkick/index"
|
7
7
|
require "searchkick/indexer"
|
8
8
|
require "searchkick/reindex_queue"
|
9
|
+
require "searchkick/hash_wrapper"
|
9
10
|
require "searchkick/results"
|
10
11
|
require "searchkick/query"
|
11
12
|
require "searchkick/multi_search"
|
@@ -54,14 +55,11 @@ module Searchkick
|
|
54
55
|
|
55
56
|
Elasticsearch::Client.new({
|
56
57
|
url: ENV["ELASTICSEARCH_URL"],
|
57
|
-
transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}}
|
58
|
+
transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}},
|
59
|
+
retry_on_failure: 2
|
58
60
|
}.deep_merge(client_options)) do |f|
|
59
61
|
f.use Searchkick::Middleware
|
60
|
-
f.request
|
61
|
-
credentials: Aws::Credentials.new(aws_credentials[:access_key_id], aws_credentials[:secret_access_key]),
|
62
|
-
service_name: "es",
|
63
|
-
region: aws_credentials[:region] || "us-east-1"
|
64
|
-
} if aws_credentials
|
62
|
+
f.request signer_middleware_key, signer_middleware_aws_params if aws_credentials
|
65
63
|
end
|
66
64
|
end
|
67
65
|
end
|
@@ -136,7 +134,11 @@ module Searchkick
|
|
136
134
|
end
|
137
135
|
|
138
136
|
def self.aws_credentials=(creds)
|
139
|
-
|
137
|
+
begin
|
138
|
+
require "faraday_middleware/aws_signers_v4"
|
139
|
+
rescue LoadError
|
140
|
+
require "faraday_middleware/aws_sigv4"
|
141
|
+
end
|
140
142
|
@aws_credentials = creds
|
141
143
|
@client = nil # reset client
|
142
144
|
end
|
@@ -200,6 +202,24 @@ module Searchkick
|
|
200
202
|
def self.callbacks_value=(value)
|
201
203
|
Thread.current[:searchkick_callbacks_enabled] = value
|
202
204
|
end
|
205
|
+
|
206
|
+
# private
|
207
|
+
def self.signer_middleware_key
|
208
|
+
defined?(FaradayMiddleware::AwsSignersV4) ? :aws_signers_v4 : :aws_sigv4
|
209
|
+
end
|
210
|
+
|
211
|
+
# private
|
212
|
+
def self.signer_middleware_aws_params
|
213
|
+
if signer_middleware_key == :aws_sigv4
|
214
|
+
{service: "es", region: "us-east-1"}.merge(aws_credentials)
|
215
|
+
else
|
216
|
+
{
|
217
|
+
credentials: aws_credentials[:credentials] || Aws::Credentials.new(aws_credentials[:access_key_id], aws_credentials[:secret_access_key]),
|
218
|
+
service_name: "es",
|
219
|
+
region: aws_credentials[:region] || "us-east-1"
|
220
|
+
}
|
221
|
+
end
|
222
|
+
end
|
203
223
|
end
|
204
224
|
|
205
225
|
# TODO find better ActiveModel hook
|
data/test/aggs_test.rb
CHANGED
@@ -178,6 +178,26 @@ class AggsTest < Minitest::Test
|
|
178
178
|
assert_equal 66, products.aggs["sum_price"]["value"]
|
179
179
|
end
|
180
180
|
|
181
|
+
def test_body_options
|
182
|
+
products =
|
183
|
+
Product.search("*",
|
184
|
+
body_options: {
|
185
|
+
aggs: {
|
186
|
+
price: {
|
187
|
+
histogram: {field: :price, interval: 10}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
)
|
192
|
+
|
193
|
+
expected = [
|
194
|
+
{"key" => 0.0, "doc_count" => 1},
|
195
|
+
{"key" => 10.0, "doc_count" => 1},
|
196
|
+
{"key" => 20.0, "doc_count" => 2}
|
197
|
+
]
|
198
|
+
assert_equal products.aggs["price"]["buckets"], expected
|
199
|
+
end
|
200
|
+
|
181
201
|
protected
|
182
202
|
|
183
203
|
def buckets_as_hash(agg)
|
data/test/boost_test.rb
CHANGED
@@ -191,6 +191,17 @@ class BoostTest < Minitest::Test
|
|
191
191
|
assert_order "san", ["San Francisco", "San Antonio", "San Marino"], boost_by_distance: {location: {origin: {lat: 37, lon: -122}, scale: "1000mi"}}
|
192
192
|
end
|
193
193
|
|
194
|
+
def test_boost_by_distance_v2_factor
|
195
|
+
store [
|
196
|
+
{name: "San Francisco", latitude: 37.7833, longitude: -122.4167, found_rate: 0.1},
|
197
|
+
{name: "San Antonio", latitude: 29.4167, longitude: -98.5000, found_rate: 0.99},
|
198
|
+
{name: "San Marino", latitude: 43.9333, longitude: 12.4667, found_rate: 0.2}
|
199
|
+
]
|
200
|
+
|
201
|
+
assert_order "san", ["San Antonio","San Francisco", "San Marino"], boost_by: {found_rate: {factor: 100}}, boost_by_distance: {location: {origin: [37, -122], scale: "1000mi"}}
|
202
|
+
assert_order "san", ["San Francisco", "San Antonio", "San Marino"], boost_by: {found_rate: {factor: 100}}, boost_by_distance: {location: {origin: [37, -122], scale: "1000mi", factor: 100}}
|
203
|
+
end
|
204
|
+
|
194
205
|
def test_boost_by_indices
|
195
206
|
skip if cequel?
|
196
207
|
|
data/test/ci/before_install.sh
CHANGED
@@ -14,4 +14,4 @@ fi
|
|
14
14
|
tar -xvf elasticsearch-$ELASTICSEARCH_VERSION.tar.gz
|
15
15
|
cd elasticsearch-$ELASTICSEARCH_VERSION/bin
|
16
16
|
./elasticsearch -d
|
17
|
-
wget -O- --waitretry=1 --tries=
|
17
|
+
wget -O- --waitretry=1 --tries=60 --retry-connrefused -v http://127.0.0.1:9200/
|
data/test/multi_search_test.rb
CHANGED
@@ -33,4 +33,12 @@ class MultiSearchTest < Minitest::Test
|
|
33
33
|
Searchkick.multi_search([products], retry_misspellings: true)
|
34
34
|
assert_equal ["abc", "abd"], products.map(&:name)
|
35
35
|
end
|
36
|
+
|
37
|
+
def test_error
|
38
|
+
products = Product.search("*", order: {bad_column: :asc}, execute: false)
|
39
|
+
Searchkick.multi_search([products])
|
40
|
+
assert products.error
|
41
|
+
error = assert_raises(Searchkick::Error) { products.results }
|
42
|
+
assert_equal error.message, "Query error - use the error method to view it"
|
43
|
+
end
|
36
44
|
end
|
data/test/sql_test.rb
CHANGED
@@ -211,4 +211,11 @@ class SqlTest < Minitest::Test
|
|
211
211
|
assert records.first.association(associations[klass].first).loaded?
|
212
212
|
end
|
213
213
|
end
|
214
|
+
|
215
|
+
def test_scope_results
|
216
|
+
skip unless defined?(ActiveRecord)
|
217
|
+
|
218
|
+
store_names ["Product A", "Product B"]
|
219
|
+
assert_search "product", ["Product A"], scope_results: ->(r) { r.where(name: "Product A") }
|
220
|
+
end
|
214
221
|
end
|
data/test/suggest_test.rb
CHANGED
@@ -68,6 +68,7 @@ class SuggestTest < Minitest::Test
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_multiple_models
|
71
|
+
skip # flaky test
|
71
72
|
store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
|
72
73
|
assert_equal "how big is a tiger shark", Searchkick.search("How Big is a Tigre Shar", suggest: [:name], fields: [:name]).suggestions.first
|
73
74
|
end
|
@@ -77,6 +78,10 @@ class SuggestTest < Minitest::Test
|
|
77
78
|
assert_raises(ArgumentError) { Searchkick.search("How Big is a Tigre Shar", suggest: true) }
|
78
79
|
end
|
79
80
|
|
81
|
+
def test_star
|
82
|
+
assert_equal [], Product.search("*", suggest: true).suggestions
|
83
|
+
end
|
84
|
+
|
80
85
|
protected
|
81
86
|
|
82
87
|
def assert_suggest(term, expected, options = {})
|
data/test/test_helper.rb
CHANGED
data/test/where_test.rb
CHANGED
@@ -189,6 +189,22 @@ class WhereTest < Minitest::Test
|
|
189
189
|
assert_search "san", ["San Francisco"], where: {location: {top_left: {lat: 38, lon: -123}, bottom_right: {lat: 37, lon: -122}}}
|
190
190
|
end
|
191
191
|
|
192
|
+
def test_top_right_bottom_left
|
193
|
+
store [
|
194
|
+
{name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
|
195
|
+
{name: "San Antonio", latitude: 29.4167, longitude: -98.5000}
|
196
|
+
]
|
197
|
+
assert_search "san", ["San Francisco"], where: {location: {top_right: [38, -122], bottom_left: [37, -123]}}
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_top_right_bottom_left_hash
|
201
|
+
store [
|
202
|
+
{name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
|
203
|
+
{name: "San Antonio", latitude: 29.4167, longitude: -98.5000}
|
204
|
+
]
|
205
|
+
assert_search "san", ["San Francisco"], where: {location: {top_right: {lat: 38, lon: -122}, bottom_left: {lat: 37, lon: -123}}}
|
206
|
+
end
|
207
|
+
|
192
208
|
def test_multiple_locations
|
193
209
|
store [
|
194
210
|
{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.
|
4
|
+
version: 2.5.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:
|
11
|
+
date: 2018-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- ".gitignore"
|
105
105
|
- ".travis.yml"
|
106
106
|
- CHANGELOG.md
|
107
|
+
- CONTRIBUTING.md
|
107
108
|
- Gemfile
|
108
109
|
- LICENSE.txt
|
109
110
|
- README.md
|
@@ -112,6 +113,7 @@ files:
|
|
112
113
|
- benchmark/benchmark.rb
|
113
114
|
- lib/searchkick.rb
|
114
115
|
- lib/searchkick/bulk_reindex_job.rb
|
116
|
+
- lib/searchkick/hash_wrapper.rb
|
115
117
|
- lib/searchkick/index.rb
|
116
118
|
- lib/searchkick/index_options.rb
|
117
119
|
- lib/searchkick/indexer.rb
|