search_flip 3.4.0 → 4.0.0.beta
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 +4 -4
- data/.github/workflows/test.yml +2 -3
- data/.rubocop.yml +4 -5
- data/CHANGELOG.md +6 -17
- data/README.md +35 -41
- data/UPDATING.md +40 -0
- data/lib/search_flip.rb +0 -2
- data/lib/search_flip/bulk.rb +1 -1
- data/lib/search_flip/config.rb +1 -7
- data/lib/search_flip/connection.rb +14 -27
- data/lib/search_flip/filterable.rb +0 -16
- data/lib/search_flip/http_client.rb +5 -26
- data/lib/search_flip/index.rb +4 -6
- data/lib/search_flip/json.rb +3 -3
- data/lib/search_flip/response.rb +6 -7
- data/lib/search_flip/result.rb +45 -19
- data/lib/search_flip/to_json.rb +29 -1
- data/lib/search_flip/version.rb +1 -1
- data/search_flip.gemspec +0 -2
- data/spec/search_flip/aggregation_spec.rb +17 -17
- data/spec/search_flip/criteria_spec.rb +1 -13
- data/spec/search_flip/http_client_spec.rb +3 -7
- data/spec/search_flip/index_spec.rb +4 -17
- data/spec/search_flip/json_spec.rb +4 -18
- data/spec/search_flip/null_instrumenter_spec.rb +2 -2
- data/spec/search_flip/result_spec.rb +23 -6
- data/spec/search_flip/to_json_spec.rb +28 -0
- metadata +7 -33
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b023bc6411647eaecfcb8f5b0fe535379974a0824d49699dd74ef867a67c69eb
|
|
4
|
+
data.tar.gz: 5dc4a0439f9442811f3966e86b0c6b09d09c54450a04234c3738ae0ab7935900
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5fc637e8c016531df22af678adcae60a4f4169e5f1d2215d52be0a0dff1111b849cb4d79ca36d6cbf315c8516bc51d543852ab5026339d59cb1f5d08ba356f7
|
|
7
|
+
data.tar.gz: 7cf9d55a9aca5d6d63401750d40bc656fcc345101d8ce92a300c24649e38810d399d43ff225611bbdd91a257d6cb15eee26a6c811974b987e92e72e67eded704
|
data/.github/workflows/test.yml
CHANGED
|
@@ -11,11 +11,11 @@ jobs:
|
|
|
11
11
|
- elasticsearch:5.4
|
|
12
12
|
- docker.elastic.co/elasticsearch/elasticsearch:6.7.0
|
|
13
13
|
- docker.elastic.co/elasticsearch/elasticsearch:7.0.0
|
|
14
|
-
- docker.elastic.co/elasticsearch/elasticsearch:7.
|
|
14
|
+
- docker.elastic.co/elasticsearch/elasticsearch:7.9.0
|
|
15
15
|
ruby:
|
|
16
|
+
- 2.5
|
|
16
17
|
- 2.6
|
|
17
18
|
- 2.7
|
|
18
|
-
- 3.0
|
|
19
19
|
services:
|
|
20
20
|
elasticsearch:
|
|
21
21
|
image: ${{ matrix.elasticsearch }}
|
|
@@ -28,7 +28,6 @@ jobs:
|
|
|
28
28
|
- uses: actions/setup-ruby@v1
|
|
29
29
|
with:
|
|
30
30
|
ruby-version: ${{ matrix.ruby }}
|
|
31
|
-
- run: gem install bundler
|
|
32
31
|
- run: bundle
|
|
33
32
|
- run: sleep 10
|
|
34
33
|
- run: bundle exec rspec
|
data/.rubocop.yml
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
NewCops: enable
|
|
3
|
-
TargetRubyVersion: 2.
|
|
4
|
-
SuggestExtensions: false
|
|
5
|
-
|
|
6
|
-
Layout/EmptyLineBetweenDefs:
|
|
7
|
-
EmptyLineBetweenClassDefs: false
|
|
3
|
+
TargetRubyVersion: 2.4
|
|
8
4
|
|
|
9
5
|
Gemspec/RequiredRubyVersion:
|
|
10
6
|
Enabled: false
|
|
11
7
|
|
|
8
|
+
Style/CaseLikeIf:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
12
11
|
Style/ExplicitBlockArgument:
|
|
13
12
|
Enabled: false
|
|
14
13
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
|
|
2
2
|
# CHANGELOG
|
|
3
3
|
|
|
4
|
-
##
|
|
4
|
+
## v4.0.0
|
|
5
5
|
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
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`
|
|
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"`
|
|
22
11
|
|
|
23
12
|
## v3.1.2
|
|
24
13
|
|
data/README.md
CHANGED
|
@@ -51,7 +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.
|
|
54
|
+
Finally, SearchFlip comes with a minimal set of dependencies (http-rb and oj only).
|
|
55
55
|
|
|
56
56
|
## Reference Docs
|
|
57
57
|
|
|
@@ -418,14 +418,6 @@ Simply matches all documents:
|
|
|
418
418
|
CommentIndex.match_all
|
|
419
419
|
```
|
|
420
420
|
|
|
421
|
-
* `match_none`
|
|
422
|
-
|
|
423
|
-
Simply matches none documents at all:
|
|
424
|
-
|
|
425
|
-
```ruby
|
|
426
|
-
CommentIndex.match_none
|
|
427
|
-
```
|
|
428
|
-
|
|
429
421
|
* `all`
|
|
430
422
|
|
|
431
423
|
Simply returns the criteria as is or an empty criteria when called on the index
|
|
@@ -482,8 +474,8 @@ end
|
|
|
482
474
|
```
|
|
483
475
|
|
|
484
476
|
Generally, aggregation results returned by Elasticsearch are returned as a
|
|
485
|
-
`SearchFlip::Result`, which basically is a
|
|
486
|
-
access them via:
|
|
477
|
+
`SearchFlip::Result`, which basically is a Hash with method-like access, such
|
|
478
|
+
that you can access them via:
|
|
487
479
|
|
|
488
480
|
```ruby
|
|
489
481
|
query.aggregations(:username)["mrkamel"].revenue.value
|
|
@@ -756,7 +748,7 @@ end
|
|
|
756
748
|
This allows to use different clusters per index e.g. when migrating indices to
|
|
757
749
|
new versions of Elasticsearch.
|
|
758
750
|
|
|
759
|
-
You can specify basic auth, additional headers,
|
|
751
|
+
You can specify basic auth, additional headers, etc via:
|
|
760
752
|
|
|
761
753
|
```ruby
|
|
762
754
|
http_client = SearchFlip::HTTPClient.new
|
|
@@ -773,9 +765,6 @@ http_client = http_client.via("proxy.host", 8080)
|
|
|
773
765
|
# Custom headers
|
|
774
766
|
http_client = http_client.headers(key: "value")
|
|
775
767
|
|
|
776
|
-
# Timeouts
|
|
777
|
-
http_client = http_client.timeout(20)
|
|
778
|
-
|
|
779
768
|
SearchFlip::Connection.new(base_url: "...", http_client: http_client)
|
|
780
769
|
```
|
|
781
770
|
|
|
@@ -885,52 +874,57 @@ Thus, if your ORM supports `.find_each`, `#id` and `#where` you are already
|
|
|
885
874
|
good to go. Otherwise, simply add your custom implementation of those methods
|
|
886
875
|
that work with whatever ORM you use.
|
|
887
876
|
|
|
888
|
-
## JSON
|
|
877
|
+
## Date and Timestamps in JSON
|
|
889
878
|
|
|
890
|
-
|
|
891
|
-
|
|
879
|
+
Elasticsearch requires dates and timestamps to have one of the formats listed
|
|
880
|
+
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).
|
|
881
|
+
|
|
882
|
+
However, `JSON.generate` in ruby by default outputs something like:
|
|
892
883
|
|
|
893
884
|
```ruby
|
|
894
|
-
|
|
885
|
+
JSON.generate(time: Time.now.utc)
|
|
886
|
+
# => "{\"time\":\"2018-02-22 18:19:33 UTC\"}"
|
|
895
887
|
```
|
|
896
888
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
889
|
+
This format is not compatible with Elasticsearch by default. If you're on
|
|
890
|
+
Rails, ActiveSupport adds its own `#to_json` methods to `Time`, `Date`, etc.
|
|
891
|
+
However, ActiveSupport checks whether they are used in combination with
|
|
892
|
+
`JSON.generate` or not and adapt:
|
|
901
893
|
|
|
902
894
|
```ruby
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
}
|
|
895
|
+
Time.now.utc.to_json
|
|
896
|
+
=> "\"2018-02-22T18:18:22.088Z\""
|
|
897
|
+
|
|
898
|
+
JSON.generate(time: Time.now.utc)
|
|
899
|
+
=> "{\"time\":\"2018-02-22 18:18:59 UTC\"}"
|
|
909
900
|
```
|
|
910
901
|
|
|
911
|
-
|
|
902
|
+
SearchFlip is using the [Oj gem](https://github.com/ohler55/oj) to generate
|
|
903
|
+
JSON. More concretely, SearchFlip is using:
|
|
912
904
|
|
|
913
905
|
```ruby
|
|
914
|
-
|
|
915
|
-
|
|
906
|
+
Oj.dump({ key: "value" }, mode: :custom, use_to_json: true)
|
|
907
|
+
```
|
|
916
908
|
|
|
917
|
-
|
|
918
|
-
{
|
|
919
|
-
# ...
|
|
909
|
+
This mitigates the issues if you're on Rails:
|
|
920
910
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
end
|
|
911
|
+
```ruby
|
|
912
|
+
Oj.dump(Time.now, mode: :custom, use_to_json: true)
|
|
913
|
+
# => "\"2018-02-22T18:21:21.064Z\""
|
|
925
914
|
```
|
|
926
915
|
|
|
927
|
-
|
|
916
|
+
However, if you're not on Rails, you need to add `#to_json` methods to `Time`,
|
|
917
|
+
`Date` and `DateTime` to get proper serialization. You can either add them on
|
|
918
|
+
your own, via other libraries or by simply using:
|
|
919
|
+
|
|
920
|
+
```ruby
|
|
921
|
+
require "search_flip/to_json"
|
|
922
|
+
```
|
|
928
923
|
|
|
929
924
|
## Feature Support
|
|
930
925
|
|
|
931
926
|
* for Elasticsearch 2.x, the delete-by-query plugin is required to delete
|
|
932
927
|
records via queries
|
|
933
|
-
* `#match_none` is only available with Elasticsearch >= 5
|
|
934
928
|
* `#track_total_hits` is only available with Elasticsearch >= 7
|
|
935
929
|
|
|
936
930
|
## Keeping your Models and Indices in Sync
|
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
data/lib/search_flip/bulk.rb
CHANGED
data/lib/search_flip/config.rb
CHANGED
|
@@ -5,12 +5,6 @@ module SearchFlip
|
|
|
5
5
|
bulk_limit: 1_000,
|
|
6
6
|
bulk_max_mb: 100,
|
|
7
7
|
auto_refresh: false,
|
|
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
|
-
}
|
|
8
|
+
instrumenter: NullInstrumenter.new
|
|
15
9
|
}
|
|
16
10
|
end
|
|
@@ -28,11 +28,7 @@ module SearchFlip
|
|
|
28
28
|
|
|
29
29
|
def version
|
|
30
30
|
@version_mutex.synchronize do
|
|
31
|
-
@version ||=
|
|
32
|
-
response = http_client.headers(accept: "application/json").get("#{base_url}/")
|
|
33
|
-
|
|
34
|
-
SearchFlip::JSON.parse(response.to_s)["version"]["number"]
|
|
35
|
-
end
|
|
31
|
+
@version ||= http_client.headers(accept: "application/json").get("#{base_url}/").parse["version"]["number"]
|
|
36
32
|
end
|
|
37
33
|
end
|
|
38
34
|
|
|
@@ -44,9 +40,7 @@ module SearchFlip
|
|
|
44
40
|
# @return [Hash] The raw response
|
|
45
41
|
|
|
46
42
|
def cluster_health
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
43
|
+
http_client.headers(accept: "application/json").get("#{base_url}/_cluster/health").parse
|
|
50
44
|
end
|
|
51
45
|
|
|
52
46
|
# Uses the Elasticsearch Multi Search API to execute multiple search requests
|
|
@@ -96,11 +90,10 @@ module SearchFlip
|
|
|
96
90
|
# @return [Hash] The raw response
|
|
97
91
|
|
|
98
92
|
def update_aliases(payload)
|
|
99
|
-
|
|
93
|
+
http_client
|
|
100
94
|
.headers(accept: "application/json", content_type: "application/json")
|
|
101
95
|
.post("#{base_url}/_aliases", body: SearchFlip::JSON.generate(payload))
|
|
102
|
-
|
|
103
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
96
|
+
.parse
|
|
104
97
|
end
|
|
105
98
|
|
|
106
99
|
# Sends an analyze request to Elasticsearch. Raises
|
|
@@ -112,11 +105,10 @@ module SearchFlip
|
|
|
112
105
|
# @return [Hash] The raw response
|
|
113
106
|
|
|
114
107
|
def analyze(request, params = {})
|
|
115
|
-
|
|
108
|
+
http_client
|
|
116
109
|
.headers(accept: "application/json")
|
|
117
110
|
.post("#{base_url}/_analyze", json: request, params: params)
|
|
118
|
-
|
|
119
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
111
|
+
.parse
|
|
120
112
|
end
|
|
121
113
|
|
|
122
114
|
# Fetches information about the specified index aliases. Raises
|
|
@@ -132,11 +124,10 @@ module SearchFlip
|
|
|
132
124
|
# @return [Hash] The raw response
|
|
133
125
|
|
|
134
126
|
def get_aliases(index_name: "*", alias_name: "*")
|
|
135
|
-
|
|
127
|
+
http_client
|
|
136
128
|
.headers(accept: "application/json", content_type: "application/json")
|
|
137
129
|
.get("#{base_url}/#{index_name}/_alias/#{alias_name}")
|
|
138
|
-
|
|
139
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
130
|
+
.parse
|
|
140
131
|
end
|
|
141
132
|
|
|
142
133
|
# Returns whether or not the associated Elasticsearch alias already
|
|
@@ -168,11 +159,10 @@ module SearchFlip
|
|
|
168
159
|
# @return [Array] The raw response
|
|
169
160
|
|
|
170
161
|
def get_indices(name = "*", params: {})
|
|
171
|
-
|
|
162
|
+
http_client
|
|
172
163
|
.headers(accept: "application/json", content_type: "application/json")
|
|
173
164
|
.get("#{base_url}/_cat/indices/#{name}", params: params)
|
|
174
|
-
|
|
175
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
165
|
+
.parse
|
|
176
166
|
end
|
|
177
167
|
|
|
178
168
|
alias_method :cat_indices, :get_indices
|
|
@@ -269,11 +259,10 @@ module SearchFlip
|
|
|
269
259
|
# @return [Hash] The index settings
|
|
270
260
|
|
|
271
261
|
def get_index_settings(index_name)
|
|
272
|
-
|
|
262
|
+
http_client
|
|
273
263
|
.headers(accept: "application/json")
|
|
274
264
|
.get("#{index_url(index_name)}/_settings")
|
|
275
|
-
|
|
276
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
265
|
+
.parse
|
|
277
266
|
end
|
|
278
267
|
|
|
279
268
|
# Sends a refresh request to Elasticsearch. Raises
|
|
@@ -283,7 +272,7 @@ module SearchFlip
|
|
|
283
272
|
# @return [Boolean] Returns true or raises SearchFlip::ResponseError
|
|
284
273
|
|
|
285
274
|
def refresh(index_names = nil)
|
|
286
|
-
http_client.post("#{index_names ? index_url(Array(index_names).join(",")) : base_url}/_refresh")
|
|
275
|
+
http_client.post("#{index_names ? index_url(Array(index_names).join(",")) : base_url}/_refresh", json: {})
|
|
287
276
|
|
|
288
277
|
true
|
|
289
278
|
end
|
|
@@ -321,9 +310,7 @@ module SearchFlip
|
|
|
321
310
|
url = type_name ? type_url(index_name, type_name) : index_url(index_name)
|
|
322
311
|
params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
|
|
323
312
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
313
|
+
http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params).parse
|
|
327
314
|
end
|
|
328
315
|
|
|
329
316
|
# Deletes the specified index from Elasticsearch. Raises
|
|
@@ -235,22 +235,6 @@ 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
|
-
|
|
254
238
|
# Adds an exists filter to the criteria, which selects all documents for
|
|
255
239
|
# which the specified field has a non-null value.
|
|
256
240
|
#
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
module SearchFlip
|
|
2
|
-
# The SearchFlip::HTTPClient class wraps the http gem
|
|
3
|
-
# http request/response handling, ie communicating
|
|
4
|
-
#
|
|
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)
|
|
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.
|
|
26
5
|
|
|
27
6
|
class HTTPClient
|
|
28
7
|
attr_accessor :request, :plugins
|
|
@@ -35,11 +14,11 @@ module SearchFlip
|
|
|
35
14
|
class << self
|
|
36
15
|
extend Forwardable
|
|
37
16
|
|
|
38
|
-
def_delegators :new, :headers, :via, :basic_auth, :auth
|
|
17
|
+
def_delegators :new, :headers, :via, :basic_auth, :auth
|
|
39
18
|
def_delegators :new, :get, :post, :put, :delete, :head
|
|
40
19
|
end
|
|
41
20
|
|
|
42
|
-
[:headers, :via, :basic_auth, :auth
|
|
21
|
+
[:headers, :via, :basic_auth, :auth].each do |method|
|
|
43
22
|
define_method method do |*args|
|
|
44
23
|
dup.tap do |client|
|
|
45
24
|
client.request = request.send(method, *args)
|
data/lib/search_flip/index.rb
CHANGED
|
@@ -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
|
|
156
|
+
return enum_for(:each_record, 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, :
|
|
251
|
-
:
|
|
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,
|
|
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,
|
|
@@ -487,9 +487,7 @@ module SearchFlip
|
|
|
487
487
|
# @return [Hash] The raw response
|
|
488
488
|
|
|
489
489
|
def analyze(request, params = {})
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
SearchFlip::JSON.parse(response.to_s)
|
|
490
|
+
connection.http_client.headers(accept: "application/json").post("#{index_url}/_analyze", json: request, params: params).parse
|
|
493
491
|
end
|
|
494
492
|
|
|
495
493
|
# Sends a index refresh request to Elasticsearch. Raises
|
data/lib/search_flip/json.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module SearchFlip
|
|
2
2
|
class JSON
|
|
3
3
|
def self.generate(obj)
|
|
4
|
-
Oj.dump(obj,
|
|
4
|
+
Oj.dump(obj, mode: :custom, use_to_json: true)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
def self.parse(
|
|
8
|
-
|
|
7
|
+
def self.parse(str)
|
|
8
|
+
Oj.load(str)
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
end
|
data/lib/search_flip/response.rb
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
310
|
+
SearchFlip::Result.convert(response["aggregations"][key]["buckets"])
|
|
312
311
|
else
|
|
313
|
-
Result.
|
|
312
|
+
SearchFlip::Result.convert(response["aggregations"][key])
|
|
314
313
|
end
|
|
315
314
|
end
|
|
316
315
|
end
|
data/lib/search_flip/result.rb
CHANGED
|
@@ -1,29 +1,55 @@
|
|
|
1
1
|
module SearchFlip
|
|
2
|
-
# The SearchFlip::Result class
|
|
3
|
-
#
|
|
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 <
|
|
6
|
-
def self.
|
|
7
|
-
|
|
8
|
-
end
|
|
10
|
+
class Result < Hash
|
|
11
|
+
def self.convert(hash)
|
|
12
|
+
res = self[hash]
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
20
|
-
|
|
22
|
+
res
|
|
23
|
+
end
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
36
|
+
|
|
37
|
+
# rubocop:disable Lint/MissingSuper
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
def method_missing(name, *args, &block)
|
|
40
|
+
self[name.to_s]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# rubocop:enable Lint/MissingSuper
|
|
44
|
+
|
|
45
|
+
def respond_to_missing?(name, include_private = false)
|
|
46
|
+
key?(name.to_s) || super
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.from_hit(hit)
|
|
50
|
+
res = convert(hit["_source"] || {})
|
|
51
|
+
res["_hit"] = convert(self[hit].tap { |hash| hash.delete("_source") })
|
|
52
|
+
res
|
|
27
53
|
end
|
|
28
54
|
end
|
|
29
55
|
end
|
data/lib/search_flip/to_json.rb
CHANGED
|
@@ -1 +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
|
data/lib/search_flip/version.rb
CHANGED
data/search_flip.gemspec
CHANGED
|
@@ -35,9 +35,7 @@ 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
|
-
spec.add_dependency "json"
|
|
41
39
|
spec.add_dependency "oj"
|
|
42
40
|
spec.add_dependency "ruby2_keywords"
|
|
43
41
|
end
|
|
@@ -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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
259
|
-
expect(query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket[
|
|
260
|
-
expect(query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket[
|
|
261
|
-
expect(query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket[
|
|
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
|
|
|
@@ -416,18 +416,6 @@ RSpec.describe SearchFlip::Criteria do
|
|
|
416
416
|
end
|
|
417
417
|
end
|
|
418
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
|
-
|
|
431
419
|
describe "#exists" do
|
|
432
420
|
it "sets up the constraints correctly and is chainable" do
|
|
433
421
|
product1 = create(:product, title: "title1", description: "description1")
|
|
@@ -1313,7 +1301,7 @@ RSpec.describe SearchFlip::Criteria do
|
|
|
1313
1301
|
ProductIndex.import create(:product)
|
|
1314
1302
|
|
|
1315
1303
|
query = ProductIndex.match_all.explain(true)
|
|
1316
|
-
expect(query.results.first._hit.key?(
|
|
1304
|
+
expect(query.results.first._hit.key?("_explanation")).to eq(true)
|
|
1317
1305
|
end
|
|
1318
1306
|
end
|
|
1319
1307
|
|
|
@@ -7,7 +7,7 @@ class HttpTestRequest
|
|
|
7
7
|
self.calls = []
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
[:
|
|
10
|
+
[:via, :basic_auth, :auth].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
|
|
23
|
+
[:headers, :via, :basic_auth, :auth].each do |method|
|
|
24
24
|
it { should delegate(method).to(:new) }
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -56,12 +56,8 @@ RSpec.describe SearchFlip::HTTPClient do
|
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
[:
|
|
59
|
+
[:via, :basic_auth, :auth].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
|
-
|
|
65
61
|
it "creates a dupped instance" do
|
|
66
62
|
client = SearchFlip::HTTPClient.new
|
|
67
63
|
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, :
|
|
9
|
-
:
|
|
8
|
+
:all, :profile, :where, :where_not, :filter, :range, :match_all, :exists,
|
|
9
|
+
: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,18 +211,12 @@ 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)
|
|
214
|
+
allow(TestIndex.connection).to receive(:update_mapping).and_call_original
|
|
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
|
|
226
220
|
end
|
|
227
221
|
end
|
|
228
222
|
|
|
@@ -264,19 +258,12 @@ RSpec.describe SearchFlip::Index do
|
|
|
264
258
|
TestIndex.create_index
|
|
265
259
|
TestIndex.update_mapping
|
|
266
260
|
|
|
267
|
-
allow(TestIndex.connection).to receive(:get_mapping)
|
|
261
|
+
allow(TestIndex.connection).to receive(:get_mapping).and_call_original
|
|
268
262
|
|
|
269
263
|
TestIndex.get_mapping
|
|
270
264
|
|
|
271
265
|
expect(TestIndex.connection).to have_received(:get_mapping).with("test", type_name: "test")
|
|
272
266
|
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
|
|
280
267
|
end
|
|
281
268
|
end
|
|
282
269
|
|
|
@@ -2,16 +2,6 @@ require File.expand_path("../spec_helper", __dir__)
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe SearchFlip::JSON do
|
|
4
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
5
|
it "delegates to Oj" do
|
|
16
6
|
allow(Oj).to receive(:dump)
|
|
17
7
|
|
|
@@ -19,7 +9,7 @@ RSpec.describe SearchFlip::JSON do
|
|
|
19
9
|
|
|
20
10
|
described_class.generate(payload)
|
|
21
11
|
|
|
22
|
-
expect(Oj).to have_received(:dump).with(payload, mode: :custom, use_to_json: true
|
|
12
|
+
expect(Oj).to have_received(:dump).with(payload, mode: :custom, use_to_json: true)
|
|
23
13
|
end
|
|
24
14
|
|
|
25
15
|
it "generates json" do
|
|
@@ -28,18 +18,14 @@ RSpec.describe SearchFlip::JSON do
|
|
|
28
18
|
end
|
|
29
19
|
|
|
30
20
|
describe ".parse" do
|
|
31
|
-
it "
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "delegates to JSON" do
|
|
36
|
-
allow(JSON).to receive(:parse)
|
|
21
|
+
it "delegates to Oj" do
|
|
22
|
+
allow(Oj).to receive(:load)
|
|
37
23
|
|
|
38
24
|
payload = '{"key":"value"}'
|
|
39
25
|
|
|
40
26
|
described_class.parse(payload)
|
|
41
27
|
|
|
42
|
-
expect(
|
|
28
|
+
expect(Oj).to have_received(:load).with(payload)
|
|
43
29
|
end
|
|
44
30
|
end
|
|
45
31
|
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" }) {}
|
|
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" }) {}
|
|
19
19
|
|
|
20
20
|
expect(subject).to have_received(:finish)
|
|
21
21
|
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
|
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
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:
|
|
4
|
+
version: 4.0.0.beta
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Benjamin Vetter
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-10-29 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
|
|
@@ -178,20 +164,6 @@ dependencies:
|
|
|
178
164
|
- - ">="
|
|
179
165
|
- !ruby/object:Gem::Version
|
|
180
166
|
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'
|
|
195
167
|
- !ruby/object:Gem::Dependency
|
|
196
168
|
name: oj
|
|
197
169
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -278,6 +250,7 @@ files:
|
|
|
278
250
|
- spec/search_flip/null_instrumenter_spec.rb
|
|
279
251
|
- spec/search_flip/response_spec.rb
|
|
280
252
|
- spec/search_flip/result_spec.rb
|
|
253
|
+
- spec/search_flip/to_json_spec.rb
|
|
281
254
|
- spec/spec_helper.rb
|
|
282
255
|
homepage: https://github.com/mrkamel/search_flip
|
|
283
256
|
licenses:
|
|
@@ -297,11 +270,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
297
270
|
version: '0'
|
|
298
271
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
299
272
|
requirements:
|
|
300
|
-
- - "
|
|
273
|
+
- - ">"
|
|
301
274
|
- !ruby/object:Gem::Version
|
|
302
|
-
version:
|
|
275
|
+
version: 1.3.1
|
|
303
276
|
requirements: []
|
|
304
|
-
rubygems_version: 3.
|
|
277
|
+
rubygems_version: 3.0.3
|
|
305
278
|
signing_key:
|
|
306
279
|
specification_version: 4
|
|
307
280
|
summary: Full-Featured Elasticsearch Ruby Client with a Chainable DSL
|
|
@@ -319,4 +292,5 @@ test_files:
|
|
|
319
292
|
- spec/search_flip/null_instrumenter_spec.rb
|
|
320
293
|
- spec/search_flip/response_spec.rb
|
|
321
294
|
- spec/search_flip/result_spec.rb
|
|
295
|
+
- spec/search_flip/to_json_spec.rb
|
|
322
296
|
- spec/spec_helper.rb
|