search_flip 2.3.2 → 3.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.
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # The SearchFlip::PostFilterable mixin provides chainable methods like
4
3
  # #post_where, #post_exists, #post_range, etc to add and apply search
@@ -23,7 +22,7 @@ module SearchFlip
23
22
  module PostFilterable
24
23
  def self.included(base)
25
24
  base.class_eval do
26
- attr_accessor :post_search_values, :post_must_values, :post_must_not_values, :post_should_values, :post_filter_values
25
+ attr_accessor :post_must_values, :post_must_not_values, :post_filter_values
27
26
  end
28
27
  end
29
28
 
@@ -42,13 +41,9 @@ module SearchFlip
42
41
  # @return [SearchFlip::Criteria] A newly created extended criteria
43
42
 
44
43
  def post_search(q, options = {})
45
- raise(SearchFlip::NotSupportedError) if target.connection.version.to_i < 2
44
+ return self if q.to_s.strip.length.zero?
46
45
 
47
- fresh.tap do |criteria|
48
- if q.to_s.strip.length > 0
49
- criteria.post_search_values = (post_search_values || []) + [query_string: { query: q, default_operator: :AND }.merge(options)]
50
- end
51
- end
46
+ post_must(query_string: { query: q, default_operator: :AND }.merge(options))
52
47
  end
53
48
 
54
49
  # Adds post filters to your criteria for the supplied hash composed of
@@ -76,11 +71,13 @@ module SearchFlip
76
71
  def post_where(hash)
77
72
  hash.inject(fresh) do |memo, (key, value)|
78
73
  if value.is_a?(Array)
79
- memo.post_filter terms: { key => value }
74
+ memo.post_filter(terms: { key => value })
80
75
  elsif value.is_a?(Range)
81
- memo.post_filter range: { key => { gte: value.min, lte: value.max } }
76
+ memo.post_filter(range: { key => { gte: value.min, lte: value.max } })
77
+ elsif value.nil?
78
+ memo.post_must_not(exists: { field: key })
82
79
  else
83
- memo.post_filter term: { key => value }
80
+ memo.post_filter(term: { key => value })
84
81
  end
85
82
  end
86
83
  end
@@ -109,11 +106,13 @@ module SearchFlip
109
106
  def post_where_not(hash)
110
107
  hash.inject(fresh) do |memo, (key, value)|
111
108
  if value.is_a?(Array)
112
- memo.post_must_not terms: { key => value }
109
+ memo.post_must_not(terms: { key => value })
113
110
  elsif value.is_a?(Range)
114
- memo.post_must_not range: { key => { gte: value.min, lte: value.max } }
111
+ memo.post_must_not(range: { key => { gte: value.min, lte: value.max } })
112
+ elsif value.nil?
113
+ memo.post_filter(exists: { field: key })
115
114
  else
116
- memo.post_must_not term: { key => value }
115
+ memo.post_must_not(term: { key => value })
117
116
  end
118
117
  end
119
118
  end
@@ -132,9 +131,9 @@ module SearchFlip
132
131
  #
133
132
  # @return [SearchFlip::Criteria] A newly created extended criteria
134
133
 
135
- def post_filter(*args)
134
+ def post_filter(clause)
136
135
  fresh.tap do |criteria|
137
- criteria.post_filter_values = (post_filter_values || []) + args
136
+ criteria.post_filter_values = (post_filter_values || []) + Helper.wrap_array(clause)
138
137
  end
139
138
  end
140
139
 
@@ -152,9 +151,9 @@ module SearchFlip
152
151
  #
153
152
  # @return [SearchFlip::Criteria] A newly created extended criteria
154
153
 
155
- def post_must(*args)
154
+ def post_must(clause)
156
155
  fresh.tap do |criteria|
157
- criteria.post_must_values = (post_must_values || []) + args
156
+ criteria.post_must_values = (post_must_values || []) + Helper.wrap_array(clause)
158
157
  end
159
158
  end
160
159
 
@@ -172,30 +171,27 @@ module SearchFlip
172
171
  #
173
172
  # @return [SearchFlip::Criteria] A newly created extended criteria
174
173
 
175
- def post_must_not(*args)
174
+ def post_must_not(clause)
176
175
  fresh.tap do |criteria|
177
- criteria.post_must_not_values = (post_must_not_values || []) + args
176
+ criteria.post_must_not_values = (post_must_not_values || []) + Helper.wrap_array(clause)
178
177
  end
179
178
  end
180
179
 
181
- # Adds raw post should queries to the criteria.
180
+ # Adds a raw post should query to the criteria.
182
181
  #
183
182
  # @example Raw post term should query
184
183
  # query = CommentIndex.aggregate("...")
185
- # query = query.post_should(term: { state: "new" })
184
+ # query = query.post_should([
185
+ # { term: { state: "new" } },
186
+ # { term: { state: "approved" } }
187
+ # ])
186
188
  #
187
- # @example Raw post range should query
188
- # query = CommentIndex.aggregate("...")
189
- # query = query.post_should(range: { created_at: { gte: Time.parse("2016-01-01") }})
190
- #
191
- # @param args [Array, Hash] The raw should query arguments
189
+ # @param clauses [Array] The raw should query arguments
192
190
  #
193
191
  # @return [SearchFlip::Criteria] A newly created extended criteria
194
192
 
195
- def post_should(*args)
196
- fresh.tap do |criteria|
197
- criteria.post_should_values = (post_should_values || []) + args
198
- end
193
+ def post_should(clause)
194
+ post_must(bool: { should: clause })
199
195
  end
200
196
 
201
197
  # Adds a post range filter to the criteria without being forced to specify
@@ -217,7 +213,7 @@ module SearchFlip
217
213
  # @return [SearchFlip::Criteria] A newly created extended criteria
218
214
 
219
215
  def post_range(field, options = {})
220
- post_filter range: { field => options }
216
+ post_filter(range: { field => options })
221
217
  end
222
218
 
223
219
  # Adds a post exists filter to the criteria, which selects all documents
@@ -232,10 +228,10 @@ module SearchFlip
232
228
  # @return [SearchFlip::Criteria] A newly created extended criteria
233
229
 
234
230
  def post_exists(field)
235
- post_filter exists: { field: field }
231
+ post_filter(exists: { field: field })
236
232
  end
237
233
 
238
- # Adds a post exists not filter to the criteria, which selects all documents
234
+ # Adds a post exists not query to the criteria, which selects all documents
239
235
  # for which the specified field's value is null.
240
236
  #
241
237
  # @example
@@ -247,8 +243,7 @@ module SearchFlip
247
243
  # @return [SearchFlip::Criteria] A newly created extended criteria
248
244
 
249
245
  def post_exists_not(field)
250
- post_must_not exists: { field: field }
246
+ post_must_not(exists: { field: field })
251
247
  end
252
248
  end
253
249
  end
254
-
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # The SearchFlip::Response class wraps a raw SearchFlip response and
4
3
  # decorates it with methods for aggregations, hits, records, pagination, etc.
@@ -34,16 +33,16 @@ module SearchFlip
34
33
  # Returns the total number of results.
35
34
  #
36
35
  # @example
37
- # CommentIndex.search("hello world").total_entries
36
+ # CommentIndex.search("hello world").total_count
38
37
  # # => 13
39
38
  #
40
39
  # @return [Fixnum] The total number of results
41
40
 
42
- def total_entries
41
+ def total_count
43
42
  hits["total"].is_a?(Hash) ? hits["total"]["value"] : hits["total"]
44
43
  end
45
44
 
46
- alias_method :total_count, :total_entries
45
+ alias_method :total_entries, :total_count
47
46
 
48
47
  # Returns whether or not the current page is the first page.
49
48
  #
@@ -103,7 +102,7 @@ module SearchFlip
103
102
  # @return [Fixnum] The current page number
104
103
 
105
104
  def current_page
106
- 1 + (criteria.offset_value_with_default / criteria.limit_value_with_default.to_f).ceil
105
+ 1 + (criteria.offset_value_with_default / criteria.limit_value_with_default)
107
106
  end
108
107
 
109
108
  # Returns the number of total pages for the current pagination settings, ie
@@ -116,7 +115,7 @@ module SearchFlip
116
115
  # @return [Fixnum] The total number of pages
117
116
 
118
117
  def total_pages
119
- [(total_entries / criteria.limit_value_with_default.to_f).ceil, 1].max
118
+ [(total_count.to_f / criteria.limit_value_with_default).ceil, 1].max
120
119
  end
121
120
 
122
121
  # Returns the previous page number or nil if no previous page exists, ie if
@@ -324,4 +323,3 @@ module SearchFlip
324
323
  end
325
324
  end
326
325
  end
327
-
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # The SearchFlip::Result class basically is a hash wrapper that uses
4
3
  # Hashie::Mash to provide convenient method access to the hash attributes.
@@ -9,4 +8,3 @@ module SearchFlip
9
8
  end
10
9
  end
11
10
  end
12
-
@@ -1,31 +1,29 @@
1
-
2
1
  require "time"
3
2
  require "date"
4
3
  require "json"
5
4
 
6
5
  class Time
7
- def to_json
6
+ def to_json(*args)
8
7
  iso8601(6).to_json
9
8
  end
10
9
  end
11
10
 
12
11
  class Date
13
- def to_json
12
+ def to_json(*args)
14
13
  iso8601.to_json
15
14
  end
16
15
  end
17
16
 
18
17
  class DateTime
19
- def to_json
18
+ def to_json(*args)
20
19
  iso8601(6).to_json
21
20
  end
22
21
  end
23
22
 
24
23
  if defined?(ActiveSupport)
25
24
  class ActiveSupport::TimeWithZone
26
- def to_json
25
+ def to_json(*args)
27
26
  iso8601(6).to_json
28
27
  end
29
28
  end
30
29
  end
31
-
@@ -1,5 +1,3 @@
1
-
2
1
  module SearchFlip
3
- VERSION = "2.3.2"
2
+ VERSION = "3.0.0.beta"
4
3
  end
5
-
data/lib/search_flip.rb CHANGED
@@ -1,5 +1,3 @@
1
-
2
- require "ruby2_keywords"
3
1
  require "forwardable"
4
2
  require "http"
5
3
  require "hashie"
@@ -8,6 +6,7 @@ require "oj"
8
6
  require "set"
9
7
 
10
8
  require "search_flip/version"
9
+ require "search_flip/helper"
11
10
  require "search_flip/exceptions"
12
11
  require "search_flip/json"
13
12
  require "search_flip/http_client"
@@ -41,4 +40,3 @@ module SearchFlip
41
40
  end
42
41
  end
43
42
  end
44
-
data/search_flip.gemspec CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  lib = File.expand_path("lib", __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
 
@@ -9,12 +8,12 @@ Gem::Specification.new do |spec|
9
8
  spec.version = SearchFlip::VERSION
10
9
  spec.authors = ["Benjamin Vetter"]
11
10
  spec.email = ["vetter@flakks.com"]
12
- spec.description = %q{Full-Featured Elasticsearch Ruby Client with a Chainable DSL}
13
- spec.summary = %q{Full-Featured Elasticsearch Ruby Client with a Chainable DSL}
11
+ spec.description = "Full-Featured Elasticsearch Ruby Client with a Chainable DSL"
12
+ spec.summary = "Full-Featured Elasticsearch Ruby Client with a Chainable DSL"
14
13
  spec.homepage = "https://github.com/mrkamel/search_flip"
15
14
  spec.license = "MIT"
16
15
 
17
- spec.files = `git ls-files`.split($/)
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
18
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
19
  spec.require_paths = ["lib"]
@@ -30,11 +29,11 @@ Gem::Specification.new do |spec|
30
29
  spec.add_development_dependency "factory_bot"
31
30
  spec.add_development_dependency "rake"
32
31
  spec.add_development_dependency "rspec"
32
+ spec.add_development_dependency "rubocop"
33
33
  spec.add_development_dependency "sqlite3"
34
34
  spec.add_development_dependency "timecop"
35
35
  spec.add_development_dependency "webmock"
36
36
 
37
- spec.add_dependency "ruby2_keywords"
38
37
  spec.add_dependency "hashie"
39
38
  spec.add_dependency "http"
40
39
  spec.add_dependency "oj"
@@ -1,4 +1,3 @@
1
-
2
1
  RSpec::Matchers.define :delegate do |method|
3
2
  match do |delegator|
4
3
  @method = method
@@ -29,4 +28,3 @@ RSpec::Matchers.define :delegate do |method|
29
28
 
30
29
  chain(:to) { |to| @to = to }
31
30
  end
32
-
@@ -1,4 +1,3 @@
1
-
2
1
  require File.expand_path("../spec_helper", __dir__)
3
2
 
4
3
  RSpec.describe SearchFlip::Aggregation do
@@ -287,8 +286,8 @@ RSpec.describe SearchFlip::Aggregation do
287
286
  unsupported_methods = [
288
287
  :profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :offset_value, :limit_value,
289
288
  :scroll_args, :highlight_values, :suggest_values, :custom_value, :source_value, :sort_values,
290
- :includes_values, :preload_values, :eager_load_values, :post_search_values, :post_must_values,
291
- :post_must_not_values, :post_should_values, :post_filter_values, :preference_value,
289
+ :includes_values, :preload_values, :eager_load_values, :post_must_values,
290
+ :post_must_not_values, :post_filter_values, :preference_value,
292
291
  :search_type_value, :routing_value
293
292
  ]
294
293
 
@@ -309,7 +308,7 @@ RSpec.describe SearchFlip::Aggregation do
309
308
  end
310
309
 
311
310
  describe "array concatenations" do
312
- methods = [:search_values, :must_values, :must_not_values, :should_values, :filter_values]
311
+ methods = [:must_values, :must_not_values, :filter_values]
313
312
 
314
313
  methods.each do |method|
315
314
  it "concatenates the values for #{method}" do
@@ -1,4 +1,3 @@
1
-
2
1
  require File.expand_path("../spec_helper", __dir__)
3
2
 
4
3
  RSpec.describe SearchFlip::Bulk do
@@ -75,34 +74,40 @@ RSpec.describe SearchFlip::Bulk do
75
74
  expect(&block).to raise_error(SearchFlip::ResponseError)
76
75
  end
77
76
 
78
- it "handles overly long payloads" do
77
+ it "transmits up to bulk_max_mb only" do
79
78
  product = create(:product)
80
79
 
81
- allow(product).to receive(:description).and_return("x" * 1024 * 1024 * 10)
80
+ allow(product).to receive(:description).and_return("x" * 1024 * 1024)
82
81
 
83
- block = lambda do
84
- ProductIndex.bulk bulk_max_mb: 1_000 do |bulk|
85
- 20.times do
86
- bulk.index product.id, ProductIndex.serialize(product)
87
- end
82
+ ProductIndex.bulk bulk_max_mb: 10 do |bulk|
83
+ allow(bulk).to receive(:upload).and_call_original
84
+
85
+ 20.times do
86
+ bulk.index product.id, ProductIndex.serialize(product)
88
87
  end
89
- end
90
88
 
91
- if ProductIndex.connection.version.to_i <= 2
92
- expect(&block).to raise_error(SearchFlip::ConnectionError)
93
- else
94
- expect(&block).to raise_error(SearchFlip::ResponseError)
89
+ expect(bulk).to have_received(:upload).exactly(2).times
95
90
  end
91
+ end
96
92
 
97
- block = lambda do
98
- ProductIndex.bulk bulk_max_mb: 100 do |bulk|
99
- 20.times do
100
- bulk.index product.id, ProductIndex.serialize(product)
101
- end
93
+ it "uploads a last time if there is data left within the output buffer" do
94
+ product = create(:product)
95
+
96
+ allow(product).to receive(:description).and_return("x" * 1024 * 1024)
97
+
98
+ bulk_upload = nil
99
+
100
+ ProductIndex.bulk bulk_max_mb: 5.5 do |bulk|
101
+ bulk_upload = bulk
102
+
103
+ allow(bulk).to receive(:upload).and_call_original
104
+
105
+ 6.times do
106
+ bulk.index product.id, ProductIndex.serialize(product)
102
107
  end
103
108
  end
104
109
 
105
- expect(&block).not_to raise_error
110
+ expect(bulk_upload).to have_received(:upload).exactly(2).times
106
111
  end
107
112
  end
108
113
  end
@@ -1,4 +1,3 @@
1
-
2
1
  require File.expand_path("../spec_helper", __dir__)
3
2
 
4
3
  RSpec.describe SearchFlip::Connection do
@@ -96,6 +95,12 @@ RSpec.describe SearchFlip::Connection do
96
95
  expect(connection.get_indices.map { |index| index["index"] }.to_set).to eq(["comments", "products"].to_set)
97
96
  expect(connection.get_indices("com*").map { |index| index["index"] }).to eq(["comments"])
98
97
  end
98
+
99
+ it "accepts additional parameters" do
100
+ connection = SearchFlip::Connection.new
101
+
102
+ expect(connection.get_indices("comments", params: { h: "i" })).to eq([{ "i" => "comments" }])
103
+ end
99
104
  end
100
105
 
101
106
  describe "#create_index" do
@@ -114,13 +119,15 @@ RSpec.describe SearchFlip::Connection do
114
119
  end
115
120
 
116
121
  it "respects a payload" do
117
- connection = SearchFlip::Connection.new
122
+ begin
123
+ connection = SearchFlip::Connection.new
118
124
 
119
- connection.create_index("index_name", settings: { number_of_shards: 3 })
125
+ connection.create_index("index_name", settings: { number_of_shards: 3 })
120
126
 
121
- expect(connection.get_index_settings("index_name")["index_name"]["settings"]["index"]["number_of_shards"]).to eq("3")
122
- ensure
123
- connection.delete_index("index_name") if connection.index_exists?("index_name")
127
+ expect(connection.get_index_settings("index_name")["index_name"]["settings"]["index"]["number_of_shards"]).to eq("3")
128
+ ensure
129
+ connection.delete_index("index_name") if connection.index_exists?("index_name")
130
+ end
124
131
  end
125
132
  end
126
133
 
@@ -159,6 +166,41 @@ RSpec.describe SearchFlip::Connection do
159
166
  end
160
167
  end
161
168
 
169
+ describe "#freeze_index" do
170
+ it "freezes the specified index" do
171
+ connection = SearchFlip::Connection.new
172
+
173
+ if connection.version.to_f >= 6.6
174
+ begin
175
+ connection.create_index("index_name")
176
+ connection.freeze_index("index_name")
177
+
178
+ expect(connection.get_indices("index_name", params: { h: "sth" }).first["sth"]).to eq("true")
179
+ ensure
180
+ connection.delete_index("index_name") if connection.index_exists?("index_name")
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ describe "#unfreeze_index" do
187
+ it "unfreezes the specified index" do
188
+ connection = SearchFlip::Connection.new
189
+
190
+ if connection.version.to_f >= 6.6
191
+ begin
192
+ connection.create_index("index_name")
193
+ connection.freeze_index("index_name")
194
+ connection.unfreeze_index("index_name")
195
+
196
+ expect(connection.get_indices("index_name", params: { h: "sth" }).first["sth"]).to eq("false")
197
+ ensure
198
+ connection.delete_index("index_name") if connection.index_exists?("index_name")
199
+ end
200
+ end
201
+ end
202
+ end
203
+
162
204
  describe ".analyze" do
163
205
  it "analyzes the provided request" do
164
206
  connection = SearchFlip::Connection.new
@@ -282,4 +324,3 @@ RSpec.describe SearchFlip::Connection do
282
324
  end
283
325
  end
284
326
  end
285
-