search_flip 2.3.2 → 3.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-