search_flip 3.2.1 → 4.0.0.beta4

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: fa450fa83c07d1ed7e266ef436c26075a0bd6d8b5fd2904ed98d6b4b3921c6ab
4
- data.tar.gz: b7c06e750f4b73d3ad1c8626485526b9389d95591f6c56b954f5f1f6610dec2d
3
+ metadata.gz: 26839c09be4ddaecd0107d683afc1c16516c0ef562b3e9cb8652b6e0c1818003
4
+ data.tar.gz: 22e8e36e94439280e8cb0caca5ccd0f3f552ec7518bdffaeb43365e5c8cbbea3
5
5
  SHA512:
6
- metadata.gz: cd8bbc2c91f7b35f5b3d54d225082b7d2c4a0b932b1323c4de836adfa8d3118e89bc70ac6f41b27c6dec3fdf200eafec10a7b7c145f3a73ccbc2a2f7c6eb8581
7
- data.tar.gz: 8c6a974604a63b210d6dd8e585549116266db09321745511ffe78cece7fe84284dd1e035c01be685522f3fd118ac21daa714b2fdcb91f00c08d23dbf93adc692
6
+ metadata.gz: 001b1042dd9f7f2d103adc48789c658e12c6a5abb4e223c72d0d405a41159d37cbe2d3a2a4c76efe125befed963470c518e806b31223ca468f700f3b06e6674b
7
+ data.tar.gz: d410d8a68b66496c246dac4b573c463b5fe08c4a998bff84146c6665a622a72ff524384d4ffb80d76ef83464858268816c794ab966ae1bba9ed962dd2ed7a105
@@ -28,6 +28,7 @@ jobs:
28
28
  - uses: actions/setup-ruby@v1
29
29
  with:
30
30
  ruby-version: ${{ matrix.ruby }}
31
+ - run: gem install bundler
31
32
  - run: bundle
32
33
  - run: sleep 10
33
34
  - run: bundle exec rspec
data/.rubocop.yml CHANGED
@@ -1,16 +1,24 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
- TargetRubyVersion: 2.4
3
+ TargetRubyVersion: 2.5
4
+ SuggestExtensions: false
5
+
6
+ Style/CaseLikeIf:
7
+ Enabled: false
4
8
 
5
9
  Layout/EmptyLineBetweenDefs:
6
10
  EmptyLineBetweenClassDefs: false
7
11
 
8
- Gemspec/RequiredRubyVersion:
9
- Enabled: false
12
+ Lint/EmptyBlock:
13
+ Exclude:
14
+ - spec/**/*.rb
10
15
 
11
16
  Style/ExplicitBlockArgument:
12
17
  Enabled: false
13
18
 
19
+ Gemspec/RequiredRubyVersion:
20
+ Enabled: false
21
+
14
22
  Style/HashTransformValues:
15
23
  Enabled: false
16
24
 
data/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
 
2
2
  # CHANGELOG
3
3
 
4
+ ## v4.0.0
5
+
6
+ * [BREAKING] For performance reasons, `SearchFlip::Result` now no longer
7
+ inherits `Hashie::Mash`
8
+ * It no longer supports symbol based access like `result[:id]`
9
+ * It no longer supports question mark methods like `result.title?`
10
+ * It no longer supports method based assignment like `result.some_key = "value"`
11
+ * Added `SearchFlip::Connection#get_cluster_settings` and
12
+ `#update_cluster_settings`
13
+
4
14
  ## v3.2.1
5
15
 
6
16
  * Fix `refresh` having a empty body breaking in elasticsearch 7.11
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 (http-rb and oj only).
56
55
 
57
56
  ## Reference Docs
58
57
 
@@ -483,8 +482,8 @@ end
483
482
  ```
484
483
 
485
484
  Generally, aggregation results returned by Elasticsearch are returned as a
486
- `SearchFlip::Result`, which basically is a `Hashie::Mash`, such that you can
487
- access them via:
485
+ `SearchFlip::Result`, which basically is a Hash with method-like access, such
486
+ that you can access them via:
488
487
 
489
488
  ```ruby
490
489
  query.aggregations(:username)["mrkamel"].revenue.value
@@ -883,53 +882,6 @@ Thus, if your ORM supports `.find_each`, `#id` and `#where` you are already
883
882
  good to go. Otherwise, simply add your custom implementation of those methods
884
883
  that work with whatever ORM you use.
885
884
 
886
- ## Date and Timestamps in JSON
887
-
888
- Elasticsearch requires dates and timestamps to have one of the formats listed
889
- 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).
890
-
891
- However, `JSON.generate` in ruby by default outputs something like:
892
-
893
- ```ruby
894
- JSON.generate(time: Time.now.utc)
895
- # => "{\"time\":\"2018-02-22 18:19:33 UTC\"}"
896
- ```
897
-
898
- This format is not compatible with Elasticsearch by default. If you're on
899
- Rails, ActiveSupport adds its own `#to_json` methods to `Time`, `Date`, etc.
900
- However, ActiveSupport checks whether they are used in combination with
901
- `JSON.generate` or not and adapt:
902
-
903
- ```ruby
904
- Time.now.utc.to_json
905
- => "\"2018-02-22T18:18:22.088Z\""
906
-
907
- JSON.generate(time: Time.now.utc)
908
- => "{\"time\":\"2018-02-22 18:18:59 UTC\"}"
909
- ```
910
-
911
- SearchFlip is using the [Oj gem](https://github.com/ohler55/oj) to generate
912
- JSON. More concretely, SearchFlip is using:
913
-
914
- ```ruby
915
- Oj.dump({ key: "value" }, mode: :custom, use_to_json: true)
916
- ```
917
-
918
- This mitigates the issues if you're on Rails:
919
-
920
- ```ruby
921
- Oj.dump(Time.now, mode: :custom, use_to_json: true)
922
- # => "\"2018-02-22T18:21:21.064Z\""
923
- ```
924
-
925
- However, if you're not on Rails, you need to add `#to_json` methods to `Time`,
926
- `Date` and `DateTime` to get proper serialization. You can either add them on
927
- your own, via other libraries or by simply using:
928
-
929
- ```ruby
930
- require "search_flip/to_json"
931
- ```
932
-
933
885
  ## Feature Support
934
886
 
935
887
  * for Elasticsearch 2.x, the delete-by-query plugin is required to delete
data/UPDATING.md CHANGED
@@ -1,6 +1,46 @@
1
1
 
2
2
  # Updating from previous SearchFlip versions
3
3
 
4
+ ## Update 3.x to 4.x
5
+
6
+ **[BREAKING]** For performance reasons, `SearchFlip::Result` no longer
7
+ inherits `Hashie::Mash`
8
+
9
+ * It no longer supports symbol based access like `result[:id]`
10
+
11
+ 2.x:
12
+
13
+ ```ruby
14
+ CommentIndex.match_all.results.first[:id]
15
+ CommentIndex.aggregate(:tags).aggregations(:tags).values.first[:doc_count]
16
+ ```
17
+
18
+ 3.x
19
+
20
+ ```ruby
21
+ CommentIndex.match_all.results.first["id"] # or .id
22
+ CommentIndex.aggregate(:tags).aggregations(:tags).values.first["doc_count"] # or .doc_count
23
+ ```
24
+
25
+ * It no longer supports question mark methods like `result.title?`
26
+
27
+ 2.x:
28
+
29
+ ```ruby
30
+ CommentIndex.match_all.results.first.is_published?
31
+ ```
32
+
33
+ 3.x
34
+
35
+ ```ruby
36
+ CommentIndex.match_all.results.first.is_published == true
37
+ ```
38
+
39
+ * It no longer supports method based assignment like `result.some_key = "value"`.
40
+
41
+ However, this should not have any practical implications, as changing the
42
+ results is not considered to be a common use case.
43
+
4
44
  ## Update 2.x to 3.x
5
45
 
6
46
  * **[BREAKING]** No longer pass multiple arguments to `#must`, `#must_not`,
data/lib/search_flip.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require "ruby2_keywords"
2
2
  require "forwardable"
3
3
  require "http"
4
- require "hashie"
5
4
  require "thread"
6
5
  require "oj"
7
6
  require "set"
@@ -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
 
@@ -19,6 +19,35 @@ module SearchFlip
19
19
  @version_mutex = Mutex.new
20
20
  end
21
21
 
22
+ # Queries the cluster settings from Elasticsearch
23
+ #
24
+ # @example
25
+ # connection.get_cluster_settings
26
+ # # => { "persistent" => { ... }, ... }
27
+ #
28
+ # @return [Hash] The cluster settings
29
+
30
+ def get_cluster_settings
31
+ response = http_client.get("#{base_url}/_cluster/settings")
32
+
33
+ SearchFlip::JSON.parse(response.to_s)
34
+ end
35
+
36
+ # Updates the cluster settings according to the specified payload
37
+ #
38
+ # @example
39
+ # connection.update_cluster_settings(persistent: { action: { auto_create_index: false } })
40
+ #
41
+ # @param cluster_settings [Hash] The cluster settings
42
+ #
43
+ # @return [Boolean] Returns true or raises SearchFlip::ResponseError
44
+
45
+ def update_cluster_settings(cluster_settings)
46
+ http_client.put("#{base_url}/_cluster/settings", json: cluster_settings)
47
+
48
+ true
49
+ end
50
+
22
51
  # Queries and returns the Elasticsearch version used.
23
52
  #
24
53
  # @example
@@ -28,7 +57,11 @@ module SearchFlip
28
57
 
29
58
  def version
30
59
  @version_mutex.synchronize do
31
- @version ||= http_client.headers(accept: "application/json").get("#{base_url}/").parse["version"]["number"]
60
+ @version ||= begin
61
+ response = http_client.headers(accept: "application/json").get("#{base_url}/")
62
+
63
+ SearchFlip::JSON.parse(response.to_s)["version"]["number"]
64
+ end
32
65
  end
33
66
  end
34
67
 
@@ -40,7 +73,9 @@ module SearchFlip
40
73
  # @return [Hash] The raw response
41
74
 
42
75
  def cluster_health
43
- http_client.headers(accept: "application/json").get("#{base_url}/_cluster/health").parse
76
+ response = http_client.headers(accept: "application/json").get("#{base_url}/_cluster/health")
77
+
78
+ SearchFlip::JSON.parse(response.to_s)
44
79
  end
45
80
 
46
81
  # Uses the Elasticsearch Multi Search API to execute multiple search requests
@@ -71,7 +106,7 @@ module SearchFlip
71
106
  .headers(accept: "application/json", content_type: "application/x-ndjson")
72
107
  .post("#{base_url}/_msearch", body: payload)
73
108
 
74
- raw_response.parse["responses"].map.with_index do |response, index|
109
+ SearchFlip::JSON.parse(raw_response.to_s)["responses"].map.with_index do |response, index|
75
110
  SearchFlip::Response.new(criterias[index], response)
76
111
  end
77
112
  end
@@ -90,10 +125,11 @@ module SearchFlip
90
125
  # @return [Hash] The raw response
91
126
 
92
127
  def update_aliases(payload)
93
- http_client
128
+ response = http_client
94
129
  .headers(accept: "application/json", content_type: "application/json")
95
130
  .post("#{base_url}/_aliases", body: SearchFlip::JSON.generate(payload))
96
- .parse
131
+
132
+ SearchFlip::JSON.parse(response.to_s)
97
133
  end
98
134
 
99
135
  # Sends an analyze request to Elasticsearch. Raises
@@ -105,10 +141,11 @@ module SearchFlip
105
141
  # @return [Hash] The raw response
106
142
 
107
143
  def analyze(request, params = {})
108
- http_client
144
+ response = http_client
109
145
  .headers(accept: "application/json")
110
146
  .post("#{base_url}/_analyze", json: request, params: params)
111
- .parse
147
+
148
+ SearchFlip::JSON.parse(response.to_s)
112
149
  end
113
150
 
114
151
  # Fetches information about the specified index aliases. Raises
@@ -124,10 +161,11 @@ module SearchFlip
124
161
  # @return [Hash] The raw response
125
162
 
126
163
  def get_aliases(index_name: "*", alias_name: "*")
127
- http_client
164
+ response = http_client
128
165
  .headers(accept: "application/json", content_type: "application/json")
129
166
  .get("#{base_url}/#{index_name}/_alias/#{alias_name}")
130
- .parse
167
+
168
+ SearchFlip::JSON.parse(response.to_s)
131
169
  end
132
170
 
133
171
  # Returns whether or not the associated Elasticsearch alias already
@@ -159,10 +197,11 @@ module SearchFlip
159
197
  # @return [Array] The raw response
160
198
 
161
199
  def get_indices(name = "*", params: {})
162
- http_client
200
+ response = http_client
163
201
  .headers(accept: "application/json", content_type: "application/json")
164
202
  .get("#{base_url}/_cat/indices/#{name}", params: params)
165
- .parse
203
+
204
+ SearchFlip::JSON.parse(response.to_s)
166
205
  end
167
206
 
168
207
  alias_method :cat_indices, :get_indices
@@ -259,10 +298,11 @@ module SearchFlip
259
298
  # @return [Hash] The index settings
260
299
 
261
300
  def get_index_settings(index_name)
262
- http_client
301
+ response = http_client
263
302
  .headers(accept: "application/json")
264
303
  .get("#{index_url(index_name)}/_settings")
265
- .parse
304
+
305
+ SearchFlip::JSON.parse(response.to_s)
266
306
  end
267
307
 
268
308
  # Sends a refresh request to Elasticsearch. Raises
@@ -310,7 +350,9 @@ module SearchFlip
310
350
  url = type_name ? type_url(index_name, type_name) : index_url(index_name)
311
351
  params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
312
352
 
313
- http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params).parse
353
+ response = http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params)
354
+
355
+ SearchFlip::JSON.parse(response.to_s)
314
356
  end
315
357
 
316
358
  # Deletes the specified index from Elasticsearch. Raises
@@ -608,7 +608,7 @@ module SearchFlip
608
608
  http_request.post("#{target.type_url}/_search", params: request_params, json: request)
609
609
  end
610
610
 
611
- SearchFlip::Response.new(self, http_response.parse)
611
+ SearchFlip::Response.new(self, SearchFlip::JSON.parse(http_response.to_s))
612
612
  rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
613
613
  raise e unless failsafe_value
614
614
 
@@ -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
@@ -2,7 +2,8 @@ module SearchFlip
2
2
  class JSON
3
3
  @default_options = {
4
4
  mode: :custom,
5
- use_to_json: true
5
+ time_format: :xmlschema,
6
+ bigdecimal_as_decimal: false
6
7
  }
7
8
 
8
9
  def self.default_options
@@ -12,5 +13,9 @@ module SearchFlip
12
13
  def self.generate(obj)
13
14
  Oj.dump(obj, default_options)
14
15
  end
16
+
17
+ def self.parse(json)
18
+ Oj.load(json, default_options)
19
+ end
15
20
  end
16
21
  end
@@ -156,8 +156,7 @@ module SearchFlip
156
156
  end
157
157
 
158
158
  # Returns the results, ie hits, wrapped in a SearchFlip::Result object
159
- # which basically is a Hashie::Mash. Check out the Hashie docs for further
160
- # details.
159
+ # which basically is a Hash with method-like access.
161
160
  #
162
161
  # @example
163
162
  # CommentIndex.search("hello world").results
@@ -166,7 +165,7 @@ module SearchFlip
166
165
  # @return [Array] An array of results
167
166
 
168
167
  def results
169
- @results ||= hits["hits"].map { |hit| Result.from_hit(hit) }
168
+ @results ||= hits["hits"].map { |hit| SearchFlip::Result.from_hit(hit) }
170
169
  end
171
170
 
172
171
  # Returns the named sugggetion, if a name is specified or alle suggestions.
@@ -304,13 +303,13 @@ module SearchFlip
304
303
 
305
304
  @aggregations[key] =
306
305
  if response["aggregations"].nil? || response["aggregations"][key].nil?
307
- Result.new
306
+ SearchFlip::Result.new
308
307
  elsif response["aggregations"][key]["buckets"].is_a?(Array)
309
- response["aggregations"][key]["buckets"].each_with_object({}) { |bucket, hash| hash[bucket["key"]] = Result.new(bucket) }
308
+ response["aggregations"][key]["buckets"].each_with_object({}) { |bucket, hash| hash[bucket["key"]] = SearchFlip::Result.convert(bucket) }
310
309
  elsif response["aggregations"][key]["buckets"].is_a?(Hash)
311
- Result.new response["aggregations"][key]["buckets"]
310
+ SearchFlip::Result.convert(response["aggregations"][key]["buckets"])
312
311
  else
313
- Result.new response["aggregations"][key]
312
+ SearchFlip::Result.convert(response["aggregations"][key])
314
313
  end
315
314
  end
316
315
  end
@@ -1,29 +1,51 @@
1
1
  module SearchFlip
2
- # The SearchFlip::Result class basically is a hash wrapper that uses
3
- # Hashie::Mash to provide convenient method access to the hash attributes.
2
+ # The SearchFlip::Result class is a simple Hash, but extended with
3
+ # method-like access. Keys assigned via methods are stored as strings.
4
+ #
5
+ # @example method access
6
+ # result = SearchFlip::Result.new
7
+ # result["some_key"] = "value"
8
+ # result.some_key # => "value"
4
9
 
5
- class Result < Hashie::Mash
6
- def self.disable_warnings?(*args)
7
- true
8
- end
10
+ class Result < Hash
11
+ def self.convert(hash)
12
+ res = self[hash]
9
13
 
10
- # Creates a SearchFlip::Result object from a raw hit. Useful for e.g.
11
- # top hits aggregations.
12
- #
13
- # @example
14
- # query = ProductIndex.aggregate(top_sales: { top_hits: "..." })
15
- # top_sales_hits = query.aggregations(:top_sales).top_hits.hits.hits
16
- #
17
- # SearchFlip::Result.from_hit(top_sales_hits.first)
14
+ res.each do |key, value|
15
+ if value.is_a?(Hash)
16
+ res[key] = convert(value)
17
+ elsif value.is_a?(Array)
18
+ res[key] = convert_array(value)
19
+ end
20
+ end
18
21
 
19
- def self.from_hit(hit)
20
- raw_result = (hit["_source"] || {}).dup
22
+ res
23
+ end
21
24
 
22
- raw_result["_hit"] = hit.each_with_object({}) do |(key, value), hash|
23
- hash[key] = value if key != "_source"
25
+ def self.convert_array(arr)
26
+ arr.map do |obj|
27
+ if obj.is_a?(Hash)
28
+ convert(obj)
29
+ elsif obj.is_a?(Array)
30
+ convert_array(obj)
31
+ else
32
+ obj
33
+ end
24
34
  end
35
+ end
25
36
 
26
- new(raw_result)
37
+ def method_missing(name, *args, &block)
38
+ self[name.to_s]
39
+ end
40
+
41
+ def respond_to_missing?(name, include_private = false)
42
+ key?(name.to_s) || super
43
+ end
44
+
45
+ def self.from_hit(hit)
46
+ res = convert(hit["_source"] || {})
47
+ res["_hit"] = convert(self[hit].tap { |hash| hash.delete("_source") })
48
+ res
27
49
  end
28
50
  end
29
51
  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.2.1"
2
+ VERSION = "4.0.0.beta4"
3
3
  end
data/search_flip.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.post_install_message = <<~MESSAGE
22
22
  Thanks for using search_flip!
23
- When upgrading to 3.x, please check out
23
+ When upgrading major versions, please check out
24
24
  https://github.com/mrkamel/search_flip/blob/master/UPDATING.md
25
25
  MESSAGE
26
26
 
@@ -35,7 +35,6 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "timecop"
36
36
  spec.add_development_dependency "webmock"
37
37
 
38
- spec.add_dependency "hashie"
39
38
  spec.add_dependency "http"
40
39
  spec.add_dependency "oj"
41
40
  spec.add_dependency "ruby2_keywords"
@@ -15,7 +15,7 @@ RSpec.describe SearchFlip::Aggregation do
15
15
  aggregation.where(title: "title").where(description: "description").aggregate(:category)
16
16
  end
17
17
 
18
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
18
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
19
19
 
20
20
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
21
21
  end
@@ -36,7 +36,7 @@ RSpec.describe SearchFlip::Aggregation do
36
36
  .aggregate(:category)
37
37
  end
38
38
 
39
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
39
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
40
40
 
41
41
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
42
42
  end
@@ -54,7 +54,7 @@ RSpec.describe SearchFlip::Aggregation do
54
54
  aggregation.where(title: "title1".."title3").where(price: 100..200).aggregate(:category)
55
55
  end
56
56
 
57
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
57
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
58
58
 
59
59
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
60
60
  end
@@ -74,7 +74,7 @@ RSpec.describe SearchFlip::Aggregation do
74
74
  aggregation.where_not(title: "title4").where_not(title: "title5").aggregate(:category)
75
75
  end
76
76
 
77
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
77
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
78
78
 
79
79
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
80
80
  end
@@ -94,7 +94,7 @@ RSpec.describe SearchFlip::Aggregation do
94
94
  aggregation.where_not(title: ["title1", "title2"]).where_not(title: ["title6", "title7"]).aggregate(:category)
95
95
  end
96
96
 
97
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
97
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
98
98
 
99
99
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
100
100
  end
@@ -114,7 +114,7 @@ RSpec.describe SearchFlip::Aggregation do
114
114
  aggregation.where_not(price: 100..150).where_not(title: "title6".."title7").aggregate(:category)
115
115
  end
116
116
 
117
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
117
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
118
118
 
119
119
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
120
120
  end
@@ -134,7 +134,7 @@ RSpec.describe SearchFlip::Aggregation do
134
134
  aggregation.filter(range: { price: { gte: 100, lte: 200 } }).filter(term: { title: "title" }).aggregate(:category)
135
135
  end
136
136
 
137
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
137
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
138
138
 
139
139
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
140
140
  end
@@ -156,7 +156,7 @@ RSpec.describe SearchFlip::Aggregation do
156
156
  aggregation.range(:price, gte: 100, lte: 200).range(:title, gte: "title1", lte: "title3").aggregate(:category)
157
157
  end
158
158
 
159
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
159
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
160
160
 
161
161
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
162
162
  end
@@ -174,7 +174,7 @@ RSpec.describe SearchFlip::Aggregation do
174
174
  aggregation.match_all.aggregate(:category)
175
175
  end
176
176
 
177
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
177
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
178
178
 
179
179
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
180
180
  end
@@ -194,7 +194,7 @@ RSpec.describe SearchFlip::Aggregation do
194
194
  aggregation.exists(:title).exists(:price).aggregate(:category)
195
195
  end
196
196
 
197
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
197
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
198
198
 
199
199
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
200
200
  end
@@ -214,7 +214,7 @@ RSpec.describe SearchFlip::Aggregation do
214
214
  aggregation.exists_not(:title).exists_not(:price).aggregate(:category)
215
215
  end
216
216
 
217
- aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
217
+ aggregations = query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket["key"]] = bucket.doc_count }
218
218
 
219
219
  expect(aggregations).to eq("category1" => 2, "category2" => 1)
220
220
  end
@@ -244,21 +244,21 @@ RSpec.describe SearchFlip::Aggregation do
244
244
  expect(aggregations).to eq("category1" => 3, "category2" => 3)
245
245
 
246
246
  aggregations = query.aggregations(:category)["category1"].title.buckets.each_with_object({}) do |bucket, hash|
247
- hash[bucket[:key]] = bucket.doc_count
247
+ hash[bucket["key"]] = bucket.doc_count
248
248
  end
249
249
 
250
250
  expect(aggregations).to eq("title1" => 2, "title2" => 1)
251
251
 
252
252
  aggregations = query.aggregations(:category)["category2"].title.buckets.each_with_object({}) do |bucket, hash|
253
- hash[bucket[:key]] = bucket.doc_count
253
+ hash[bucket["key"]] = bucket.doc_count
254
254
  end
255
255
 
256
256
  expect(aggregations).to eq("title1" => 1, "title2" => 2)
257
257
 
258
- expect(query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket[:key] == "title1" }.price.value).to eq(30)
259
- expect(query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket[:key] == "title2" }.price.value).to eq(15)
260
- expect(query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket[:key] == "title1" }.price.value).to eq(30)
261
- expect(query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket[:key] == "title2" }.price.value).to eq(60)
258
+ expect(query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket["key"] == "title1" }.price.value).to eq(30)
259
+ expect(query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket["key"] == "title2" }.price.value).to eq(15)
260
+ expect(query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket["key"] == "title1" }.price.value).to eq(30)
261
+ expect(query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket["key"] == "title2" }.price.value).to eq(60)
262
262
  end
263
263
  end
264
264
 
@@ -19,6 +19,35 @@ RSpec.describe SearchFlip::Connection do
19
19
  end
20
20
  end
21
21
 
22
+ describe "#get_cluster_settings" do
23
+ it "returns the cluster settings" do
24
+ expect(SearchFlip::Connection.new.get_cluster_settings).to be_kind_of(Hash)
25
+ end
26
+ end
27
+
28
+ describe "#update_cluster_settings" do
29
+ let(:connection) { SearchFlip::Connection.new }
30
+
31
+ after do
32
+ connection.update_cluster_settings(persistent: { action: { auto_create_index: false } }) if connection.version.to_i > 2
33
+ end
34
+
35
+ it "updates the cluster settings" do
36
+ if connection.version.to_i > 2
37
+ connection.update_cluster_settings(persistent: { action: { auto_create_index: false } })
38
+ connection.update_cluster_settings(persistent: { action: { auto_create_index: true } })
39
+
40
+ expect(connection.get_cluster_settings["persistent"]["action"]["auto_create_index"]).to eq("true")
41
+ end
42
+ end
43
+
44
+ it "returns true" do
45
+ if connection.version.to_i > 2
46
+ expect(connection.update_cluster_settings(persistent: { action: { auto_create_index: false } })).to eq(true)
47
+ end
48
+ end
49
+ end
50
+
22
51
  describe "#msearch" do
23
52
  it "sends multiple queries and returns all responses" do
24
53
  ProductIndex.import create(:product)
@@ -1313,7 +1313,7 @@ RSpec.describe SearchFlip::Criteria do
1313
1313
  ProductIndex.import create(:product)
1314
1314
 
1315
1315
  query = ProductIndex.match_all.explain(true)
1316
- expect(query.results.first._hit.key?(:_explanation)).to eq(true)
1316
+ expect(query.results.first._hit.key?("_explanation")).to eq(true)
1317
1317
  end
1318
1318
  end
1319
1319
 
@@ -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:00Z"}')
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, 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 Oj" do
36
+ allow(Oj).to receive(:load)
37
+
38
+ payload = '{"key":"value"}'
39
+
40
+ described_class.parse(payload)
41
+
42
+ expect(Oj).to have_received(:load).with(payload, mode: :custom, time_format: :xmlschema, bigdecimal_as_decimal: false)
43
+ end
44
+ end
45
+ end
@@ -1,6 +1,29 @@
1
1
  require File.expand_path("../spec_helper", __dir__)
2
2
 
3
3
  RSpec.describe SearchFlip::Result do
4
+ describe ".convert" do
5
+ it "deeply converts hashes and arrays" do
6
+ result = described_class.convert("parent" => { "child" => [{ "key1" => "value" }, { "key2" => 3 }] })
7
+
8
+ expect(result.parent.child[0].key1).to eq("value")
9
+ expect(result.parent.child[1].key2).to eq(3)
10
+ end
11
+ end
12
+
13
+ describe "#method_missing" do
14
+ it "returns the value of the key equal to the message name" do
15
+ expect(described_class.convert("some_key" => "value").some_key).to eq("value")
16
+ expect(described_class.new.some_key).to be_nil
17
+ end
18
+ end
19
+
20
+ describe "#responds_to_missing?" do
21
+ it "returns true/false if the key equal to the message name is present or not" do
22
+ expect(described_class.convert("some_key" => nil).respond_to?(:some_key)).to eq(true)
23
+ expect(described_class.convert("some_key" => nil).respond_to?(:other_key)).to eq(false)
24
+ end
25
+ end
26
+
4
27
  describe ".from_hit" do
5
28
  it "adds a _hit key into _source and merges the hit keys into it" do
6
29
  result = SearchFlip::Result.from_hit("_score" => 1.0, "_source" => { "name" => "Some name" })
@@ -8,12 +31,6 @@ RSpec.describe SearchFlip::Result do
8
31
  expect(result).to eq("name" => "Some name", "_hit" => { "_score" => 1.0 })
9
32
  end
10
33
 
11
- it "allows deep method access" do
12
- result = SearchFlip::Result.from_hit("_source" => { "key1" => [{ "key2" => "value" }] })
13
-
14
- expect(result.key1[0].key2).to eq("value")
15
- end
16
-
17
34
  it "works with the _source being disabled" do
18
35
  result = SearchFlip::Result.from_hit("_id" => 1)
19
36
 
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.2.1
4
+ version: 4.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Vetter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-13 00:00:00.000000000 Z
11
+ date: 2021-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -150,20 +150,6 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: hashie
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :runtime
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
153
  - !ruby/object:Gem::Dependency
168
154
  name: http
169
155
  requirement: !ruby/object:Gem::Requirement
@@ -259,11 +245,11 @@ files:
259
245
  - spec/search_flip/criteria_spec.rb
260
246
  - spec/search_flip/http_client_spec.rb
261
247
  - spec/search_flip/index_spec.rb
248
+ - spec/search_flip/json_spec.rb
262
249
  - spec/search_flip/model_spec.rb
263
250
  - spec/search_flip/null_instrumenter_spec.rb
264
251
  - spec/search_flip/response_spec.rb
265
252
  - spec/search_flip/result_spec.rb
266
- - spec/search_flip/to_json_spec.rb
267
253
  - spec/spec_helper.rb
268
254
  homepage: https://github.com/mrkamel/search_flip
269
255
  licenses:
@@ -271,7 +257,7 @@ licenses:
271
257
  metadata: {}
272
258
  post_install_message: |
273
259
  Thanks for using search_flip!
274
- When upgrading to 3.x, please check out
260
+ When upgrading major versions, please check out
275
261
  https://github.com/mrkamel/search_flip/blob/master/UPDATING.md
276
262
  rdoc_options: []
277
263
  require_paths:
@@ -283,9 +269,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
283
269
  version: '0'
284
270
  required_rubygems_version: !ruby/object:Gem::Requirement
285
271
  requirements:
286
- - - ">="
272
+ - - ">"
287
273
  - !ruby/object:Gem::Version
288
- version: '0'
274
+ version: 1.3.1
289
275
  requirements: []
290
276
  rubygems_version: 3.2.3
291
277
  signing_key:
@@ -300,9 +286,9 @@ test_files:
300
286
  - spec/search_flip/criteria_spec.rb
301
287
  - spec/search_flip/http_client_spec.rb
302
288
  - spec/search_flip/index_spec.rb
289
+ - spec/search_flip/json_spec.rb
303
290
  - spec/search_flip/model_spec.rb
304
291
  - spec/search_flip/null_instrumenter_spec.rb
305
292
  - spec/search_flip/response_spec.rb
306
293
  - spec/search_flip/result_spec.rb
307
- - spec/search_flip/to_json_spec.rb
308
294
  - spec/spec_helper.rb
@@ -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