search_flip 3.0.0.beta4 → 3.0.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddb0dadc9a92281c24b5ce7f5ba911cf97a9a7d81caecc1da23febf6c497bf91
4
- data.tar.gz: b0da1b7250010cdd555af7d4cc57ffd388bb2bb8d9b077d8660f2172d7ad15e8
3
+ metadata.gz: 4fae82bf67967306afbb6bfb0f2f321b8993fa364710b24dc7b3860f2bb6ace0
4
+ data.tar.gz: a060faa2ea3b06233dc132b65df0e4c2bc18ab51ea5cfe92eadd96aaa7c8ddbf
5
5
  SHA512:
6
- metadata.gz: 00775bd514c07e991476927bafe5b9ba3696c6edda268188f2f54f8d08ce74456c2ecda08f66aaa510f21559428f35806d6311d7a5e16384cff68869feccd52c
7
- data.tar.gz: b6eb35784a8c3f54c537340ca3ba7d33b81e683a59c9aa1356bddb1e2bcafe9052ccbc19b15628d19971ca0035fa34a28f1c05b75806b3347ab330d73a4ee28c
6
+ metadata.gz: 15aa25cab455dfadead099deb59594580f9c09f71476818f28d483a7d7e8cb3decfd7c4126ecfaf438b92ed19ded8edfe01566d4cffcd1a03394608b66db9ebb
7
+ data.tar.gz: 3a57bc2cecc98641c32f33c688576c7c808482bde3e3f9901590c7241102ef8a01d22f1dd875bd56d60c32fc9d38d4f97fd95e2f54249ccb2699c1ddc35a1e06
data/CHANGELOG.md CHANGED
@@ -23,6 +23,7 @@
23
23
  * Added `SearchFlip::Result.from_hit`
24
24
  * Added support for `source`, `sort`, `page`, `per`, `paginate`, `explain`, and
25
25
  `highlight` to aggregations
26
+ * Added support for instrumentation
26
27
 
27
28
  ## v2.3.1
28
29
 
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 elasticsearch [Bulk API] docs for more info as well as
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.
@@ -790,6 +790,30 @@ end
790
790
 
791
791
  These options will be passed whenever records get indexed, deleted, etc.
792
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
+
793
817
  ## Non-ActiveRecord models
794
818
 
795
819
  SearchFlip ships with built-in support for ActiveRecord models, but using
@@ -73,7 +73,8 @@ module SearchFlip
73
73
  unsupported_methods = [
74
74
  :profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :scroll_args,
75
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, :routing_value
76
+ :post_must_not_values, :post_filter_values, :preference_value, :search_type_value,
77
+ :routing_value
77
78
  ]
78
79
 
79
80
  unsupported_methods.each do |unsupported_method|
@@ -4,6 +4,7 @@ module SearchFlip
4
4
  base_url: "http://127.0.0.1:9200",
5
5
  bulk_limit: 1_000,
6
6
  bulk_max_mb: 100,
7
- auto_refresh: false
7
+ auto_refresh: false,
8
+ instrumenter: NullInstrumenter.new
8
9
  }
9
10
  end
@@ -219,45 +219,47 @@ module SearchFlip
219
219
  # @return [Hash] The generated request object
220
220
 
221
221
  def request
222
- res = {}
223
-
224
- if must_values || must_not_values || filter_values
225
- res[:query] = {
226
- bool: {
227
- must: must_values.to_a,
228
- must_not: must_not_values.to_a,
229
- filter: filter_values.to_a
230
- }.reject { |_, value| value.empty? }
231
- }
232
- end
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
233
234
 
234
- res.update(from: offset_value_with_default, size: limit_value_with_default)
235
-
236
- res[:track_total_hits] = track_total_hits_value unless track_total_hits_value.nil?
237
- res[:explain] = explain_value unless explain_value.nil?
238
- res[:timeout] = timeout_value if timeout_value
239
- res[:terminate_after] = terminate_after_value if terminate_after_value
240
- res[:highlight] = highlight_values if highlight_values
241
- res[:suggest] = suggest_values if suggest_values
242
- res[:sort] = sort_values if sort_values
243
- res[:aggregations] = aggregation_values if aggregation_values
244
-
245
- if post_must_values || post_must_not_values || post_filter_values
246
- res[:post_filter] = {
247
- bool: {
248
- must: post_must_values.to_a,
249
- must_not: post_must_not_values.to_a,
250
- filter: post_filter_values.to_a
251
- }.reject { |_, value| value.empty? }
252
- }
253
- end
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
254
255
 
255
- res[:_source] = source_value unless source_value.nil?
256
- res[:profile] = true if profile_value
256
+ res[:_source] = source_value unless source_value.nil?
257
+ res[:profile] = true if profile_value
257
258
 
258
- res.update(custom_value) if custom_value
259
+ res.update(custom_value) if custom_value
259
260
 
260
- res
261
+ res
262
+ end
261
263
  end
262
264
 
263
265
  # Adds a suggestion section with the given name to the request.
@@ -520,30 +522,13 @@ module SearchFlip
520
522
 
521
523
  def execute
522
524
  @response ||= begin
523
- http_request = connection.http_client.headers(accept: "application/json")
524
-
525
- http_response =
526
- if scroll_args && scroll_args[:id]
527
- http_request.post(
528
- "#{connection.base_url}/_search/scroll",
529
- params: request_params,
530
- json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
531
- )
532
- elsif scroll_args
533
- http_request.post(
534
- "#{target.type_url}/_search",
535
- params: request_params.merge(scroll: scroll_args[:timeout]),
536
- json: request
537
- )
538
- else
539
- http_request.post("#{target.type_url}/_search", params: request_params, json: request)
540
- end
541
-
542
- SearchFlip::Response.new(self, http_response.parse)
543
- rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
544
- raise e unless failsafe_value
545
-
546
- 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
547
532
  end
548
533
  end
549
534
 
@@ -585,6 +570,7 @@ module SearchFlip
585
570
 
586
571
  def fresh
587
572
  dup.tap do |criteria|
573
+ criteria.instance_variable_set(:@request, nil)
588
574
  criteria.instance_variable_set(:@response, nil)
589
575
  end
590
576
  end
@@ -610,6 +596,33 @@ module SearchFlip
610
596
 
611
597
  private
612
598
 
599
+ def execute!
600
+ http_request = connection.http_client.headers(accept: "application/json")
601
+
602
+ http_response =
603
+ if scroll_args && scroll_args[:id]
604
+ http_request.post(
605
+ "#{connection.base_url}/_search/scroll",
606
+ params: request_params,
607
+ json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
608
+ )
609
+ elsif scroll_args
610
+ http_request.post(
611
+ "#{target.type_url}/_search",
612
+ params: request_params.merge(scroll: scroll_args[:timeout]),
613
+ json: request
614
+ )
615
+ else
616
+ http_request.post("#{target.type_url}/_search", params: request_params, json: request)
617
+ end
618
+
619
+ SearchFlip::Response.new(self, http_response.parse)
620
+ rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
621
+ raise e unless failsafe_value
622
+
623
+ SearchFlip::Response.new(self, "took" => 0, "hits" => { "total" => 0, "hits" => [] })
624
+ end
625
+
613
626
  def yield_in_batches(options = {})
614
627
  return enum_for(:yield_in_batches, options) unless block_given?
615
628
 
@@ -0,0 +1,21 @@
1
+ module SearchFlip
2
+ class NullInstrumenter
3
+ def instrument(name, payload = {})
4
+ start(name, payload)
5
+
6
+ begin
7
+ yield(payload) if block_given?
8
+ ensure
9
+ finish(name, payload)
10
+ end
11
+ end
12
+
13
+ def start(_name, _payload)
14
+ true
15
+ end
16
+
17
+ def finish(_name, _payload)
18
+ true
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module SearchFlip
2
- VERSION = "3.0.0.beta4"
2
+ VERSION = "3.0.0.beta5"
3
3
  end
data/lib/search_flip.rb CHANGED
@@ -6,6 +6,7 @@ require "oj"
6
6
  require "set"
7
7
 
8
8
  require "search_flip/version"
9
+ require "search_flip/null_instrumenter"
9
10
  require "search_flip/helper"
10
11
  require "search_flip/exceptions"
11
12
  require "search_flip/json"
@@ -1198,9 +1198,12 @@ RSpec.describe SearchFlip::Criteria do
1198
1198
 
1199
1199
  query = ProductIndex.criteria.tap(&:records)
1200
1200
 
1201
+ expect(query.instance_variable_get(:@request)).not_to be_nil
1201
1202
  expect(query.instance_variable_get(:@response)).not_to be_nil
1202
1203
 
1203
1204
  expect(query.object_id).not_to eq(query.fresh.object_id)
1205
+
1206
+ expect(query.fresh.instance_variable_get(:@request)).to be_nil
1204
1207
  expect(query.fresh.instance_variable_get(:@response)).to be_nil
1205
1208
  end
1206
1209
  end
@@ -1234,10 +1237,49 @@ RSpec.describe SearchFlip::Criteria do
1234
1237
  end
1235
1238
  end
1236
1239
 
1240
+ describe "#execute" do
1241
+ around do |example|
1242
+ default_instrumenter = SearchFlip::Config[:instrumenter]
1243
+
1244
+ SearchFlip::Config[:instrumenter] = ActiveSupport::Notifications.instrumenter
1245
+
1246
+ begin
1247
+ example.run
1248
+ ensure
1249
+ SearchFlip::Config[:instrumenter] = default_instrumenter
1250
+ end
1251
+ end
1252
+
1253
+ let(:notifications) { [] }
1254
+
1255
+ let!(:subscriber) do
1256
+ ActiveSupport::Notifications.subscribe("request.search_flip") do |*args|
1257
+ notifications << args
1258
+ end
1259
+ end
1260
+
1261
+ after { ActiveSupport::Notifications.unsubscribe(subscriber) }
1262
+
1263
+ it "instruments the request" do
1264
+ ProductIndex.match_all.execute
1265
+
1266
+ expect(notifications).to be_present
1267
+ end
1268
+
1269
+ it "passes the index, request and response" do
1270
+ ProductIndex.match_all.execute
1271
+
1272
+ expect(notifications.first[4][:index]).to eq(ProductIndex)
1273
+ expect(notifications.first[4][:request]).to be_present
1274
+ expect(notifications.first[4][:response]).to be_present
1275
+ end
1276
+ end
1277
+
1237
1278
  describe "#track_total_hits" do
1238
1279
  it "is added to the request" do
1239
1280
  if ProductIndex.connection.version.to_i >= 7
1240
1281
  query = ProductIndex.track_total_hits(false)
1282
+
1241
1283
  expect(query.request[:track_total_hits]).to eq(false)
1242
1284
  expect { query.execute }.not_to raise_error
1243
1285
  end
@@ -0,0 +1,43 @@
1
+ require File.expand_path("../spec_helper", __dir__)
2
+
3
+ RSpec.describe SearchFlip::NullInstrumenter do
4
+ subject { described_class.new }
5
+
6
+ describe "#instrument" do
7
+ it "calls start" do
8
+ allow(subject).to receive(:start)
9
+
10
+ subject.instrument("name", { key: "value" }) {}
11
+
12
+ expect(subject).to have_received(:start)
13
+ end
14
+
15
+ it "calls finish" do
16
+ allow(subject).to receive(:finish)
17
+
18
+ subject.instrument("name", { key: "value" }) {}
19
+
20
+ expect(subject).to have_received(:finish)
21
+ end
22
+
23
+ it "yields and passes the payload" do
24
+ yielded_payload = nil
25
+
26
+ subject.instrument("name", { key: "value" }) { |payload| yielded_payload = payload }
27
+
28
+ expect(yielded_payload).to eq(key: "value")
29
+ end
30
+ end
31
+
32
+ describe "#start" do
33
+ it "returns true" do
34
+ expect(subject.start("name", { key: "value" })).to eq(true)
35
+ end
36
+ end
37
+
38
+ describe "#finish" do
39
+ it "returns true" do
40
+ expect(subject.finish("name", { key: "value" })).to eq(true)
41
+ end
42
+ end
43
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search_flip
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.beta4
4
+ version: 3.0.0.beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Vetter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-22 00:00:00.000000000 Z
11
+ date: 2020-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -212,6 +212,7 @@ files:
212
212
  - lib/search_flip/index.rb
213
213
  - lib/search_flip/json.rb
214
214
  - lib/search_flip/model.rb
215
+ - lib/search_flip/null_instrumenter.rb
215
216
  - lib/search_flip/paginatable.rb
216
217
  - lib/search_flip/post_filterable.rb
217
218
  - lib/search_flip/response.rb
@@ -229,6 +230,7 @@ files:
229
230
  - spec/search_flip/http_client_spec.rb
230
231
  - spec/search_flip/index_spec.rb
231
232
  - spec/search_flip/model_spec.rb
233
+ - spec/search_flip/null_instrumenter_spec.rb
232
234
  - spec/search_flip/response_spec.rb
233
235
  - spec/search_flip/result_spec.rb
234
236
  - spec/search_flip/to_json_spec.rb
@@ -268,6 +270,7 @@ test_files:
268
270
  - spec/search_flip/http_client_spec.rb
269
271
  - spec/search_flip/index_spec.rb
270
272
  - spec/search_flip/model_spec.rb
273
+ - spec/search_flip/null_instrumenter_spec.rb
271
274
  - spec/search_flip/response_spec.rb
272
275
  - spec/search_flip/result_spec.rb
273
276
  - spec/search_flip/to_json_spec.rb