search_flip 3.0.0.beta2 → 3.0.0
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/.rubocop.yml +3 -0
- data/.travis.yml +3 -1
- data/CHANGELOG.md +7 -0
- data/README.md +33 -1
- data/lib/search_flip.rb +8 -0
- data/lib/search_flip/aggregatable.rb +9 -1
- data/lib/search_flip/aggregation.rb +30 -5
- data/lib/search_flip/bulk.rb +5 -5
- data/lib/search_flip/config.rb +2 -1
- data/lib/search_flip/criteria.rb +83 -294
- data/lib/search_flip/customable.rb +34 -0
- data/lib/search_flip/explainable.rb +28 -0
- data/lib/search_flip/highlightable.rb +49 -0
- data/lib/search_flip/http_client.rb +6 -2
- data/lib/search_flip/index.rb +1 -1
- data/lib/search_flip/null_instrumenter.rb +21 -0
- data/lib/search_flip/paginatable.rb +93 -0
- data/lib/search_flip/response.rb +1 -1
- data/lib/search_flip/sortable.rb +69 -0
- data/lib/search_flip/sourceable.rb +30 -0
- data/lib/search_flip/version.rb +1 -1
- data/search_flip.gemspec +2 -1
- data/spec/search_flip/aggregation_spec.rb +178 -24
- data/spec/search_flip/criteria_spec.rb +42 -0
- data/spec/search_flip/null_instrumenter_spec.rb +43 -0
- metadata +28 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa160794662cedbe89066113cb845c84daaee29451ba8c8ba4f9183689b42582
|
|
4
|
+
data.tar.gz: b55133b0f4f90f61db0b76042c1223a17035f277ba221a00c0f665512e1cb170
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb2e1e1fd39d64639b9a1f542997e6a00c6238226144fbd32f45f0023f93aa827a1c54597fb71f993090c9d78bcb8b92bbac3a5185d584f26cbeb2e1c4b1a641
|
|
7
|
+
data.tar.gz: caea53f0332131113662b24818268bfd5e3cf537ad04b07b853aba600fc4edd417d1783fd3a1d37fd6d848a75df21ec2e3d39c45a8dafab5fdc515d6f8ca43a9
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
|
@@ -5,9 +5,11 @@ env:
|
|
|
5
5
|
- ES_IMAGE=elasticsearch:5.4
|
|
6
6
|
- ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:6.7.0
|
|
7
7
|
- ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:7.0.0
|
|
8
|
-
- ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:7.
|
|
8
|
+
- ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:7.6.0
|
|
9
9
|
rvm:
|
|
10
|
+
- ruby-2.5.3
|
|
10
11
|
- ruby-2.6.2
|
|
12
|
+
- ruby-2.7.1
|
|
11
13
|
before_install:
|
|
12
14
|
- docker-compose up -d
|
|
13
15
|
- sleep 10
|
data/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,13 @@
|
|
|
21
21
|
* `Connection#freeze_index`, `Connection#unfreeze_index`, `Index#freeze_index`
|
|
22
22
|
and `Index#unfreeze_index` added
|
|
23
23
|
* Added `SearchFlip::Result.from_hit`
|
|
24
|
+
* Added support for `source`, `sort`, `page`, `per`, `paginate`, `explain`, and
|
|
25
|
+
`highlight` to aggregations
|
|
26
|
+
* Added support for instrumentation
|
|
27
|
+
|
|
28
|
+
## v2.3.2
|
|
29
|
+
|
|
30
|
+
* Remove ruby 2.7 warnings
|
|
24
31
|
|
|
25
32
|
## v2.3.1
|
|
26
33
|
|
data/README.md
CHANGED
|
@@ -274,7 +274,7 @@ CommentIndex.create(Comment.first, { bulk_max_mb: 100 }, routing: "routing_key")
|
|
|
274
274
|
CommentIndex.update(Comment.first, ...)
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
Checkout the
|
|
277
|
+
Checkout the Elasticsearch [Bulk API] docs for more info as well as
|
|
278
278
|
[SearchFlip::Bulk](http://www.rubydoc.info/github/mrkamel/search_flip/SearchFlip/Bulk)
|
|
279
279
|
for a complete list of available options to control the bulk indexing of
|
|
280
280
|
SearchFlip.
|
|
@@ -505,6 +505,14 @@ end
|
|
|
505
505
|
query.aggregations(:average_price).average_price.value
|
|
506
506
|
```
|
|
507
507
|
|
|
508
|
+
Even various criteria for top hits aggregations can be specified elegantly:
|
|
509
|
+
|
|
510
|
+
```ruby
|
|
511
|
+
query = ProductIndex.aggregate(sponsored: { top_hits: {} }) do |aggregation|
|
|
512
|
+
aggregation.sort(:rank).highlight(:title).source([:id, :title])
|
|
513
|
+
end
|
|
514
|
+
```
|
|
515
|
+
|
|
508
516
|
Checkout [Aggregatable](http://www.rubydoc.info/github/mrkamel/search_flip/SearchFlip/Aggregatable)
|
|
509
517
|
as well as [Aggregation](http://www.rubydoc.info/github/mrkamel/search_flip/SearchFlip/Aggregation)
|
|
510
518
|
for a complete API reference.
|
|
@@ -782,6 +790,30 @@ end
|
|
|
782
790
|
|
|
783
791
|
These options will be passed whenever records get indexed, deleted, etc.
|
|
784
792
|
|
|
793
|
+
## Instrumentation
|
|
794
|
+
|
|
795
|
+
SearchFlip supports instrumentation for request execution via
|
|
796
|
+
`ActiveSupport::Notifications` compatible instrumenters to e.g. allow global
|
|
797
|
+
performance tracing, etc.
|
|
798
|
+
|
|
799
|
+
To use instrumentation, configure the instrumenter:
|
|
800
|
+
|
|
801
|
+
```ruby
|
|
802
|
+
SearchFlip::Config[:instrumenter] = ActiveSupport::Notifications.notifier
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
Subsequently, you can subscribe to notifcations for `request.search_flip`:
|
|
806
|
+
|
|
807
|
+
```ruby
|
|
808
|
+
ActiveSupport::Notifications.subscribe("request.search_flip") do |name, start, finish, id, payload|
|
|
809
|
+
payload[:index] # the index class
|
|
810
|
+
payload[:request] # the request hash sent to Elasticsearch
|
|
811
|
+
payload[:response] # the SearchFlip::Response object or nil in case of errors
|
|
812
|
+
end
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
A notification will be send for every request that is sent to Elasticsearch.
|
|
816
|
+
|
|
785
817
|
## Non-ActiveRecord models
|
|
786
818
|
|
|
787
819
|
SearchFlip ships with built-in support for ActiveRecord models, but using
|
data/lib/search_flip.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "ruby2_keywords"
|
|
1
2
|
require "forwardable"
|
|
2
3
|
require "http"
|
|
3
4
|
require "hashie"
|
|
@@ -6,6 +7,7 @@ require "oj"
|
|
|
6
7
|
require "set"
|
|
7
8
|
|
|
8
9
|
require "search_flip/version"
|
|
10
|
+
require "search_flip/null_instrumenter"
|
|
9
11
|
require "search_flip/helper"
|
|
10
12
|
require "search_flip/exceptions"
|
|
11
13
|
require "search_flip/json"
|
|
@@ -14,6 +16,12 @@ require "search_flip/config"
|
|
|
14
16
|
require "search_flip/connection"
|
|
15
17
|
require "search_flip/bulk"
|
|
16
18
|
require "search_flip/filterable"
|
|
19
|
+
require "search_flip/customable"
|
|
20
|
+
require "search_flip/explainable"
|
|
21
|
+
require "search_flip/highlightable"
|
|
22
|
+
require "search_flip/paginatable"
|
|
23
|
+
require "search_flip/sortable"
|
|
24
|
+
require "search_flip/sourceable"
|
|
17
25
|
require "search_flip/post_filterable"
|
|
18
26
|
require "search_flip/aggregatable"
|
|
19
27
|
require "search_flip/aggregation"
|
|
@@ -57,7 +57,15 @@ module SearchFlip
|
|
|
57
57
|
if block
|
|
58
58
|
aggregation = yield(SearchFlip::Aggregation.new(target: target))
|
|
59
59
|
|
|
60
|
-
field_or_hash.is_a?(Hash)
|
|
60
|
+
if field_or_hash.is_a?(Hash)
|
|
61
|
+
aggregation_hash = field_or_hash.values.first
|
|
62
|
+
aggregation_hash = aggregation_hash[:top_hits] if aggregation_hash.is_a?(Hash) && aggregation_hash.key?(:top_hits)
|
|
63
|
+
aggregation_hash = aggregation_hash["top_hits"] if aggregation_hash.is_a?(Hash) && aggregation_hash.key?("top_hits")
|
|
64
|
+
|
|
65
|
+
aggregation_hash.merge!(aggregation.to_hash)
|
|
66
|
+
else
|
|
67
|
+
hash[field_or_hash].merge!(aggregation.to_hash)
|
|
68
|
+
end
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
criteria.aggregation_values = (aggregation_values || {}).merge(hash)
|
|
@@ -6,6 +6,12 @@ module SearchFlip
|
|
|
6
6
|
class Aggregation
|
|
7
7
|
include Filterable
|
|
8
8
|
include Aggregatable
|
|
9
|
+
include Paginatable
|
|
10
|
+
include Highlightable
|
|
11
|
+
include Explainable
|
|
12
|
+
include Sourceable
|
|
13
|
+
include Sortable
|
|
14
|
+
include Customable
|
|
9
15
|
|
|
10
16
|
attr_reader :target
|
|
11
17
|
|
|
@@ -40,6 +46,15 @@ module SearchFlip
|
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
|
|
49
|
+
res.update(from: offset_value_with_default, size: limit_value_with_default) if offset_value || limit_value
|
|
50
|
+
|
|
51
|
+
res[:explain] = explain_value unless explain_value.nil?
|
|
52
|
+
res[:highlight] = highlight_values if highlight_values
|
|
53
|
+
res[:sort] = sort_values if sort_values
|
|
54
|
+
res[:_source] = source_value unless source_value.nil?
|
|
55
|
+
|
|
56
|
+
res.update(custom_value) if custom_value
|
|
57
|
+
|
|
43
58
|
res
|
|
44
59
|
end
|
|
45
60
|
|
|
@@ -56,11 +71,10 @@ module SearchFlip
|
|
|
56
71
|
|
|
57
72
|
fresh.tap do |aggregation|
|
|
58
73
|
unsupported_methods = [
|
|
59
|
-
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :
|
|
60
|
-
:
|
|
61
|
-
:
|
|
62
|
-
:
|
|
63
|
-
:search_type_value, :routing_value
|
|
74
|
+
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :scroll_args,
|
|
75
|
+
:suggest_values, :includes_values, :preload_values, :eager_load_values, :post_must_values,
|
|
76
|
+
:post_must_not_values, :post_filter_values, :preference_value, :search_type_value,
|
|
77
|
+
:routing_value
|
|
64
78
|
]
|
|
65
79
|
|
|
66
80
|
unsupported_methods.each do |unsupported_method|
|
|
@@ -69,10 +83,19 @@ module SearchFlip
|
|
|
69
83
|
end
|
|
70
84
|
end
|
|
71
85
|
|
|
86
|
+
aggregation.source_value = other.source_value if other.source_value
|
|
87
|
+
aggregation.offset_value = other.offset_value if other.offset_value
|
|
88
|
+
aggregation.limit_value = other.limit_value if other.limit_value
|
|
89
|
+
aggregation.scroll_args = other.scroll_args if other.scroll_args
|
|
90
|
+
aggregation.explain_value = other.explain_value unless other.explain_value.nil?
|
|
91
|
+
|
|
92
|
+
aggregation.sort_values = (aggregation.sort_values || []) + other.sort_values if other.sort_values
|
|
72
93
|
aggregation.must_values = (aggregation.must_values || []) + other.must_values if other.must_values
|
|
73
94
|
aggregation.must_not_values = (aggregation.must_not_values || []) + other.must_not_values if other.must_not_values
|
|
74
95
|
aggregation.filter_values = (aggregation.filter_values || []) + other.filter_values if other.filter_values
|
|
75
96
|
|
|
97
|
+
aggregation.highlight_values = (aggregation.highlight_values || {}).merge(other.highlight_values) if other.highlight_values
|
|
98
|
+
aggregation.custom_value = (aggregation.custom_value || {}).merge(other.custom_value) if other.custom_value
|
|
76
99
|
aggregation.aggregation_values = (aggregation.aggregation_values || {}).merge(other.aggregation_values) if other.aggregation_values
|
|
77
100
|
end
|
|
78
101
|
end
|
|
@@ -89,6 +112,8 @@ module SearchFlip
|
|
|
89
112
|
end
|
|
90
113
|
end
|
|
91
114
|
|
|
115
|
+
ruby2_keywords :method_missing
|
|
116
|
+
|
|
92
117
|
# @api private
|
|
93
118
|
#
|
|
94
119
|
# Simply dups the object for api compatability.
|
data/lib/search_flip/bulk.rb
CHANGED
|
@@ -77,7 +77,7 @@ module SearchFlip
|
|
|
77
77
|
# and versioning
|
|
78
78
|
|
|
79
79
|
def index(id, object, options = {})
|
|
80
|
-
perform
|
|
80
|
+
perform(:index, id, SearchFlip::JSON.generate(object), options)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
# @api private
|
|
@@ -86,7 +86,7 @@ module SearchFlip
|
|
|
86
86
|
#
|
|
87
87
|
# @see #index
|
|
88
88
|
|
|
89
|
-
def import(*args)
|
|
89
|
+
ruby2_keywords def import(*args)
|
|
90
90
|
index(*args)
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -100,7 +100,7 @@ module SearchFlip
|
|
|
100
100
|
# and versioning
|
|
101
101
|
|
|
102
102
|
def create(id, object, options = {})
|
|
103
|
-
perform
|
|
103
|
+
perform(:create, id, SearchFlip::JSON.generate(object), options)
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
# @api private
|
|
@@ -113,7 +113,7 @@ module SearchFlip
|
|
|
113
113
|
# and versioning
|
|
114
114
|
|
|
115
115
|
def update(id, object, options = {})
|
|
116
|
-
perform
|
|
116
|
+
perform(:update, id, SearchFlip::JSON.generate(object), options)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
# @api private
|
|
@@ -125,7 +125,7 @@ module SearchFlip
|
|
|
125
125
|
# and versioning
|
|
126
126
|
|
|
127
127
|
def delete(id, options = {})
|
|
128
|
-
perform
|
|
128
|
+
perform(:delete, id, nil, options)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
private
|
data/lib/search_flip/config.rb
CHANGED
data/lib/search_flip/criteria.rb
CHANGED
|
@@ -13,15 +13,20 @@ module SearchFlip
|
|
|
13
13
|
# CommentIndex.sort("_doc").find_each { |comment| "..." }
|
|
14
14
|
|
|
15
15
|
class Criteria
|
|
16
|
+
include Sortable
|
|
17
|
+
include Sourceable
|
|
18
|
+
include Highlightable
|
|
19
|
+
include Explainable
|
|
20
|
+
include Paginatable
|
|
21
|
+
include Customable
|
|
16
22
|
include Filterable
|
|
17
23
|
include PostFilterable
|
|
18
24
|
include Aggregatable
|
|
19
25
|
extend Forwardable
|
|
20
26
|
|
|
21
|
-
attr_accessor :target, :profile_value, :source_value, :
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
:routing_value, :track_total_hits_value, :explain_value
|
|
27
|
+
attr_accessor :target, :profile_value, :source_value, :suggest_values, :includes_values,
|
|
28
|
+
:eager_load_values, :preload_values, :failsafe_value, :scroll_args, :terminate_after_value,
|
|
29
|
+
:timeout_value, :preference_value, :search_type_value, :routing_value, :track_total_hits_value
|
|
25
30
|
|
|
26
31
|
# Creates a new criteria while merging the attributes (constraints,
|
|
27
32
|
# settings, etc) of the current criteria with the attributes of another one
|
|
@@ -71,22 +76,6 @@ module SearchFlip
|
|
|
71
76
|
end
|
|
72
77
|
end
|
|
73
78
|
|
|
74
|
-
# Specifies whether or not to enable explanation for each hit on how
|
|
75
|
-
# its score was computed.
|
|
76
|
-
#
|
|
77
|
-
# @example
|
|
78
|
-
# CommentIndex.explain(true)
|
|
79
|
-
#
|
|
80
|
-
# @param value [Boolean] The value for explain
|
|
81
|
-
#
|
|
82
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
83
|
-
|
|
84
|
-
def explain(value)
|
|
85
|
-
fresh.tap do |criteria|
|
|
86
|
-
criteria.explain_value = value
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
79
|
# Specifies if or how many hits should be counted/tracked. Check out the
|
|
91
80
|
# elasticsearch docs for futher details.
|
|
92
81
|
#
|
|
@@ -217,7 +206,7 @@ module SearchFlip
|
|
|
217
206
|
#
|
|
218
207
|
# @return [SearchFlip::Criteria] Simply returns self
|
|
219
208
|
|
|
220
|
-
def with_settings(*args)
|
|
209
|
+
ruby2_keywords def with_settings(*args)
|
|
221
210
|
fresh.tap do |criteria|
|
|
222
211
|
criteria.target = target.with_settings(*args)
|
|
223
212
|
end
|
|
@@ -230,81 +219,46 @@ module SearchFlip
|
|
|
230
219
|
# @return [Hash] The generated request object
|
|
231
220
|
|
|
232
221
|
def request
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
res.update(from: offset_value_with_default, size: limit_value_with_default)
|
|
246
|
-
|
|
247
|
-
res[:track_total_hits] = track_total_hits_value unless track_total_hits_value.nil?
|
|
248
|
-
res[:explain] = explain_value unless explain_value.nil?
|
|
249
|
-
res[:timeout] = timeout_value if timeout_value
|
|
250
|
-
res[:terminate_after] = terminate_after_value if terminate_after_value
|
|
251
|
-
res[:highlight] = highlight_values if highlight_values
|
|
252
|
-
res[:suggest] = suggest_values if suggest_values
|
|
253
|
-
res[:sort] = sort_values if sort_values
|
|
254
|
-
res[:aggregations] = aggregation_values if aggregation_values
|
|
255
|
-
|
|
256
|
-
if post_must_values || post_must_not_values || post_filter_values
|
|
257
|
-
res[:post_filter] = {
|
|
258
|
-
bool: {
|
|
259
|
-
must: post_must_values.to_a,
|
|
260
|
-
must_not: post_must_not_values.to_a,
|
|
261
|
-
filter: post_filter_values.to_a
|
|
262
|
-
}.reject { |_, value| value.empty? }
|
|
263
|
-
}
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
res[:_source] = source_value unless source_value.nil?
|
|
267
|
-
res[:profile] = true if profile_value
|
|
222
|
+
@request ||= begin
|
|
223
|
+
res = {}
|
|
224
|
+
|
|
225
|
+
if must_values || must_not_values || filter_values
|
|
226
|
+
res[:query] = {
|
|
227
|
+
bool: {
|
|
228
|
+
must: must_values.to_a,
|
|
229
|
+
must_not: must_not_values.to_a,
|
|
230
|
+
filter: filter_values.to_a
|
|
231
|
+
}.reject { |_, value| value.empty? }
|
|
232
|
+
}
|
|
233
|
+
end
|
|
268
234
|
|
|
269
|
-
|
|
235
|
+
res.update(from: offset_value_with_default, size: limit_value_with_default)
|
|
236
|
+
|
|
237
|
+
res[:track_total_hits] = track_total_hits_value unless track_total_hits_value.nil?
|
|
238
|
+
res[:explain] = explain_value unless explain_value.nil?
|
|
239
|
+
res[:timeout] = timeout_value if timeout_value
|
|
240
|
+
res[:terminate_after] = terminate_after_value if terminate_after_value
|
|
241
|
+
res[:highlight] = highlight_values if highlight_values
|
|
242
|
+
res[:suggest] = suggest_values if suggest_values
|
|
243
|
+
res[:sort] = sort_values if sort_values
|
|
244
|
+
res[:aggregations] = aggregation_values if aggregation_values
|
|
245
|
+
|
|
246
|
+
if post_must_values || post_must_not_values || post_filter_values
|
|
247
|
+
res[:post_filter] = {
|
|
248
|
+
bool: {
|
|
249
|
+
must: post_must_values.to_a,
|
|
250
|
+
must_not: post_must_not_values.to_a,
|
|
251
|
+
filter: post_filter_values.to_a
|
|
252
|
+
}.reject { |_, value| value.empty? }
|
|
253
|
+
}
|
|
254
|
+
end
|
|
270
255
|
|
|
271
|
-
|
|
272
|
-
|
|
256
|
+
res[:_source] = source_value unless source_value.nil?
|
|
257
|
+
res[:profile] = true if profile_value
|
|
273
258
|
|
|
274
|
-
|
|
275
|
-
#
|
|
276
|
-
# @example
|
|
277
|
-
# CommentIndex.highlight([:title, :message])
|
|
278
|
-
# CommentIndex.highlight(:title).highlight(:description)
|
|
279
|
-
# CommentIndex.highlight(:title, require_field_match: false)
|
|
280
|
-
# CommentIndex.highlight(title: { type: "fvh" })
|
|
281
|
-
#
|
|
282
|
-
# @example
|
|
283
|
-
# query = CommentIndex.highlight(:title).search("hello")
|
|
284
|
-
# query.results[0].highlight.title # => "<em>hello</em> world"
|
|
285
|
-
#
|
|
286
|
-
# @param fields [Hash, Array, String, Symbol] The fields to highligt.
|
|
287
|
-
# Supports raw Elasticsearch values by passing a Hash.
|
|
288
|
-
#
|
|
289
|
-
# @param options [Hash] Extra highlighting options. Check out the Elasticsearch
|
|
290
|
-
# docs for further details.
|
|
291
|
-
#
|
|
292
|
-
# @return [SearchFlip::Criteria] A new criteria including the highlighting
|
|
259
|
+
res.update(custom_value) if custom_value
|
|
293
260
|
|
|
294
|
-
|
|
295
|
-
fresh.tap do |criteria|
|
|
296
|
-
criteria.highlight_values = (criteria.highlight_values || {}).merge(options)
|
|
297
|
-
|
|
298
|
-
hash =
|
|
299
|
-
if fields.is_a?(Hash)
|
|
300
|
-
fields
|
|
301
|
-
elsif fields.is_a?(Array)
|
|
302
|
-
fields.each_with_object({}) { |field, h| h[field] = {} }
|
|
303
|
-
else
|
|
304
|
-
{ fields => {} }
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
criteria.highlight_values[:fields] = (criteria.highlight_values[:fields] || {}).merge(hash)
|
|
261
|
+
res
|
|
308
262
|
end
|
|
309
263
|
end
|
|
310
264
|
|
|
@@ -397,24 +351,6 @@ module SearchFlip
|
|
|
397
351
|
true
|
|
398
352
|
end
|
|
399
353
|
|
|
400
|
-
# Use to specify which fields of the source document you want Elasticsearch
|
|
401
|
-
# to return for each matching result.
|
|
402
|
-
#
|
|
403
|
-
# @example
|
|
404
|
-
# CommentIndex.source([:id, :message]).search("hello world")
|
|
405
|
-
# CommentIndex.source(exclude: "description")
|
|
406
|
-
# CommentIndex.source(false)
|
|
407
|
-
#
|
|
408
|
-
# @param value Pass any allowed value to restrict the returned source
|
|
409
|
-
#
|
|
410
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
411
|
-
|
|
412
|
-
def source(value)
|
|
413
|
-
fresh.tap do |criteria|
|
|
414
|
-
criteria.source_value = value
|
|
415
|
-
end
|
|
416
|
-
end
|
|
417
|
-
|
|
418
354
|
# Specify associations of the target model you want to include via
|
|
419
355
|
# ActiveRecord's or other ORM's mechanisms when records get fetched from
|
|
420
356
|
# the database.
|
|
@@ -472,166 +408,6 @@ module SearchFlip
|
|
|
472
408
|
end
|
|
473
409
|
end
|
|
474
410
|
|
|
475
|
-
# Specify the sort order you want Elasticsearch to use for sorting the
|
|
476
|
-
# results. When you call this multiple times, the sort orders are appended
|
|
477
|
-
# to the already existing ones. The sort arguments get passed to
|
|
478
|
-
# Elasticsearch without modifications, such that you can use sort by
|
|
479
|
-
# script, etc here as well.
|
|
480
|
-
#
|
|
481
|
-
# @example Default usage
|
|
482
|
-
# CommentIndex.sort(:user_id, :id)
|
|
483
|
-
#
|
|
484
|
-
# # Same as
|
|
485
|
-
#
|
|
486
|
-
# CommentIndex.sort(:user_id).sort(:id)
|
|
487
|
-
#
|
|
488
|
-
# @example Default hash usage
|
|
489
|
-
# CommentIndex.sort(user_id: "asc").sort(id: "desc")
|
|
490
|
-
#
|
|
491
|
-
# # Same as
|
|
492
|
-
#
|
|
493
|
-
# CommentIndex.sort({ user_id: "asc" }, { id: "desc" })
|
|
494
|
-
#
|
|
495
|
-
# @example Sort by native script
|
|
496
|
-
# CommentIndex.sort("_script" => "sort_script", lang: "native", order: "asc", type: "number")
|
|
497
|
-
#
|
|
498
|
-
# @param args The sort values that get passed to Elasticsearch
|
|
499
|
-
#
|
|
500
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
501
|
-
|
|
502
|
-
def sort(*args)
|
|
503
|
-
fresh.tap do |criteria|
|
|
504
|
-
criteria.sort_values = (sort_values || []) + args
|
|
505
|
-
end
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
alias_method :order, :sort
|
|
509
|
-
|
|
510
|
-
# Specify the sort order you want Elasticsearch to use for sorting the
|
|
511
|
-
# results with already existing sort orders being removed.
|
|
512
|
-
#
|
|
513
|
-
# @example
|
|
514
|
-
# CommentIndex.sort(user_id: "asc").resort(id: "desc")
|
|
515
|
-
#
|
|
516
|
-
# # Same as
|
|
517
|
-
#
|
|
518
|
-
# CommentIndex.sort(id: "desc")
|
|
519
|
-
#
|
|
520
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
521
|
-
#
|
|
522
|
-
# @see #sort See #sort for more details
|
|
523
|
-
|
|
524
|
-
def resort(*args)
|
|
525
|
-
fresh.tap do |criteria|
|
|
526
|
-
criteria.sort_values = args
|
|
527
|
-
end
|
|
528
|
-
end
|
|
529
|
-
|
|
530
|
-
alias_method :reorder, :resort
|
|
531
|
-
|
|
532
|
-
# Adds a fully custom field/section to the request, such that upcoming or
|
|
533
|
-
# minor Elasticsearch features as well as other custom requirements can be
|
|
534
|
-
# used without having yet specialized criteria methods.
|
|
535
|
-
#
|
|
536
|
-
# @note Use with caution, because using #custom will potentiall override
|
|
537
|
-
# other sections like +aggregations+, +query+, +sort+, etc if you use the
|
|
538
|
-
# the same section names.
|
|
539
|
-
#
|
|
540
|
-
# @example
|
|
541
|
-
# CommentIndex.custom(section: { argument: "value" }).request
|
|
542
|
-
# => {:section=>{:argument=>"value"},...}
|
|
543
|
-
#
|
|
544
|
-
# @param hash [Hash] The custom section that is added to the request
|
|
545
|
-
#
|
|
546
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
547
|
-
|
|
548
|
-
def custom(hash)
|
|
549
|
-
fresh.tap do |criteria|
|
|
550
|
-
criteria.custom_value = (custom_value || {}).merge(hash)
|
|
551
|
-
end
|
|
552
|
-
end
|
|
553
|
-
|
|
554
|
-
# Sets the request offset, ie SearchFlip's from parameter that is used
|
|
555
|
-
# to skip results in the result set from being returned.
|
|
556
|
-
#
|
|
557
|
-
# @example
|
|
558
|
-
# CommentIndex.offset(100)
|
|
559
|
-
#
|
|
560
|
-
# @param value [Fixnum] The offset value, ie the number of results that are
|
|
561
|
-
# skipped in the result set
|
|
562
|
-
#
|
|
563
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
564
|
-
|
|
565
|
-
def offset(value)
|
|
566
|
-
fresh.tap do |criteria|
|
|
567
|
-
criteria.offset_value = value.to_i
|
|
568
|
-
end
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
# @api private
|
|
572
|
-
#
|
|
573
|
-
# Returns the offset value or, if not yet set, the default limit value (0).
|
|
574
|
-
#
|
|
575
|
-
# @return [Fixnum] The offset value
|
|
576
|
-
|
|
577
|
-
def offset_value_with_default
|
|
578
|
-
(offset_value || 0).to_i
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
# Sets the request limit, ie Elasticsearch's size parameter that is used
|
|
582
|
-
# to restrict the results that get returned.
|
|
583
|
-
#
|
|
584
|
-
# @example
|
|
585
|
-
# CommentIndex.limit(100)
|
|
586
|
-
#
|
|
587
|
-
# @param value [Fixnum] The limit value, ie the max number of results that
|
|
588
|
-
# should be returned
|
|
589
|
-
#
|
|
590
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
591
|
-
|
|
592
|
-
def limit(value)
|
|
593
|
-
fresh.tap do |criteria|
|
|
594
|
-
criteria.limit_value = value.to_i
|
|
595
|
-
end
|
|
596
|
-
end
|
|
597
|
-
|
|
598
|
-
# @api private
|
|
599
|
-
#
|
|
600
|
-
# Returns the limit value or, if not yet set, the default limit value (30).
|
|
601
|
-
#
|
|
602
|
-
# @return [Fixnum] The limit value
|
|
603
|
-
|
|
604
|
-
def limit_value_with_default
|
|
605
|
-
(limit_value || 30).to_i
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
# Sets pagination parameters for the criteria by using offset and limit,
|
|
609
|
-
# ie Elasticsearch's from and size parameters.
|
|
610
|
-
#
|
|
611
|
-
# @example
|
|
612
|
-
# CommentIndex.paginate(page: 3)
|
|
613
|
-
# CommentIndex.paginate(page: 5, per_page: 60)
|
|
614
|
-
#
|
|
615
|
-
# @param page [#to_i] The current page
|
|
616
|
-
# @param per_page [#to_i] The number of results per page
|
|
617
|
-
#
|
|
618
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
|
619
|
-
|
|
620
|
-
def paginate(page: 1, per_page: 30)
|
|
621
|
-
page = [page.to_i, 1].max
|
|
622
|
-
per_page = per_page.to_i
|
|
623
|
-
|
|
624
|
-
offset((page - 1) * per_page).limit(per_page)
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
def page(value)
|
|
628
|
-
paginate(page: value, per_page: limit_value_with_default)
|
|
629
|
-
end
|
|
630
|
-
|
|
631
|
-
def per(value)
|
|
632
|
-
paginate(page: 1 + (offset_value_with_default / limit_value_with_default), per_page: value)
|
|
633
|
-
end
|
|
634
|
-
|
|
635
411
|
# Fetches the records specified by the criteria in batches using the
|
|
636
412
|
# ElasicSearch scroll API and yields each batch. The batch size and scroll
|
|
637
413
|
# API timeout can be specified. Check out the Elasticsearch docs for
|
|
@@ -746,30 +522,13 @@ module SearchFlip
|
|
|
746
522
|
|
|
747
523
|
def execute
|
|
748
524
|
@response ||= begin
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
|
|
757
|
-
)
|
|
758
|
-
elsif scroll_args
|
|
759
|
-
http_request.post(
|
|
760
|
-
"#{target.type_url}/_search",
|
|
761
|
-
params: request_params.merge(scroll: scroll_args[:timeout]),
|
|
762
|
-
json: request
|
|
763
|
-
)
|
|
764
|
-
else
|
|
765
|
-
http_request.post("#{target.type_url}/_search", params: request_params, json: request)
|
|
766
|
-
end
|
|
767
|
-
|
|
768
|
-
SearchFlip::Response.new(self, http_response.parse)
|
|
769
|
-
rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
|
|
770
|
-
raise e unless failsafe_value
|
|
771
|
-
|
|
772
|
-
SearchFlip::Response.new(self, "took" => 0, "hits" => { "total" => 0, "hits" => [] })
|
|
525
|
+
Config[:instrumenter].instrument("request.search_flip", index: target, request: request) do |payload|
|
|
526
|
+
response = execute!
|
|
527
|
+
|
|
528
|
+
payload[:response] = response
|
|
529
|
+
|
|
530
|
+
response
|
|
531
|
+
end
|
|
773
532
|
end
|
|
774
533
|
end
|
|
775
534
|
|
|
@@ -811,6 +570,7 @@ module SearchFlip
|
|
|
811
570
|
|
|
812
571
|
def fresh
|
|
813
572
|
dup.tap do |criteria|
|
|
573
|
+
criteria.instance_variable_set(:@request, nil)
|
|
814
574
|
criteria.instance_variable_set(:@response, nil)
|
|
815
575
|
end
|
|
816
576
|
end
|
|
@@ -827,6 +587,8 @@ module SearchFlip
|
|
|
827
587
|
end
|
|
828
588
|
end
|
|
829
589
|
|
|
590
|
+
ruby2_keywords :method_missing
|
|
591
|
+
|
|
830
592
|
def_delegators :response, :total_entries, :total_count, :current_page, :previous_page,
|
|
831
593
|
:prev_page, :next_page, :first_page?, :last_page?, :out_of_range?, :total_pages,
|
|
832
594
|
:hits, :ids, :count, :size, :length, :took, :aggregations, :suggestions,
|
|
@@ -836,6 +598,33 @@ module SearchFlip
|
|
|
836
598
|
|
|
837
599
|
private
|
|
838
600
|
|
|
601
|
+
def execute!
|
|
602
|
+
http_request = connection.http_client.headers(accept: "application/json")
|
|
603
|
+
|
|
604
|
+
http_response =
|
|
605
|
+
if scroll_args && scroll_args[:id]
|
|
606
|
+
http_request.post(
|
|
607
|
+
"#{connection.base_url}/_search/scroll",
|
|
608
|
+
params: request_params,
|
|
609
|
+
json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
|
|
610
|
+
)
|
|
611
|
+
elsif scroll_args
|
|
612
|
+
http_request.post(
|
|
613
|
+
"#{target.type_url}/_search",
|
|
614
|
+
params: request_params.merge(scroll: scroll_args[:timeout]),
|
|
615
|
+
json: request
|
|
616
|
+
)
|
|
617
|
+
else
|
|
618
|
+
http_request.post("#{target.type_url}/_search", params: request_params, json: request)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
SearchFlip::Response.new(self, http_response.parse)
|
|
622
|
+
rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
|
|
623
|
+
raise e unless failsafe_value
|
|
624
|
+
|
|
625
|
+
SearchFlip::Response.new(self, "took" => 0, "hits" => { "total" => 0, "hits" => [] })
|
|
626
|
+
end
|
|
627
|
+
|
|
839
628
|
def yield_in_batches(options = {})
|
|
840
629
|
return enum_for(:yield_in_batches, options) unless block_given?
|
|
841
630
|
|