search_flip 3.0.0.beta4 → 3.0.0.beta5

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 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