searchkick 4.0.0 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|