search_flip 3.0.0 → 3.2.1

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: fa160794662cedbe89066113cb845c84daaee29451ba8c8ba4f9183689b42582
4
- data.tar.gz: b55133b0f4f90f61db0b76042c1223a17035f277ba221a00c0f665512e1cb170
3
+ metadata.gz: fa450fa83c07d1ed7e266ef436c26075a0bd6d8b5fd2904ed98d6b4b3921c6ab
4
+ data.tar.gz: b7c06e750f4b73d3ad1c8626485526b9389d95591f6c56b954f5f1f6610dec2d
5
5
  SHA512:
6
- metadata.gz: eb2e1e1fd39d64639b9a1f542997e6a00c6238226144fbd32f45f0023f93aa827a1c54597fb71f993090c9d78bcb8b92bbac3a5185d584f26cbeb2e1c4b1a641
7
- data.tar.gz: caea53f0332131113662b24818268bfd5e3cf537ad04b07b853aba600fc4edd417d1783fd3a1d37fd6d848a75df21ec2e3d39c45a8dafab5fdc515d6f8ca43a9
6
+ metadata.gz: cd8bbc2c91f7b35f5b3d54d225082b7d2c4a0b932b1323c4de836adfa8d3118e89bc70ac6f41b27c6dec3fdf200eafec10a7b7c145f3a73ccbc2a2f7c6eb8581
7
+ data.tar.gz: 8c6a974604a63b210d6dd8e585549116266db09321745511ffe78cece7fe84284dd1e035c01be685522f3fd118ac21daa714b2fdcb91f00c08d23dbf93adc692
@@ -0,0 +1,34 @@
1
+ on: push
2
+ name: test
3
+ jobs:
4
+ test:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ fail-fast: false
8
+ matrix:
9
+ elasticsearch:
10
+ - plainpicture/elasticsearch:2.4.1_delete-by-query
11
+ - elasticsearch:5.4
12
+ - docker.elastic.co/elasticsearch/elasticsearch:6.7.0
13
+ - docker.elastic.co/elasticsearch/elasticsearch:7.0.0
14
+ - docker.elastic.co/elasticsearch/elasticsearch:7.11.2
15
+ ruby:
16
+ - 2.5
17
+ - 2.6
18
+ - 2.7
19
+ services:
20
+ elasticsearch:
21
+ image: ${{ matrix.elasticsearch }}
22
+ env:
23
+ discovery.type: single-node
24
+ ports:
25
+ - 9200:9200
26
+ steps:
27
+ - uses: actions/checkout@v1
28
+ - uses: actions/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby }}
31
+ - run: bundle
32
+ - run: sleep 10
33
+ - run: bundle exec rspec
34
+ - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -1,5 +1,15 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
+ TargetRubyVersion: 2.4
4
+
5
+ Layout/EmptyLineBetweenDefs:
6
+ EmptyLineBetweenClassDefs: false
7
+
8
+ Gemspec/RequiredRubyVersion:
9
+ Enabled: false
10
+
11
+ Style/ExplicitBlockArgument:
12
+ Enabled: false
3
13
 
4
14
  Style/HashTransformValues:
5
15
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,6 +1,29 @@
1
1
 
2
2
  # CHANGELOG
3
3
 
4
+ ## v3.2.1
5
+
6
+ * Fix `refresh` having a empty body breaking in elasticsearch 7.11
7
+
8
+ ## v3.2.0
9
+
10
+ * Fix `index_scope` not being passed in `each_record` without block
11
+ * Added `SearchFlip::Criteria#match_none`
12
+
13
+ ## v3.1.2
14
+
15
+ * Fix ignored false value for source when merging criterias
16
+
17
+ ## v3.1.1
18
+
19
+ * Make `SearchFlip::Result.from_hit` work with the `_source` being disabled
20
+
21
+ ## v3.1.0
22
+
23
+ * Added plugin support in `SearchFlip::HTTPClient`
24
+ * Added `SearchFlip::AwsSigv4Plugin` to be able to use AWS Elasticsearch with
25
+ signed requests
26
+
4
27
  ## v3.0.0
5
28
 
6
29
  * Added `Criteria#to_query`, which returns a raw query including all queries
data/README.md CHANGED
@@ -419,6 +419,14 @@ Simply matches all documents:
419
419
  CommentIndex.match_all
420
420
  ```
421
421
 
422
+ * `match_none`
423
+
424
+ Simply matches none documents at all:
425
+
426
+ ```ruby
427
+ CommentIndex.match_none
428
+ ```
429
+
422
430
  * `all`
423
431
 
424
432
  Simply returns the criteria as is or an empty criteria when called on the index
@@ -475,8 +483,8 @@ end
475
483
  ```
476
484
 
477
485
  Generally, aggregation results returned by Elasticsearch are returned as a
478
- `SearchFlip::Result`, which basically is `Hashie::Mash`such that you can access
479
- them via:
486
+ `SearchFlip::Result`, which basically is a `Hashie::Mash`, such that you can
487
+ access them via:
480
488
 
481
489
  ```ruby
482
490
  query.aggregations(:username)["mrkamel"].revenue.value
@@ -769,6 +777,41 @@ http_client = http_client.headers(key: "value")
769
777
  SearchFlip::Connection.new(base_url: "...", http_client: http_client)
770
778
  ```
771
779
 
780
+ ## AWS Elasticsearch / Signed Requests
781
+
782
+ To use SearchFlip with AWS Elasticsearch and signed requests, you have to add
783
+ `aws-sdk-core` to your Gemfile and tell SearchFlip to use the
784
+ `SearchFlip::AwsSigv4Plugin`:
785
+
786
+ ```ruby
787
+ require "search_flip/aws_sigv4_plugin"
788
+
789
+ MyConnection = SearchFlip::Connection.new(
790
+ base_url: "https://your-elasticsearch-cluster.es.amazonaws.com",
791
+ http_client: SearchFlip::HTTPClient.new(
792
+ plugins: [
793
+ SearchFlip::AwsSigv4Plugin.new(
794
+ region: "...",
795
+ access_key_id: "...",
796
+ secret_access_key: "..."
797
+ )
798
+ ]
799
+ )
800
+ )
801
+ ```
802
+
803
+ Again, in your index you need to specify this connection:
804
+
805
+ ```ruby
806
+ class MyIndex
807
+ include SearchFlip::Index
808
+
809
+ def self.connection
810
+ MyConnection
811
+ end
812
+ end
813
+ ```
814
+
772
815
  ## Routing and other index-time options
773
816
 
774
817
  Override `index_options` in case you want to use routing or pass other
@@ -891,6 +934,7 @@ require "search_flip/to_json"
891
934
 
892
935
  * for Elasticsearch 2.x, the delete-by-query plugin is required to delete
893
936
  records via queries
937
+ * `#match_none` is only available with Elasticsearch >= 5
894
938
  * `#track_total_hits` is only available with Elasticsearch >= 7
895
939
 
896
940
  ## Keeping your Models and Indices in Sync
@@ -914,6 +958,10 @@ It uses `after_commit` (if applicable, `after_save`, `after_destroy` and
914
958
  `after_touch` otherwise) hooks to synchronously update the index when your
915
959
  model changes.
916
960
 
961
+ ## Semantic Versioning
962
+
963
+ SearchFlip is using Semantic Versioning: [SemVer](http://semver.org/)
964
+
917
965
  ## Links
918
966
 
919
967
  * Elasticsearch: [https://www.elastic.co/](https://www.elastic.co/)
@@ -945,5 +993,4 @@ $ rspec
945
993
 
946
994
  That's it.
947
995
 
948
-
949
996
  [Bulk API]: https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
data/lib/search_flip.rb CHANGED
@@ -39,6 +39,8 @@ module SearchFlip
39
39
  attr_reader :code, :body
40
40
 
41
41
  def initialize(code:, body:)
42
+ super
43
+
42
44
  @code = code
43
45
  @body = body
44
46
  end
@@ -40,7 +40,7 @@ module SearchFlip
40
40
  else
41
41
  filters = (filter_values || []) + (must_not_values || []).map { |must_not_value| { not: must_not_value } }
42
42
  queries = must_values ? { must: must_values } : {}
43
- filters_and_queries = filters + (queries.size > 0 ? [bool: queries] : [])
43
+ filters_and_queries = filters + (queries.size > 0 ? [{ bool: queries }] : [])
44
44
 
45
45
  res[:filter] = filters_and_queries.size > 1 ? { and: filters_and_queries } : filters_and_queries.first
46
46
  end
@@ -101,7 +101,7 @@ module SearchFlip
101
101
  end
102
102
 
103
103
  def respond_to_missing?(name, *args)
104
- target.respond_to?(name, *args)
104
+ target.respond_to?(name, *args) || super
105
105
  end
106
106
 
107
107
  def method_missing(name, *args, &block)
@@ -0,0 +1,47 @@
1
+ require "aws-sdk-core"
2
+ require "uri"
3
+
4
+ module SearchFlip
5
+ # The SearchFlip::AwsSigV4Plugin is a plugin for the SearchFlip::HTTPClient
6
+ # to be used with AWS Elasticsearch to sign requests, i.e. add signed
7
+ # headers, before sending the request to Elasticsearch.
8
+ #
9
+ # @example
10
+ # MyConnection = SearchFlip::Connection.new(
11
+ # base_url: "https://your-elasticsearch-cluster.es.amazonaws.com",
12
+ # http_client: SearchFlip::HTTPClient.new(
13
+ # plugins: [
14
+ # SearchFlip::AwsSigv4Plugin.new(
15
+ # region: "...",
16
+ # access_key_id: "...",
17
+ # secret_access_key: "..."
18
+ # )
19
+ # ]
20
+ # )
21
+ # )
22
+
23
+ class AwsSigv4Plugin
24
+ attr_accessor :signer
25
+
26
+ def initialize(options = {})
27
+ self.signer = Aws::Sigv4::Signer.new({ service: "es" }.merge(options))
28
+ end
29
+
30
+ def call(request, method, uri, options = {})
31
+ full_uri = URI.parse(uri)
32
+ full_uri.query = URI.encode_www_form(options[:params]) if options[:params]
33
+
34
+ signature_request = {
35
+ http_method: method.to_s.upcase,
36
+ url: full_uri.to_s
37
+ }
38
+
39
+ signature_request[:body] = options[:body] if options.key?(:body)
40
+ signature_request[:body] = options[:json].respond_to?(:to_str) ? options[:json] : JSON.generate(options[:json]) if options[:json]
41
+
42
+ signature = signer.sign_request(signature_request)
43
+
44
+ request.headers(signature.headers)
45
+ end
46
+ end
47
+ end
@@ -272,7 +272,7 @@ module SearchFlip
272
272
  # @return [Boolean] Returns true or raises SearchFlip::ResponseError
273
273
 
274
274
  def refresh(index_names = nil)
275
- http_client.post("#{index_names ? index_url(Array(index_names).join(",")) : base_url}/_refresh", json: {})
275
+ http_client.post("#{index_names ? index_url(Array(index_names).join(",")) : base_url}/_refresh")
276
276
 
277
277
  true
278
278
  end
@@ -44,35 +44,25 @@ module SearchFlip
44
44
  other = other.criteria
45
45
 
46
46
  fresh.tap do |criteria|
47
- criteria.profile_value = other.profile_value unless other.profile_value.nil?
48
- criteria.failsafe_value = other.failsafe_value unless other.failsafe_value.nil?
49
- criteria.terminate_after_value = other.terminate_after_value unless other.terminate_after_value.nil?
50
- criteria.timeout_value = other.timeout_value unless other.timeout_value.nil?
51
- criteria.offset_value = other.offset_value if other.offset_value
52
- criteria.limit_value = other.limit_value if other.limit_value
53
- criteria.scroll_args = other.scroll_args if other.scroll_args
54
- criteria.source_value = other.source_value if other.source_value
55
- criteria.preference_value = other.preference_value if other.preference_value
56
- criteria.search_type_value = other.search_type_value if other.search_type_value
57
- criteria.routing_value = other.routing_value if other.routing_value
58
- criteria.track_total_hits_value = other.track_total_hits_value unless other.track_total_hits_value.nil?
59
- criteria.explain_value = other.explain_value unless other.explain_value.nil?
60
-
61
- criteria.sort_values = (criteria.sort_values || []) + other.sort_values if other.sort_values
62
- criteria.includes_values = (criteria.includes_values || []) + other.includes_values if other.includes_values
63
- criteria.preload_values = (criteria.preload_values || []) + other.preload_values if other.preload_values
64
- criteria.eager_load_values = (criteria.eager_load_values || []) + other.eager_load_values if other.eager_load_values
65
- criteria.must_values = (criteria.must_values || []) + other.must_values if other.must_values
66
- criteria.must_not_values = (criteria.must_not_values || []) + other.must_not_values if other.must_not_values
67
- criteria.filter_values = (criteria.filter_values || []) + other.filter_values if other.filter_values
68
- criteria.post_must_values = (criteria.post_must_values || []) + other.post_must_values if other.post_must_values
69
- criteria.post_must_not_values = (criteria.post_must_not_values || []) + other.post_must_not_values if other.post_must_not_values
70
- criteria.post_filter_values = (criteria.post_filter_values || []) + other.post_filter_values if other.post_filter_values
71
-
72
- criteria.highlight_values = (criteria.highlight_values || {}).merge(other.highlight_values) if other.highlight_values
73
- criteria.suggest_values = (criteria.suggest_values || {}).merge(other.suggest_values) if other.suggest_values
74
- criteria.custom_value = (criteria.custom_value || {}).merge(other.custom_value) if other.custom_value
75
- criteria.aggregation_values = (criteria.aggregation_values || {}).merge(other.aggregation_values) if other.aggregation_values
47
+ [
48
+ :profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :offset_value,
49
+ :limit_value, :scroll_args, :source_value, :preference_value, :search_type_value,
50
+ :routing_value, :track_total_hits_value, :explain_value
51
+ ].each do |name|
52
+ criteria.send(:"#{name}=", other.send(name)) unless other.send(name).nil?
53
+ end
54
+
55
+ [
56
+ :sort_values, :includes_values, :preload_values, :eager_load_values, :must_values,
57
+ :must_not_values, :filter_values, :post_must_values, :post_must_not_values,
58
+ :post_filter_values
59
+ ].each do |name|
60
+ criteria.send(:"#{name}=", (criteria.send(name) || []) + other.send(name)) if other.send(name)
61
+ end
62
+
63
+ [:highlight_values, :suggest_values, :custom_value, :aggregation_values].each do |name|
64
+ criteria.send(:"#{name}=", (criteria.send(name) || {}).merge(other.send(name))) if other.send(name)
65
+ end
76
66
  end
77
67
  end
78
68
 
@@ -576,7 +566,7 @@ module SearchFlip
576
566
  end
577
567
 
578
568
  def respond_to_missing?(name, *args)
579
- target.respond_to?(name, *args)
569
+ target.respond_to?(name, *args) || super
580
570
  end
581
571
 
582
572
  def method_missing(name, *args, &block)
@@ -235,6 +235,22 @@ module SearchFlip
235
235
  filter(match_all: options)
236
236
  end
237
237
 
238
+ # Adds a match none filter to the criteria, which simply matches none
239
+ # documents at all. Check out the Elasticsearch docs for further details.
240
+ #
241
+ # @example Basic usage
242
+ # CommentIndex.match_none
243
+ #
244
+ # @example Filter chaining
245
+ # query = CommentIndex.search("...")
246
+ # query = query.match_none unless current_user.admin?
247
+ #
248
+ # @return [SearchFlip::Criteria] A newly created extended criteria
249
+
250
+ def match_none
251
+ filter(match_none: {})
252
+ end
253
+
238
254
  # Adds an exists filter to the criteria, which selects all documents for
239
255
  # which the specified field has a non-null value.
240
256
  #
@@ -34,9 +34,10 @@ module SearchFlip
34
34
  criteria.highlight_values = (criteria.highlight_values || {}).merge(options)
35
35
 
36
36
  hash =
37
- if fields.is_a?(Hash)
37
+ case fields
38
+ when Hash
38
39
  fields
39
- elsif fields.is_a?(Array)
40
+ when Array
40
41
  fields.each_with_object({}) { |field, h| h[field] = {} }
41
42
  else
42
43
  { fields => {} }
@@ -1,15 +1,14 @@
1
1
  module SearchFlip
2
- # @api private
3
- #
4
2
  # The SearchFlip::HTTPClient class wraps the http gem, is for internal use
5
3
  # and responsible for the http request/response handling, ie communicating
6
4
  # with Elasticsearch.
7
5
 
8
6
  class HTTPClient
9
- attr_accessor :request
7
+ attr_accessor :request, :plugins
10
8
 
11
- def initialize
9
+ def initialize(plugins: [])
12
10
  self.request = HTTP
11
+ self.plugins = plugins
13
12
  end
14
13
 
15
14
  class << self
@@ -30,17 +29,16 @@ module SearchFlip
30
29
  end
31
30
 
32
31
  [:get, :post, :put, :delete, :head].each do |method|
33
- define_method(method) do |*args|
34
- execute(method, *args)
32
+ define_method(method) do |uri, options = {}|
33
+ execute(method, uri, options)
35
34
  end
36
-
37
- ruby2_keywords method
38
35
  end
39
36
 
40
37
  private
41
38
 
42
- ruby2_keywords def execute(method, *args)
43
- response = request.send(method, *args)
39
+ def execute(method, uri, options = {})
40
+ final_request = plugins.inject(self) { |res, cur| cur.call(res, method, uri, options) }
41
+ response = final_request.request.send(method, uri, options)
44
42
 
45
43
  raise SearchFlip::ResponseError.new(code: response.code, body: response.body.to_s) unless response.status.success?
46
44
 
@@ -153,7 +153,7 @@ module SearchFlip
153
153
  # scope to be applied to the scope
154
154
 
155
155
  def each_record(scope, index_scope: false)
156
- return enum_for(:each_record, scope) unless block_given?
156
+ return enum_for(:each_record, scope, index_scope: index_scope) unless block_given?
157
157
 
158
158
  if scope.respond_to?(:find_each)
159
159
  (index_scope ? self.index_scope(scope) : scope).find_each do |record|
@@ -247,8 +247,8 @@ module SearchFlip
247
247
  SearchFlip::Criteria.new(target: self)
248
248
  end
249
249
 
250
- def_delegators :criteria, :all, :profile, :where, :where_not, :filter, :range, :match_all, :exists,
251
- :exists_not, :post_where, :post_where_not, :post_range, :post_exists, :post_exists_not,
250
+ def_delegators :criteria, :all, :profile, :where, :where_not, :filter, :range, :match_all, :match_none,
251
+ :exists, :exists_not, :post_where, :post_where_not, :post_range, :post_exists, :post_exists_not,
252
252
  :post_filter, :post_must, :post_must_not, :post_should, :aggregate, :scroll, :source,
253
253
  :includes, :eager_load, :preload, :sort, :resort, :order, :reorder, :offset, :limit, :paginate,
254
254
  :page, :per, :search, :highlight, :suggest, :custom, :find_in_batches, :find_results_in_batches,
@@ -17,7 +17,7 @@ module SearchFlip
17
17
  # SearchFlip::Result.from_hit(top_sales_hits.first)
18
18
 
19
19
  def self.from_hit(hit)
20
- raw_result = hit["_source"].dup
20
+ raw_result = (hit["_source"] || {}).dup
21
21
 
22
22
  raw_result["_hit"] = hit.each_with_object({}) do |(key, value), hash|
23
23
  hash[key] = value if key != "_source"
@@ -1,3 +1,3 @@
1
1
  module SearchFlip
2
- VERSION = "3.0.0"
2
+ VERSION = "3.2.1"
3
3
  end
data/search_flip.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  MESSAGE
26
26
 
27
27
  spec.add_development_dependency "activerecord", ">= 3.0"
28
+ spec.add_development_dependency "aws-sdk-core"
28
29
  spec.add_development_dependency "bundler"
29
30
  spec.add_development_dependency "factory_bot"
30
31
  spec.add_development_dependency "rake"
@@ -0,0 +1,41 @@
1
+ require File.expand_path("../spec_helper", __dir__)
2
+ require "search_flip/aws_sigv4_plugin"
3
+
4
+ RSpec.describe SearchFlip::AwsSigv4Plugin do
5
+ describe "#call" do
6
+ subject(:plugin) do
7
+ SearchFlip::AwsSigv4Plugin.new(
8
+ region: "us-east-1",
9
+ access_key_id: "access key",
10
+ secret_access_key: "secret access key"
11
+ )
12
+ end
13
+
14
+ let(:client) { SearchFlip::HTTPClient.new }
15
+
16
+ it "adds the signed headers to the request" do
17
+ Timecop.freeze Time.parse("2020-01-01 12:00:00 UTC") do
18
+ expect(client).to receive(:headers).with(
19
+ "host" => "localhost",
20
+ "authorization" => /.*/,
21
+ "x-amz-content-sha256" => /.*/,
22
+ "x-amz-date" => /20200101T120000Z/
23
+ )
24
+
25
+ plugin.call(client, :get, "http://localhost/index")
26
+ end
27
+ end
28
+
29
+ it "feeds the http method, full url and body to the signer" do
30
+ signing_request = {
31
+ http_method: "GET",
32
+ url: "http://localhost/index?param=value",
33
+ body: JSON.generate(key: "value")
34
+ }
35
+
36
+ expect(plugin.signer).to receive(:sign_request).with(signing_request).and_call_original
37
+
38
+ plugin.call(client, :get, "http://localhost/index", params: { param: "value" }, json: { key: "value" })
39
+ end
40
+ end
41
+ end
@@ -36,10 +36,10 @@ RSpec.describe SearchFlip::Connection do
36
36
  it "changes the aliases" do
37
37
  connection = SearchFlip::Connection.new
38
38
 
39
- connection.update_aliases(actions: [add: { index: "products", alias: "alias1" }])
39
+ connection.update_aliases(actions: [{ add: { index: "products", alias: "alias1" } }])
40
40
  expect(connection.get_aliases(alias_name: "alias1").keys).to eq(["products"])
41
41
 
42
- connection.update_aliases(actions: [remove: { index: "products", alias: "alias1" }])
42
+ connection.update_aliases(actions: [{ remove: { index: "products", alias: "alias1" } }])
43
43
  expect(connection.alias_exists?("alias1")).to eq(false)
44
44
  end
45
45
  end
@@ -79,11 +79,11 @@ RSpec.describe SearchFlip::Connection do
79
79
 
80
80
  expect(connection.alias_exists?(:some_alias)).to eq(false)
81
81
 
82
- connection.update_aliases(actions: [add: { index: "products", alias: "some_alias" }])
82
+ connection.update_aliases(actions: [{ add: { index: "products", alias: "some_alias" } }])
83
83
 
84
84
  expect(connection.alias_exists?(:some_alias)).to eq(true)
85
85
  ensure
86
- connection.update_aliases(actions: [remove: { index: "products", alias: "some_alias" }])
86
+ connection.update_aliases(actions: [{ remove: { index: "products", alias: "some_alias" } }])
87
87
  end
88
88
  end
89
89
  end
@@ -110,6 +110,16 @@ RSpec.describe SearchFlip::Criteria do
110
110
 
111
111
  expect(criteria1.merge(criteria2).send(method)).to eq("value2")
112
112
  end
113
+
114
+ it "handles false values correctly" do
115
+ criteria1 = SearchFlip::Criteria.new(target: TestIndex)
116
+ criteria1.send("#{method}=", true)
117
+
118
+ criteria2 = SearchFlip::Criteria.new(target: TestIndex)
119
+ criteria2.send("#{method}=", false)
120
+
121
+ expect(criteria1.merge(criteria2).send(method)).to eq(false)
122
+ end
113
123
  end
114
124
  end
115
125
 
@@ -406,6 +416,18 @@ RSpec.describe SearchFlip::Criteria do
406
416
  end
407
417
  end
408
418
 
419
+ describe "#match_none" do
420
+ it "does not match any documents" do
421
+ if ProductIndex.connection.version.to_i >= 5
422
+ ProductIndex.import create(:product)
423
+
424
+ query = ProductIndex.match_none
425
+
426
+ expect(query.records).to eq([])
427
+ end
428
+ end
429
+ end
430
+
409
431
  describe "#exists" do
410
432
  it "sets up the constraints correctly and is chainable" do
411
433
  product1 = create(:product, title: "title1", description: "description1")
@@ -39,6 +39,23 @@ RSpec.describe SearchFlip::HTTPClient do
39
39
  end
40
40
  end
41
41
 
42
+ describe "plugins" do
43
+ subject do
44
+ SearchFlip::HTTPClient.new(
45
+ plugins: [
46
+ ->(request, _method, _uri, _options = {}) { request.headers("First-Header" => "Value") },
47
+ ->(request, _method, _uri, _options = {}) { request.headers("Second-Header" => "Value") }
48
+ ]
49
+ )
50
+ end
51
+
52
+ it "injects the plugins and uses their result in the request" do
53
+ stub_request(:get, "http://localhost/path").with(query: { key: "value" }, headers: { "First-Header" => "Value", "Second-Header" => "Value" }).and_return(body: "success")
54
+
55
+ expect(subject.get("http://localhost/path", params: { key: "value" }).body.to_s).to eq("success")
56
+ end
57
+ end
58
+
42
59
  [:via, :basic_auth, :auth].each do |method|
43
60
  describe "##{method}" do
44
61
  it "creates a dupped instance" do
@@ -5,8 +5,8 @@ RSpec.describe SearchFlip::Index do
5
5
  subject { ProductIndex }
6
6
 
7
7
  methods = [
8
- :all, :profile, :where, :where_not, :filter, :range, :match_all, :exists,
9
- :exists_not, :post_where, :post_where_not, :post_filter, :post_must,
8
+ :all, :profile, :where, :where_not, :filter, :range, :match_all, :match_none,
9
+ :exists, :exists_not, :post_where, :post_where_not, :post_filter, :post_must,
10
10
  :post_must_not, :post_should, :post_range, :post_exists, :post_exists_not,
11
11
  :aggregate, :scroll, :source, :includes, :eager_load, :preload, :sort, :resort,
12
12
  :order, :reorder, :offset, :limit, :paginate, :page, :per, :search,
@@ -211,12 +211,18 @@ RSpec.describe SearchFlip::Index do
211
211
  mapping = { properties: { id: { type: "long" } } }
212
212
 
213
213
  allow(TestIndex).to receive(:mapping).and_return(mapping)
214
- allow(TestIndex.connection).to receive(:update_mapping).and_call_original
214
+ allow(TestIndex.connection).to receive(:update_mapping)
215
215
 
216
216
  TestIndex.update_mapping
217
217
 
218
218
  expect(TestIndex.connection).to have_received(:update_mapping).with("test", { "test" => mapping }, type_name: "test")
219
219
  end
220
+
221
+ it "updates the mapping" do
222
+ TestIndex.create_index
223
+
224
+ expect(TestIndex.update_mapping).to eq(true)
225
+ end
220
226
  end
221
227
  end
222
228
 
@@ -258,12 +264,19 @@ RSpec.describe SearchFlip::Index do
258
264
  TestIndex.create_index
259
265
  TestIndex.update_mapping
260
266
 
261
- allow(TestIndex.connection).to receive(:get_mapping).and_call_original
267
+ allow(TestIndex.connection).to receive(:get_mapping)
262
268
 
263
269
  TestIndex.get_mapping
264
270
 
265
271
  expect(TestIndex.connection).to have_received(:get_mapping).with("test", type_name: "test")
266
272
  end
273
+
274
+ it "returns the mapping" do
275
+ TestIndex.create_index
276
+ TestIndex.update_mapping
277
+
278
+ expect(TestIndex.get_mapping).to be_present
279
+ end
267
280
  end
268
281
  end
269
282
 
@@ -7,7 +7,7 @@ RSpec.describe SearchFlip::NullInstrumenter do
7
7
  it "calls start" do
8
8
  allow(subject).to receive(:start)
9
9
 
10
- subject.instrument("name", { key: "value" }) {}
10
+ subject.instrument("name", { key: "value" }) { true }
11
11
 
12
12
  expect(subject).to have_received(:start)
13
13
  end
@@ -15,7 +15,7 @@ RSpec.describe SearchFlip::NullInstrumenter do
15
15
  it "calls finish" do
16
16
  allow(subject).to receive(:finish)
17
17
 
18
- subject.instrument("name", { key: "value" }) {}
18
+ subject.instrument("name", { key: "value" }) { true }
19
19
 
20
20
  expect(subject).to have_received(:finish)
21
21
  end
@@ -13,5 +13,11 @@ RSpec.describe SearchFlip::Result do
13
13
 
14
14
  expect(result.key1[0].key2).to eq("value")
15
15
  end
16
+
17
+ it "works with the _source being disabled" do
18
+ result = SearchFlip::Result.from_hit("_id" => 1)
19
+
20
+ expect(result._hit._id).to eq(1)
21
+ end
16
22
  end
17
23
  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
4
+ version: 3.2.1
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-05-29 00:00:00.000000000 Z
11
+ date: 2021-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -199,9 +213,9 @@ executables: []
199
213
  extensions: []
200
214
  extra_rdoc_files: []
201
215
  files:
216
+ - ".github/workflows/test.yml"
202
217
  - ".gitignore"
203
218
  - ".rubocop.yml"
204
- - ".travis.yml"
205
219
  - CHANGELOG.md
206
220
  - Gemfile
207
221
  - LICENSE.txt
@@ -212,6 +226,7 @@ files:
212
226
  - lib/search_flip.rb
213
227
  - lib/search_flip/aggregatable.rb
214
228
  - lib/search_flip/aggregation.rb
229
+ - lib/search_flip/aws_sigv4_plugin.rb
215
230
  - lib/search_flip/bulk.rb
216
231
  - lib/search_flip/config.rb
217
232
  - lib/search_flip/connection.rb
@@ -238,6 +253,7 @@ files:
238
253
  - search_flip.gemspec
239
254
  - spec/delegate_matcher.rb
240
255
  - spec/search_flip/aggregation_spec.rb
256
+ - spec/search_flip/aws_sigv4_plugin_spec.rb
241
257
  - spec/search_flip/bulk_spec.rb
242
258
  - spec/search_flip/connection_spec.rb
243
259
  - spec/search_flip/criteria_spec.rb
@@ -271,13 +287,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
271
287
  - !ruby/object:Gem::Version
272
288
  version: '0'
273
289
  requirements: []
274
- rubygems_version: 3.0.3
290
+ rubygems_version: 3.2.3
275
291
  signing_key:
276
292
  specification_version: 4
277
293
  summary: Full-Featured Elasticsearch Ruby Client with a Chainable DSL
278
294
  test_files:
279
295
  - spec/delegate_matcher.rb
280
296
  - spec/search_flip/aggregation_spec.rb
297
+ - spec/search_flip/aws_sigv4_plugin_spec.rb
281
298
  - spec/search_flip/bulk_spec.rb
282
299
  - spec/search_flip/connection_spec.rb
283
300
  - spec/search_flip/criteria_spec.rb
data/.travis.yml DELETED
@@ -1,20 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- env:
4
- - ES_IMAGE=plainpicture/elasticsearch:2.4.1_delete-by-query
5
- - ES_IMAGE=elasticsearch:5.4
6
- - ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:6.7.0
7
- - ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:7.0.0
8
- - ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:7.6.0
9
- rvm:
10
- - ruby-2.5.3
11
- - ruby-2.6.2
12
- - ruby-2.7.1
13
- before_install:
14
- - docker-compose up -d
15
- - sleep 10
16
- install:
17
- - travis_retry bundle install
18
- script:
19
- - bundle exec rspec
20
- - bundle exec rubocop