search_flip 3.1.1 → 3.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb8e67eac92f6f39f78dcc6770ea46d397ab7ec26b1e461b76634367a558f734
4
- data.tar.gz: 04e8445e8039e72eac4344cb67de22c04107020b63112f01c9ed76512942c314
3
+ metadata.gz: 6722b210d8b3d0baa98e3370f26e2f293a21ea7db15dd08ba47deb2a1dba4e1c
4
+ data.tar.gz: a66d9f5482e06ae11cffeece148a8eb290523ee6ec4c91f510c1258b1c780225
5
5
  SHA512:
6
- metadata.gz: 429bacd5e7d64f922bb2c20d82d8e2ead32340f8e85abacfd55f0034791cc13c7557a5973dadf7fb19681364f56f899830aecefd7ddedab58b06c9cb8c8a13bd
7
- data.tar.gz: f60dfa7d6396971dadd0d960442074a1702493e8149ba885fb84a09f77031b4450603a9ece5391e55d5e747a917b95a4708a0bad3232610c4d4a75ddebc88cd0
6
+ metadata.gz: 4e60dbae8d1bf0c4be3964bd0daf0740c4dd89827d61481d21960af17f34b237b512dde09f08111949d9b728bc3290c59a535930e168aaa562c30c9f74faa841
7
+ data.tar.gz: 3b7d941dee59924dc47b486e5cd3d88af8a3a9a42e8f6952e14934087e2a0acbe0014ee09caabec373640c35c5ae1d72a433e2100fcd2257e8d920c09f39e3b6
@@ -0,0 +1,35 @@
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.6
17
+ - 2.7
18
+ - 3.0
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: gem install bundler
32
+ - run: bundle
33
+ - run: sleep 10
34
+ - run: bundle exec rspec
35
+ - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -1,6 +1,10 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
- TargetRubyVersion: 2.4
3
+ TargetRubyVersion: 2.5
4
+ SuggestExtensions: false
5
+
6
+ Layout/EmptyLineBetweenDefs:
7
+ EmptyLineBetweenClassDefs: false
4
8
 
5
9
  Gemspec/RequiredRubyVersion:
6
10
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,6 +1,29 @@
1
1
 
2
2
  # CHANGELOG
3
3
 
4
+ ## v3.4.0
5
+
6
+ * Expose `Http#timeout` via `SearchFlip::HTTPClient`
7
+
8
+ ## v3.3.0
9
+
10
+ * Update httprb
11
+ * Changed oj default options
12
+ * Allow to set oj json options
13
+
14
+ ## v3.2.1
15
+
16
+ * Fix `refresh` having a empty body breaking in elasticsearch 7.11
17
+
18
+ ## v3.2.0
19
+
20
+ * Fix `index_scope` not being passed in `each_record` without block
21
+ * Added `SearchFlip::Criteria#match_none`
22
+
23
+ ## v3.1.2
24
+
25
+ * Fix ignored false value for source when merging criterias
26
+
4
27
  ## v3.1.1
5
28
 
6
29
  * Make `SearchFlip::Result.from_hit` work with the `_source` being disabled
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  **Full-Featured Elasticsearch Ruby Client with a Chainable DSL**
5
5
 
6
- [![Build Status](https://secure.travis-ci.org/mrkamel/search_flip.svg?branch=master)](http://travis-ci.org/mrkamel/search_flip)
6
+ [![Build](https://github.com/mrkamel/search_flip/workflows/test/badge.svg)](https://github.com/mrkamel/search_flip/actions?query=workflow%3Atest+branch%3Amaster)
7
7
  [![Gem Version](https://badge.fury.io/rb/search_flip.svg)](http://badge.fury.io/rb/search_flip)
8
8
 
9
9
  Using SearchFlip it is dead-simple to create index classes that correspond to
@@ -51,8 +51,7 @@ CommentIndex.search("hello world").where(available: true).sort(id: "desc").aggre
51
51
 
52
52
  ```
53
53
 
54
- Finally, SearchFlip comes with a minimal set of dependencies (http-rb, hashie
55
- and oj only).
54
+ Finally, SearchFlip comes with a minimal set of dependencies.
56
55
 
57
56
  ## Reference Docs
58
57
 
@@ -419,6 +418,14 @@ Simply matches all documents:
419
418
  CommentIndex.match_all
420
419
  ```
421
420
 
421
+ * `match_none`
422
+
423
+ Simply matches none documents at all:
424
+
425
+ ```ruby
426
+ CommentIndex.match_none
427
+ ```
428
+
422
429
  * `all`
423
430
 
424
431
  Simply returns the criteria as is or an empty criteria when called on the index
@@ -749,7 +756,7 @@ end
749
756
  This allows to use different clusters per index e.g. when migrating indices to
750
757
  new versions of Elasticsearch.
751
758
 
752
- You can specify basic auth, additional headers, etc via:
759
+ You can specify basic auth, additional headers, request timeouts, etc via:
753
760
 
754
761
  ```ruby
755
762
  http_client = SearchFlip::HTTPClient.new
@@ -766,6 +773,9 @@ http_client = http_client.via("proxy.host", 8080)
766
773
  # Custom headers
767
774
  http_client = http_client.headers(key: "value")
768
775
 
776
+ # Timeouts
777
+ http_client = http_client.timeout(20)
778
+
769
779
  SearchFlip::Connection.new(base_url: "...", http_client: http_client)
770
780
  ```
771
781
 
@@ -875,57 +885,52 @@ Thus, if your ORM supports `.find_each`, `#id` and `#where` you are already
875
885
  good to go. Otherwise, simply add your custom implementation of those methods
876
886
  that work with whatever ORM you use.
877
887
 
878
- ## Date and Timestamps in JSON
888
+ ## JSON
879
889
 
880
- Elasticsearch requires dates and timestamps to have one of the formats listed
881
- here: [https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#strict-date-time](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#strict-date-time).
882
-
883
- However, `JSON.generate` in ruby by default outputs something like:
890
+ SearchFlip is using the [Oj gem](https://github.com/ohler55/oj) to generate
891
+ JSON. More concretely, SearchFlip is using:
884
892
 
885
893
  ```ruby
886
- JSON.generate(time: Time.now.utc)
887
- # => "{\"time\":\"2018-02-22 18:19:33 UTC\"}"
894
+ Oj.dump({ key: "value" }, mode: :custom, use_to_json: true, time_format: :xmlschema, bigdecimal_as_decimal: false)
888
895
  ```
889
896
 
890
- This format is not compatible with Elasticsearch by default. If you're on
891
- Rails, ActiveSupport adds its own `#to_json` methods to `Time`, `Date`, etc.
892
- However, ActiveSupport checks whether they are used in combination with
893
- `JSON.generate` or not and adapt:
897
+ The `use_to_json` option is used for maximum compatibility, most importantly
898
+ when using rails `ActiveSupport::TimeWithZone` timestamps, which `oj` can not
899
+ serialize natively. However, `use_to_json` adds performance overhead. You can
900
+ change the json options via:
894
901
 
895
902
  ```ruby
896
- Time.now.utc.to_json
897
- => "\"2018-02-22T18:18:22.088Z\""
898
-
899
- JSON.generate(time: Time.now.utc)
900
- => "{\"time\":\"2018-02-22 18:18:59 UTC\"}"
903
+ SearchFlip::Config[:json_options] = {
904
+ mode: :custom,
905
+ use_to_json: false,
906
+ time_format: :xmlschema,
907
+ bigdecimal_as_decimal: false
908
+ }
901
909
  ```
902
910
 
903
- SearchFlip is using the [Oj gem](https://github.com/ohler55/oj) to generate
904
- JSON. More concretely, SearchFlip is using:
911
+ However, you then have to convert timestamps manually for indexation via e.g.:
905
912
 
906
913
  ```ruby
907
- Oj.dump({ key: "value" }, mode: :custom, use_to_json: true)
908
- ```
914
+ class MyIndex
915
+ # ...
909
916
 
910
- This mitigates the issues if you're on Rails:
917
+ def self.serialize(model)
918
+ {
919
+ # ...
911
920
 
912
- ```ruby
913
- Oj.dump(Time.now, mode: :custom, use_to_json: true)
914
- # => "\"2018-02-22T18:21:21.064Z\""
921
+ created_at: model.created_at.to_time
922
+ }
923
+ end
924
+ end
915
925
  ```
916
926
 
917
- However, if you're not on Rails, you need to add `#to_json` methods to `Time`,
918
- `Date` and `DateTime` to get proper serialization. You can either add them on
919
- your own, via other libraries or by simply using:
920
-
921
- ```ruby
922
- require "search_flip/to_json"
923
- ```
927
+ Please check out the oj docs for more details.
924
928
 
925
929
  ## Feature Support
926
930
 
927
931
  * for Elasticsearch 2.x, the delete-by-query plugin is required to delete
928
932
  records via queries
933
+ * `#match_none` is only available with Elasticsearch >= 5
929
934
  * `#track_total_hits` is only available with Elasticsearch >= 7
930
935
 
931
936
  ## Keeping your Models and Indices in Sync
data/lib/search_flip.rb CHANGED
@@ -3,6 +3,7 @@ require "forwardable"
3
3
  require "http"
4
4
  require "hashie"
5
5
  require "thread"
6
+ require "json"
6
7
  require "oj"
7
8
  require "set"
8
9
 
@@ -143,7 +143,7 @@ module SearchFlip
143
143
 
144
144
  return if options[:raise] == false
145
145
 
146
- parsed_response = response.parse
146
+ parsed_response = SearchFlip::JSON.parse(response.to_s)
147
147
 
148
148
  return unless parsed_response["errors"]
149
149
 
@@ -5,6 +5,12 @@ module SearchFlip
5
5
  bulk_limit: 1_000,
6
6
  bulk_max_mb: 100,
7
7
  auto_refresh: false,
8
- instrumenter: NullInstrumenter.new
8
+ instrumenter: NullInstrumenter.new,
9
+ json_options: {
10
+ mode: :custom,
11
+ use_to_json: true,
12
+ time_format: :xmlschema,
13
+ bigdecimal_as_decimal: false
14
+ }
9
15
  }
10
16
  end
@@ -28,7 +28,11 @@ module SearchFlip
28
28
 
29
29
  def version
30
30
  @version_mutex.synchronize do
31
- @version ||= http_client.headers(accept: "application/json").get("#{base_url}/").parse["version"]["number"]
31
+ @version ||= begin
32
+ response = http_client.headers(accept: "application/json").get("#{base_url}/")
33
+
34
+ SearchFlip::JSON.parse(response.to_s)["version"]["number"]
35
+ end
32
36
  end
33
37
  end
34
38
 
@@ -40,7 +44,9 @@ module SearchFlip
40
44
  # @return [Hash] The raw response
41
45
 
42
46
  def cluster_health
43
- http_client.headers(accept: "application/json").get("#{base_url}/_cluster/health").parse
47
+ response = http_client.headers(accept: "application/json").get("#{base_url}/_cluster/health")
48
+
49
+ SearchFlip::JSON.parse(response.to_s)
44
50
  end
45
51
 
46
52
  # Uses the Elasticsearch Multi Search API to execute multiple search requests
@@ -71,7 +77,7 @@ module SearchFlip
71
77
  .headers(accept: "application/json", content_type: "application/x-ndjson")
72
78
  .post("#{base_url}/_msearch", body: payload)
73
79
 
74
- raw_response.parse["responses"].map.with_index do |response, index|
80
+ SearchFlip::JSON.parse(raw_response.to_s)["responses"].map.with_index do |response, index|
75
81
  SearchFlip::Response.new(criterias[index], response)
76
82
  end
77
83
  end
@@ -90,10 +96,11 @@ module SearchFlip
90
96
  # @return [Hash] The raw response
91
97
 
92
98
  def update_aliases(payload)
93
- http_client
99
+ response = http_client
94
100
  .headers(accept: "application/json", content_type: "application/json")
95
101
  .post("#{base_url}/_aliases", body: SearchFlip::JSON.generate(payload))
96
- .parse
102
+
103
+ SearchFlip::JSON.parse(response.to_s)
97
104
  end
98
105
 
99
106
  # Sends an analyze request to Elasticsearch. Raises
@@ -105,10 +112,11 @@ module SearchFlip
105
112
  # @return [Hash] The raw response
106
113
 
107
114
  def analyze(request, params = {})
108
- http_client
115
+ response = http_client
109
116
  .headers(accept: "application/json")
110
117
  .post("#{base_url}/_analyze", json: request, params: params)
111
- .parse
118
+
119
+ SearchFlip::JSON.parse(response.to_s)
112
120
  end
113
121
 
114
122
  # Fetches information about the specified index aliases. Raises
@@ -124,10 +132,11 @@ module SearchFlip
124
132
  # @return [Hash] The raw response
125
133
 
126
134
  def get_aliases(index_name: "*", alias_name: "*")
127
- http_client
135
+ response = http_client
128
136
  .headers(accept: "application/json", content_type: "application/json")
129
137
  .get("#{base_url}/#{index_name}/_alias/#{alias_name}")
130
- .parse
138
+
139
+ SearchFlip::JSON.parse(response.to_s)
131
140
  end
132
141
 
133
142
  # Returns whether or not the associated Elasticsearch alias already
@@ -159,10 +168,11 @@ module SearchFlip
159
168
  # @return [Array] The raw response
160
169
 
161
170
  def get_indices(name = "*", params: {})
162
- http_client
171
+ response = http_client
163
172
  .headers(accept: "application/json", content_type: "application/json")
164
173
  .get("#{base_url}/_cat/indices/#{name}", params: params)
165
- .parse
174
+
175
+ SearchFlip::JSON.parse(response.to_s)
166
176
  end
167
177
 
168
178
  alias_method :cat_indices, :get_indices
@@ -259,10 +269,11 @@ module SearchFlip
259
269
  # @return [Hash] The index settings
260
270
 
261
271
  def get_index_settings(index_name)
262
- http_client
272
+ response = http_client
263
273
  .headers(accept: "application/json")
264
274
  .get("#{index_url(index_name)}/_settings")
265
- .parse
275
+
276
+ SearchFlip::JSON.parse(response.to_s)
266
277
  end
267
278
 
268
279
  # Sends a refresh request to Elasticsearch. Raises
@@ -272,7 +283,7 @@ module SearchFlip
272
283
  # @return [Boolean] Returns true or raises SearchFlip::ResponseError
273
284
 
274
285
  def refresh(index_names = nil)
275
- http_client.post("#{index_names ? index_url(Array(index_names).join(",")) : base_url}/_refresh", json: {})
286
+ http_client.post("#{index_names ? index_url(Array(index_names).join(",")) : base_url}/_refresh")
276
287
 
277
288
  true
278
289
  end
@@ -310,7 +321,9 @@ module SearchFlip
310
321
  url = type_name ? type_url(index_name, type_name) : index_url(index_name)
311
322
  params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
312
323
 
313
- http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params).parse
324
+ response = http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params)
325
+
326
+ SearchFlip::JSON.parse(response.to_s)
314
327
  end
315
328
 
316
329
  # Deletes the specified index from Elasticsearch. Raises
@@ -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
 
@@ -618,7 +608,7 @@ module SearchFlip
618
608
  http_request.post("#{target.type_url}/_search", params: request_params, json: request)
619
609
  end
620
610
 
621
- SearchFlip::Response.new(self, http_response.parse)
611
+ SearchFlip::Response.new(self, SearchFlip::JSON.parse(http_response.to_s))
622
612
  rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
623
613
  raise e unless failsafe_value
624
614
 
@@ -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
  #
@@ -1,7 +1,28 @@
1
1
  module SearchFlip
2
- # The SearchFlip::HTTPClient class wraps the http gem, is for internal use
3
- # and responsible for the http request/response handling, ie communicating
4
- # with Elasticsearch.
2
+ # The SearchFlip::HTTPClient class wraps the http gem and responsible for the
3
+ # http request/response handling, ie communicating with Elasticsearch. You
4
+ # only need to use it directly if you need authentication to communicate with
5
+ # Elasticsearch or if you want to set some custom http settings.
6
+ #
7
+ # @example
8
+ # http_client = SearchFlip::HTTPClient.new
9
+ #
10
+ # # Basic Auth
11
+ # http_client = http_client.basic_auth(user: "username", pass: "password")
12
+ #
13
+ # # Raw Auth Header
14
+ # http_client = http_client.auth("Bearer VGhlIEhUVFAgR2VtLCBST0NLUw")
15
+ #
16
+ # # Proxy Settings
17
+ # http_client = http_client.via("proxy.host", 8080)
18
+ #
19
+ # # Custom headers
20
+ # http_client = http_client.headers(key: "value")
21
+ #
22
+ # # Timeouts
23
+ # http_client = http_client.timeout(20)
24
+ #
25
+ # SearchFlip::Connection.new(base_url: "...", http_client: http_client)
5
26
 
6
27
  class HTTPClient
7
28
  attr_accessor :request, :plugins
@@ -14,11 +35,11 @@ module SearchFlip
14
35
  class << self
15
36
  extend Forwardable
16
37
 
17
- def_delegators :new, :headers, :via, :basic_auth, :auth
38
+ def_delegators :new, :headers, :via, :basic_auth, :auth, :timeout
18
39
  def_delegators :new, :get, :post, :put, :delete, :head
19
40
  end
20
41
 
21
- [:headers, :via, :basic_auth, :auth].each do |method|
42
+ [:headers, :via, :basic_auth, :auth, :timeout].each do |method|
22
43
  define_method method do |*args|
23
44
  dup.tap do |client|
24
45
  client.request = request.send(method, *args)
@@ -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,
@@ -455,7 +455,9 @@ module SearchFlip
455
455
  # @return [Hash] The specified document
456
456
 
457
457
  def get(id, params = {})
458
- connection.http_client.headers(accept: "application/json").get("#{type_url}/#{id}", params: params).parse
458
+ response = connection.http_client.headers(accept: "application/json").get("#{type_url}/#{id}", params: params)
459
+
460
+ SearchFlip::JSON.parse(response.to_s)
459
461
  end
460
462
 
461
463
  # Retrieves the documents specified by ids from elasticsearch.
@@ -471,7 +473,9 @@ module SearchFlip
471
473
  # @return [Hash] The raw response
472
474
 
473
475
  def mget(request, params = {})
474
- connection.http_client.headers(accept: "application/json").post("#{type_url}/_mget", json: request, params: params).parse
476
+ response = connection.http_client.headers(accept: "application/json").post("#{type_url}/_mget", json: request, params: params)
477
+
478
+ SearchFlip::JSON.parse(response.to_s)
475
479
  end
476
480
 
477
481
  # Sends an analyze request to Elasticsearch. Raises
@@ -483,7 +487,9 @@ module SearchFlip
483
487
  # @return [Hash] The raw response
484
488
 
485
489
  def analyze(request, params = {})
486
- connection.http_client.headers(accept: "application/json").post("#{index_url}/_analyze", json: request, params: params).parse
490
+ response = connection.http_client.headers(accept: "application/json").post("#{index_url}/_analyze", json: request, params: params)
491
+
492
+ SearchFlip::JSON.parse(response.to_s)
487
493
  end
488
494
 
489
495
  # Sends a index refresh request to Elasticsearch. Raises
@@ -1,16 +1,11 @@
1
1
  module SearchFlip
2
2
  class JSON
3
- @default_options = {
4
- mode: :custom,
5
- use_to_json: true
6
- }
7
-
8
- def self.default_options
9
- @default_options
3
+ def self.generate(obj)
4
+ Oj.dump(obj, SearchFlip::Config[:json_options])
10
5
  end
11
6
 
12
- def self.generate(obj)
13
- Oj.dump(obj, default_options)
7
+ def self.parse(json)
8
+ ::JSON.parse(json)
14
9
  end
15
10
  end
16
11
  end
@@ -1,29 +1 @@
1
- require "time"
2
- require "date"
3
- require "json"
4
-
5
- class Time
6
- def to_json(*args)
7
- iso8601(6).to_json
8
- end
9
- end
10
-
11
- class Date
12
- def to_json(*args)
13
- iso8601.to_json
14
- end
15
- end
16
-
17
- class DateTime
18
- def to_json(*args)
19
- iso8601(6).to_json
20
- end
21
- end
22
-
23
- if defined?(ActiveSupport)
24
- class ActiveSupport::TimeWithZone
25
- def to_json(*args)
26
- iso8601(6).to_json
27
- end
28
- end
29
- end
1
+ warn "[DEPRECATION] Using search_flip/to_json is not neccessary anymore"
@@ -1,3 +1,3 @@
1
1
  module SearchFlip
2
- VERSION = "3.1.1"
2
+ VERSION = "3.4.0"
3
3
  end
data/search_flip.gemspec CHANGED
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
37
37
 
38
38
  spec.add_dependency "hashie"
39
39
  spec.add_dependency "http"
40
+ spec.add_dependency "json"
40
41
  spec.add_dependency "oj"
41
42
  spec.add_dependency "ruby2_keywords"
42
43
  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")
@@ -7,7 +7,7 @@ class HttpTestRequest
7
7
  self.calls = []
8
8
  end
9
9
 
10
- [:via, :basic_auth, :auth].each do |method|
10
+ [:headers, :via, :basic_auth, :auth, :timeout].each do |method|
11
11
  define_method method do |*args|
12
12
  dup.tap do |request|
13
13
  request.calls = calls + [[method, args]]
@@ -20,7 +20,7 @@ RSpec.describe SearchFlip::HTTPClient do
20
20
  describe "delegation" do
21
21
  subject { SearchFlip::HTTPClient }
22
22
 
23
- [:headers, :via, :basic_auth, :auth].each do |method|
23
+ [:headers, :via, :basic_auth, :auth, :timeout].each do |method|
24
24
  it { should delegate(method).to(:new) }
25
25
  end
26
26
 
@@ -56,8 +56,12 @@ RSpec.describe SearchFlip::HTTPClient do
56
56
  end
57
57
  end
58
58
 
59
- [:via, :basic_auth, :auth].each do |method|
59
+ [:headers, :via, :basic_auth, :auth, :timeout].each do |method|
60
60
  describe "##{method}" do
61
+ it "is understood by HTTP" do
62
+ expect(HTTP.respond_to?(method)).to eq(true)
63
+ end
64
+
61
65
  it "creates a dupped instance" do
62
66
  client = SearchFlip::HTTPClient.new
63
67
  client.request = HttpTestRequest.new
@@ -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
 
@@ -0,0 +1,45 @@
1
+ require File.expand_path("../spec_helper", __dir__)
2
+
3
+ RSpec.describe SearchFlip::JSON do
4
+ describe ".generate" do
5
+ it "encodes timestamps correctly" do
6
+ Timecop.freeze "2020-06-01 12:00:00 UTC" do
7
+ expect(described_class.generate(timestamp: Time.now.utc)).to eq('{"timestamp":"2020-06-01T12:00:00.000Z"}')
8
+ end
9
+ end
10
+
11
+ it "encodes bigdecimals as string" do
12
+ expect(described_class.generate(value: BigDecimal(1))).to eq('{"value":"1.0"}')
13
+ end
14
+
15
+ it "delegates to Oj" do
16
+ allow(Oj).to receive(:dump)
17
+
18
+ payload = { key: "value" }
19
+
20
+ described_class.generate(payload)
21
+
22
+ expect(Oj).to have_received(:dump).with(payload, mode: :custom, use_to_json: true, time_format: :xmlschema, bigdecimal_as_decimal: false)
23
+ end
24
+
25
+ it "generates json" do
26
+ expect(described_class.generate(key: "value")).to eq('{"key":"value"}')
27
+ end
28
+ end
29
+
30
+ describe ".parse" do
31
+ it "returns the parsed json payload" do
32
+ expect(described_class.parse('{"key":"value"}')).to eq("key" => "value")
33
+ end
34
+
35
+ it "delegates to JSON" do
36
+ allow(JSON).to receive(:parse)
37
+
38
+ payload = '{"key":"value"}'
39
+
40
+ described_class.parse(payload)
41
+
42
+ expect(JSON).to have_received(:parse).with(payload)
43
+ end
44
+ end
45
+ end
@@ -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
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.1.1
4
+ version: 3.4.0
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-08-21 00:00:00.000000000 Z
11
+ date: 2021-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: json
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: oj
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -213,9 +227,9 @@ executables: []
213
227
  extensions: []
214
228
  extra_rdoc_files: []
215
229
  files:
230
+ - ".github/workflows/test.yml"
216
231
  - ".gitignore"
217
232
  - ".rubocop.yml"
218
- - ".travis.yml"
219
233
  - CHANGELOG.md
220
234
  - Gemfile
221
235
  - LICENSE.txt
@@ -259,11 +273,11 @@ files:
259
273
  - spec/search_flip/criteria_spec.rb
260
274
  - spec/search_flip/http_client_spec.rb
261
275
  - spec/search_flip/index_spec.rb
276
+ - spec/search_flip/json_spec.rb
262
277
  - spec/search_flip/model_spec.rb
263
278
  - spec/search_flip/null_instrumenter_spec.rb
264
279
  - spec/search_flip/response_spec.rb
265
280
  - spec/search_flip/result_spec.rb
266
- - spec/search_flip/to_json_spec.rb
267
281
  - spec/spec_helper.rb
268
282
  homepage: https://github.com/mrkamel/search_flip
269
283
  licenses:
@@ -287,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
287
301
  - !ruby/object:Gem::Version
288
302
  version: '0'
289
303
  requirements: []
290
- rubygems_version: 3.0.3
304
+ rubygems_version: 3.2.3
291
305
  signing_key:
292
306
  specification_version: 4
293
307
  summary: Full-Featured Elasticsearch Ruby Client with a Chainable DSL
@@ -300,9 +314,9 @@ test_files:
300
314
  - spec/search_flip/criteria_spec.rb
301
315
  - spec/search_flip/http_client_spec.rb
302
316
  - spec/search_flip/index_spec.rb
317
+ - spec/search_flip/json_spec.rb
303
318
  - spec/search_flip/model_spec.rb
304
319
  - spec/search_flip/null_instrumenter_spec.rb
305
320
  - spec/search_flip/response_spec.rb
306
321
  - spec/search_flip/result_spec.rb
307
- - spec/search_flip/to_json_spec.rb
308
322
  - spec/spec_helper.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.8.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
@@ -1,28 +0,0 @@
1
- require File.expand_path("../spec_helper", __dir__)
2
- require "search_flip/to_json"
3
-
4
- RSpec.describe "to_json" do
5
- it "uses the correct format for Time" do
6
- Timecop.freeze Time.parse("2018-01-01 12:00:00 UTC") do
7
- expect(Time.now.utc.to_json).to eq("\"2018-01-01T12:00:00.000000Z\"")
8
- end
9
- end
10
-
11
- it "uses the correct format for Date" do
12
- Timecop.freeze Time.parse("2018-01-01 12:00:00 UTC") do
13
- expect(Date.today.to_json).to eq("\"2018-01-01\"")
14
- end
15
- end
16
-
17
- it "uses the correct format for DateTime" do
18
- Timecop.freeze Time.parse("2018-01-01 12:00:00 UTC") do
19
- expect(Time.now.utc.to_json).to eq("\"2018-01-01T12:00:00.000000Z\"")
20
- end
21
- end
22
-
23
- it "uses the correct format for TimeWithZone" do
24
- Timecop.freeze Time.parse("2018-01-01 12:00:00 UTC") do
25
- expect(Time.find_zone("UTC").now.to_json).to eq("\"2018-01-01T12:00:00.000000Z\"")
26
- end
27
- end
28
- end