searchkick 4.0.0 → 4.0.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +19 -6
- data/lib/searchkick.rb +1 -1
- data/lib/searchkick/index_options.rb +16 -7
- data/lib/searchkick/logging.rb +3 -1
- data/lib/searchkick/query.rb +35 -6
- data/lib/searchkick/results.rb +33 -6
- data/lib/searchkick/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf57aedd7e85672dd9bc5f60f22719af2f5cb1797e1afa8f16c426ea291dd4db
|
4
|
+
data.tar.gz: 54542375eb2cb3c5b7917304cd7e68530a826641c8f1a556af551083964a8849
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5029c0fc16c139f4358786400aad6c36751c6335221e9985f66e5ac308152817db9cd41e709ff6f2b18e731842c85b1a17b9aaeb649b7895393e7001aad8426d
|
7
|
+
data.tar.gz: 63bd24be4b7b41c539c0acd0c0c8bc6502095707539d235f8ddc9fc49aa2634c2fb602e40ce77e771d151945118d2c4da73d26bcda8c4290d61eae4b6c44b468
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1044,10 +1044,10 @@ Visit the Shield page and reset your password. You’ll need to add the username
|
|
1044
1044
|
heroku config:get FOUNDELASTICSEARCH_URL
|
1045
1045
|
```
|
1046
1046
|
|
1047
|
-
And add `elastic:password@` right after `https
|
1047
|
+
And add `elastic:password@` right after `https://` and add port `9243` at the end:
|
1048
1048
|
|
1049
1049
|
```sh
|
1050
|
-
heroku config:set ELASTICSEARCH_URL=https://elastic:password@12345.us-east-1.aws.found.io
|
1050
|
+
heroku config:set ELASTICSEARCH_URL=https://elastic:password@12345.us-east-1.aws.found.io:9243
|
1051
1051
|
```
|
1052
1052
|
|
1053
1053
|
Then deploy and reindex:
|
@@ -1396,10 +1396,8 @@ Create a custom mapping:
|
|
1396
1396
|
```ruby
|
1397
1397
|
class Product < ApplicationRecord
|
1398
1398
|
searchkick mappings: {
|
1399
|
-
|
1400
|
-
|
1401
|
-
name: {type: "keyword"}
|
1402
|
-
}
|
1399
|
+
properties: {
|
1400
|
+
name: {type: "keyword"}
|
1403
1401
|
}
|
1404
1402
|
}
|
1405
1403
|
end
|
@@ -1479,6 +1477,21 @@ Boost specific models with:
|
|
1479
1477
|
indices_boost: {Category => 2, Product => 1}
|
1480
1478
|
```
|
1481
1479
|
|
1480
|
+
## Scroll API
|
1481
|
+
|
1482
|
+
To retrieve a very large number of results, use the [scroll API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html).
|
1483
|
+
|
1484
|
+
```ruby
|
1485
|
+
products = Product.search "*", scroll: "1m"
|
1486
|
+
while products.any?
|
1487
|
+
# do something ...
|
1488
|
+
|
1489
|
+
products = products.scroll
|
1490
|
+
end
|
1491
|
+
```
|
1492
|
+
|
1493
|
+
You should call `scroll` on each new set of results, not the original result.
|
1494
|
+
|
1482
1495
|
## Nested Data
|
1483
1496
|
|
1484
1497
|
To query nested data, use dot notation.
|
data/lib/searchkick.rb
CHANGED
@@ -5,18 +5,29 @@ module Searchkick
|
|
5
5
|
language = options[:language]
|
6
6
|
language = language.call if language.respond_to?(:call)
|
7
7
|
|
8
|
+
below62 = Searchkick.server_below?("6.2.0")
|
9
|
+
below70 = Searchkick.server_below?("7.0.0")
|
10
|
+
|
11
|
+
if below70
|
12
|
+
index_type = options[:_type]
|
13
|
+
index_type = index_type.call if index_type.respond_to?(:call)
|
14
|
+
end
|
15
|
+
|
16
|
+
custom_mapping = options[:mapping] || {}
|
17
|
+
if below70 && custom_mapping.keys.map(&:to_sym).include?(:properties)
|
18
|
+
# add type
|
19
|
+
custom_mapping = {index_type => custom_mapping}
|
20
|
+
end
|
21
|
+
|
8
22
|
if options[:mappings] && !options[:merge_mappings]
|
9
23
|
settings = options[:settings] || {}
|
10
|
-
mappings =
|
24
|
+
mappings = custom_mapping
|
11
25
|
else
|
12
|
-
below62 = Searchkick.server_below?("6.2.0")
|
13
|
-
below70 = Searchkick.server_below?("7.0.0")
|
14
26
|
|
15
27
|
default_type = "text"
|
16
28
|
default_analyzer = :searchkick_index
|
17
29
|
keyword_mapping = {type: "keyword"}
|
18
30
|
|
19
|
-
all = options.key?(:_all) ? options[:_all] : false
|
20
31
|
index_true_value = true
|
21
32
|
index_false_value = false
|
22
33
|
|
@@ -412,12 +423,10 @@ module Searchkick
|
|
412
423
|
}
|
413
424
|
|
414
425
|
if below70
|
415
|
-
index_type = options[:_type]
|
416
|
-
index_type = index_type.call if index_type.respond_to?(:call)
|
417
426
|
mappings = {index_type => mappings}
|
418
427
|
end
|
419
428
|
|
420
|
-
mappings = mappings.symbolize_keys.deep_merge(
|
429
|
+
mappings = mappings.symbolize_keys.deep_merge(custom_mapping.symbolize_keys)
|
421
430
|
end
|
422
431
|
|
423
432
|
{
|
data/lib/searchkick/logging.rb
CHANGED
@@ -167,7 +167,9 @@ module Searchkick
|
|
167
167
|
|
168
168
|
# no easy way to tell which host the client will use
|
169
169
|
host = Searchkick.client.transport.hosts.first
|
170
|
-
|
170
|
+
params = ["pretty"]
|
171
|
+
params << "scroll=#{payload[:query][:scroll]}" if payload[:query][:scroll]
|
172
|
+
debug " #{color(name, YELLOW, true)} curl #{host[:protocol]}://#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?#{params.join('&')} -H 'Content-Type: application/json' -d '#{payload[:query][:body].to_json}'"
|
171
173
|
end
|
172
174
|
|
173
175
|
def request(event)
|
data/lib/searchkick/query.rb
CHANGED
@@ -12,14 +12,14 @@ module Searchkick
|
|
12
12
|
:took, :error, :model_name, :entry_name, :total_count, :total_entries,
|
13
13
|
:current_page, :per_page, :limit_value, :padding, :total_pages, :num_pages,
|
14
14
|
:offset_value, :offset, :previous_page, :prev_page, :next_page, :first_page?, :last_page?,
|
15
|
-
:out_of_range?, :hits, :response, :to_a, :first
|
15
|
+
:out_of_range?, :hits, :response, :to_a, :first, :scroll
|
16
16
|
|
17
17
|
def initialize(klass, term = "*", **options)
|
18
18
|
unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost,
|
19
19
|
:boost_by, :boost_by_distance, :boost_by_recency, :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, :models, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile,
|
22
|
-
:request_params, :routing, :scope_results, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where]
|
22
|
+
:request_params, :routing, :scope_results, :scroll, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where]
|
23
23
|
raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
|
24
24
|
|
25
25
|
term = term.to_s
|
@@ -60,7 +60,8 @@ module Searchkick
|
|
60
60
|
if options[:models]
|
61
61
|
@index_mapping = {}
|
62
62
|
Array(options[:models]).each do |model|
|
63
|
-
|
63
|
+
# there can be multiple models per index name due to inheritance - see #1259
|
64
|
+
(@index_mapping[model.searchkick_index.name] ||= []) << model
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -81,6 +82,7 @@ module Searchkick
|
|
81
82
|
}
|
82
83
|
params[:type] = @type if @type
|
83
84
|
params[:routing] = @routing if @routing
|
85
|
+
params[:scroll] = @scroll if @scroll
|
84
86
|
params.merge!(options[:request_params]) if options[:request_params]
|
85
87
|
params
|
86
88
|
end
|
@@ -108,7 +110,9 @@ module Searchkick
|
|
108
110
|
# no easy way to tell which host the client will use
|
109
111
|
host = Searchkick.client.transport.hosts.first
|
110
112
|
credentials = host[:user] || host[:password] ? "#{host[:user]}:#{host[:password]}@" : nil
|
111
|
-
|
113
|
+
params = ["pretty"]
|
114
|
+
params << "scroll=#{options[:scroll]}" if options[:scroll]
|
115
|
+
"curl #{host[:protocol]}://#{credentials}#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?#{params.join('&')} -H 'Content-Type: application/json' -d '#{query[:body].to_json}'"
|
112
116
|
end
|
113
117
|
|
114
118
|
def handle_response(response)
|
@@ -127,7 +131,9 @@ module Searchkick
|
|
127
131
|
term: term,
|
128
132
|
scope_results: options[:scope_results],
|
129
133
|
total_entries: options[:total_entries],
|
130
|
-
index_mapping: @index_mapping
|
134
|
+
index_mapping: @index_mapping,
|
135
|
+
suggest: options[:suggest],
|
136
|
+
scroll: options[:scroll]
|
131
137
|
}
|
132
138
|
|
133
139
|
if options[:debug]
|
@@ -228,6 +234,7 @@ module Searchkick
|
|
228
234
|
per_page = (options[:limit] || options[:per_page] || 10_000).to_i
|
229
235
|
padding = [options[:padding].to_i, 0].max
|
230
236
|
offset = options[:offset] || (page - 1) * per_page + padding
|
237
|
+
scroll = options[:scroll]
|
231
238
|
|
232
239
|
# model and eager loading
|
233
240
|
load = options[:load].nil? ? true : options[:load]
|
@@ -423,6 +430,21 @@ module Searchkick
|
|
423
430
|
where[:type] = [options[:type] || klass].flatten.map { |v| searchkick_index.klass_document_type(v, true) }
|
424
431
|
end
|
425
432
|
|
433
|
+
models = Array(options[:models])
|
434
|
+
if models.any? { |m| m != m.searchkick_klass }
|
435
|
+
warn "[searchkick] WARNING: Passing child models to models option throws off hits and pagination - use type option instead"
|
436
|
+
|
437
|
+
# uncomment once aliases are supported with _index
|
438
|
+
# index_type_or =
|
439
|
+
# models.map do |m|
|
440
|
+
# v = {_index: m.searchkick_index.name}
|
441
|
+
# v[:type] = m.searchkick_index.klass_document_type(m, true) if m != m.searchkick_klass
|
442
|
+
# v
|
443
|
+
# end
|
444
|
+
|
445
|
+
# where[:or] = Array(where[:or]) + [index_type_or]
|
446
|
+
end
|
447
|
+
|
426
448
|
# start everything as efficient filters
|
427
449
|
# move to post_filters as aggs demand
|
428
450
|
filters = where_filters(where)
|
@@ -480,7 +502,7 @@ module Searchkick
|
|
480
502
|
pagination_options = options[:page] || options[:limit] || options[:per_page] || options[:offset] || options[:padding]
|
481
503
|
if !options[:body] || pagination_options
|
482
504
|
payload[:size] = per_page
|
483
|
-
payload[:from] = offset
|
505
|
+
payload[:from] = offset if offset > 0
|
484
506
|
end
|
485
507
|
|
486
508
|
# type
|
@@ -497,11 +519,18 @@ module Searchkick
|
|
497
519
|
# run block
|
498
520
|
options[:block].call(payload) if options[:block]
|
499
521
|
|
522
|
+
# scroll optimization when interating over all docs
|
523
|
+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html
|
524
|
+
if options[:scroll] && payload[:query] == {match_all: {}}
|
525
|
+
payload[:sort] ||= ["_doc"]
|
526
|
+
end
|
527
|
+
|
500
528
|
@body = payload
|
501
529
|
@page = page
|
502
530
|
@per_page = per_page
|
503
531
|
@padding = padding
|
504
532
|
@load = load
|
533
|
+
@scroll = scroll
|
505
534
|
end
|
506
535
|
|
507
536
|
def set_fields
|
data/lib/searchkick/results.rb
CHANGED
@@ -26,15 +26,19 @@ module Searchkick
|
|
26
26
|
results = {}
|
27
27
|
|
28
28
|
hits.group_by { |hit, _| hit["_index"] }.each do |index, grouped_hits|
|
29
|
-
|
29
|
+
klasses =
|
30
30
|
if @klass
|
31
|
-
@klass
|
31
|
+
[@klass]
|
32
32
|
else
|
33
33
|
index_alias = index.split("_")[0..-2].join("_")
|
34
|
-
(options[:index_mapping] || {})[index_alias]
|
34
|
+
Array((options[:index_mapping] || {})[index_alias])
|
35
35
|
end
|
36
|
-
raise Searchkick::Error, "Unknown model for index: #{index}" unless
|
37
|
-
|
36
|
+
raise Searchkick::Error, "Unknown model for index: #{index}" unless klasses.any?
|
37
|
+
|
38
|
+
results[index] = {}
|
39
|
+
klasses.each do |klass|
|
40
|
+
results[index].merge!(results_query(klass, grouped_hits).to_a.index_by { |r| r.id.to_s })
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
missing_ids = []
|
@@ -90,7 +94,7 @@ module Searchkick
|
|
90
94
|
def suggestions
|
91
95
|
if response["suggest"]
|
92
96
|
response["suggest"].values.flat_map { |v| v.first["options"] }.sort_by { |o| -o["score"] }.map { |o| o["text"] }.uniq
|
93
|
-
elsif options[:term] == "*"
|
97
|
+
elsif options[:suggest] || options[:term] == "*" # TODO remove 2nd term
|
94
98
|
[]
|
95
99
|
else
|
96
100
|
raise "Pass `suggest: true` to the search method for suggestions"
|
@@ -217,6 +221,29 @@ module Searchkick
|
|
217
221
|
@options[:misspellings]
|
218
222
|
end
|
219
223
|
|
224
|
+
def scroll_id
|
225
|
+
@response["_scroll_id"]
|
226
|
+
end
|
227
|
+
|
228
|
+
def scroll
|
229
|
+
raise Searchkick::Error, "Pass `scroll` option to the search method for scrolling" unless scroll_id
|
230
|
+
|
231
|
+
params = {
|
232
|
+
scroll: options[:scroll],
|
233
|
+
scroll_id: scroll_id
|
234
|
+
}
|
235
|
+
|
236
|
+
begin
|
237
|
+
Searchkick::Results.new(@klass, Searchkick.client.scroll(params), @options)
|
238
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
|
239
|
+
if e.class.to_s =~ /NotFound/ && e.message =~ /search_context_missing_exception/i
|
240
|
+
raise Searchkick::Error, "Scroll id has expired"
|
241
|
+
else
|
242
|
+
raise e
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
220
247
|
private
|
221
248
|
|
222
249
|
def results_query(records, hits)
|
data/lib/searchkick/version.rb
CHANGED
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: 4.0.
|
4
|
+
version: 4.0.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: 2019-
|
11
|
+
date: 2019-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|