search_flip 3.0.0 → 3.2.1

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