meilisearch 0.15.3 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e48c328b5e42361ede3a76e638721b8a2117b8fc6d6128c3709083fe9dae9f76
4
- data.tar.gz: 733ed1fbfded2147c3e440978b7f3bad5a212b54928eb0b41694a474ebafefcd
3
+ metadata.gz: c6e2e138205c88de9e5a82459a9a9648c1170380eb977ca839b8ed25aa238a8f
4
+ data.tar.gz: 642d60688d0000f6ae2d6f2ea64de47236eff9af6b34d71ec5bde38a2e76cc83
5
5
  SHA512:
6
- metadata.gz: efd4e7d40c5c1d676b2f4b2bd8ef4f80b980453d92ab5d894baf92142ba4a62153ad5654bdef24f45cf5877421be558299fcc7013b5127e173ff81b4b598d6ab
7
- data.tar.gz: 6360ee85bd1a6163ddc7baad68f185dbc82ad340ff1b4c6b5cf7f7767a4ec162cab4bb37f5c0742e4285115460526909681b27475bf94c40939198ef6c723ecc
6
+ metadata.gz: 76f796decd057670599f0cbd8e3c921f73457717b0e49e4a58ee9a7faabbefeae5819ba701c59aff27b4bd22fe449d60623b68e7be47e99d70379caa140097c1
7
+ data.tar.gz: 43734101c23e8fd5ab2fcf1b8044b5005b4e809f9947e16519e0f099e82491e75cce33499329faffb73fec91a589bf5de776566498b2c53054c793cbee7f0891
@@ -6,8 +6,14 @@ module MeiliSearch
6
6
  class Client < HTTPRequest
7
7
  ### INDEXES
8
8
 
9
+ def raw_indexes
10
+ http_get('/indexes')
11
+ end
12
+
9
13
  def indexes
10
- http_get '/indexes'
14
+ raw_indexes.map do |index_hash|
15
+ index_object(index_hash['uid'], index_hash['primaryKey'])
16
+ end
11
17
  end
12
18
 
13
19
  # Usage:
@@ -34,6 +40,17 @@ module MeiliSearch
34
40
  index_object(index_uid).delete
35
41
  end
36
42
 
43
+ # Usage:
44
+ # client.delete_index_if_exists('indexUID')
45
+ def delete_index_if_exists(index_uid)
46
+ index_object(index_uid).delete
47
+ true
48
+ rescue ApiError => e
49
+ raise e if e.code != 'index_not_found'
50
+
51
+ false
52
+ end
53
+
37
54
  # Usage:
38
55
  # client.index('indexUID')
39
56
  def index(index_uid)
@@ -44,6 +61,10 @@ module MeiliSearch
44
61
  index_object(index_uid).fetch_info
45
62
  end
46
63
 
64
+ def fetch_raw_index(index_uid)
65
+ index_object(index_uid).fetch_raw_info
66
+ end
67
+
47
68
  ### KEYS
48
69
 
49
70
  def keys
@@ -88,7 +109,7 @@ module MeiliSearch
88
109
  private
89
110
 
90
111
  def index_object(uid, primary_key = nil)
91
- Index.new(uid, @base_url, @api_key, primary_key)
112
+ Index.new(uid, @base_url, @api_key, primary_key, @options)
92
113
  end
93
114
  end
94
115
  end
@@ -29,10 +29,15 @@ module MeiliSearch
29
29
 
30
30
  def get_meilisearch_error_info(http_body)
31
31
  @http_body = JSON.parse(http_body)
32
- @ms_code = @http_body['errorCode']
32
+ @ms_code = @http_body['code']
33
33
  @ms_message = @http_body['message']
34
- @ms_type = @http_body['errorType']
35
- @ms_link = @http_body['errorLink']
34
+ @ms_type = @http_body['type']
35
+ @ms_link = @http_body['link']
36
+ rescue JSON::ParserError
37
+ # We might receive a JSON::ParserError when, for example, MeiliSearch is running behind
38
+ # some proxy (ELB or Nginx, for example), and the request timeouts, returning us
39
+ # a raw HTML body instead of a JSON as we were expecting
40
+ @ms_message = "The server has not returned a valid JSON HTTP body: #{http_body}"
36
41
  end
37
42
 
38
43
  def details
@@ -7,6 +7,8 @@ module MeiliSearch
7
7
  class HTTPRequest
8
8
  include HTTParty
9
9
 
10
+ attr_reader :options
11
+
10
12
  def initialize(url, api_key = nil, options = {})
11
13
  @base_url = url
12
14
  @api_key = api_key
@@ -15,13 +17,17 @@ module MeiliSearch
15
17
  'Content-Type' => 'application/json',
16
18
  'X-Meili-API-Key' => api_key
17
19
  }.compact
20
+ @headers_no_body = {
21
+ 'X-Meili-API-Key' => api_key
22
+ }.compact
18
23
  end
19
24
 
20
25
  def http_get(relative_path = '', query_params = {})
21
26
  send_request(
22
27
  proc { |path, config| self.class.get(path, config) },
23
28
  relative_path,
24
- query_params
29
+ query_params: query_params,
30
+ headers: @headers_no_body
25
31
  )
26
32
  end
27
33
 
@@ -29,8 +35,9 @@ module MeiliSearch
29
35
  send_request(
30
36
  proc { |path, config| self.class.post(path, config) },
31
37
  relative_path,
32
- query_params,
33
- body
38
+ query_params: query_params,
39
+ body: body,
40
+ headers: @headers
34
41
  )
35
42
  end
36
43
 
@@ -38,22 +45,26 @@ module MeiliSearch
38
45
  send_request(
39
46
  proc { |path, config| self.class.put(path, config) },
40
47
  relative_path,
41
- query_params,
42
- body
48
+ query_params: query_params,
49
+ body: body,
50
+ headers: @headers
43
51
  )
44
52
  end
45
53
 
46
54
  def http_delete(relative_path = '')
47
55
  send_request(
48
56
  proc { |path, config| self.class.delete(path, config) },
49
- relative_path
57
+ relative_path,
58
+ headers: @headers_no_body
50
59
  )
51
60
  end
52
61
 
53
62
  private
54
63
 
55
- def send_request(http_method, relative_path, query_params = nil, body = nil)
56
- config = http_config(query_params, body)
64
+ SNAKE_CASE = /[^a-zA-Z0-9]+(.)/.freeze
65
+
66
+ def send_request(http_method, relative_path, query_params: nil, body: nil, headers: nil)
67
+ config = http_config(query_params, body, headers)
57
68
  begin
58
69
  response = http_method.call(@base_url + relative_path, config)
59
70
  rescue Errno::ECONNREFUSED => e
@@ -62,10 +73,10 @@ module MeiliSearch
62
73
  validate(response)
63
74
  end
64
75
 
65
- def http_config(query_params, body)
66
- body = body.to_json
76
+ def http_config(query_params, body, headers)
77
+ body = transform_attributes(body).to_json
67
78
  {
68
- headers: @headers,
79
+ headers: headers,
69
80
  query: query_params,
70
81
  body: body,
71
82
  timeout: @options[:timeout] || 1,
@@ -73,6 +84,25 @@ module MeiliSearch
73
84
  }.compact
74
85
  end
75
86
 
87
+ def transform_attributes(body)
88
+ case body
89
+ when Array
90
+ body.map { |item| transform_attributes(item) }
91
+ when Hash
92
+ parse(body)
93
+ else
94
+ body
95
+ end
96
+ end
97
+
98
+ def parse(body)
99
+ body
100
+ .transform_keys(&:to_s)
101
+ .transform_keys do |key|
102
+ key.include?('_') ? key.downcase.gsub(SNAKE_CASE, &:upcase).gsub('_', '') : key
103
+ end
104
+ end
105
+
76
106
  def validate(response)
77
107
  raise ApiError.new(response.code, response.message, response.body) unless response.success?
78
108
 
@@ -5,36 +5,55 @@ require 'timeout'
5
5
 
6
6
  module MeiliSearch
7
7
  class Index < HTTPRequest
8
- attr_reader :uid, :primary_key
8
+ attr_reader :uid, :primary_key, :created_at, :updated_at
9
9
 
10
- def initialize(index_uid, url, api_key = nil, primary_key = nil)
10
+ def initialize(index_uid, url, api_key = nil, primary_key = nil, options = {})
11
11
  @uid = index_uid
12
12
  @primary_key = primary_key
13
- super(url, api_key)
13
+ super(url, api_key, options)
14
14
  end
15
15
 
16
16
  def fetch_info
17
- index_hash = http_get "/indexes/#{@uid}"
18
- @primary_key = index_hash['primaryKey']
17
+ index_hash = http_get indexes_path(id: @uid)
18
+ set_base_properties index_hash
19
19
  self
20
20
  end
21
21
 
22
+ def fetch_primary_key
23
+ fetch_info.primary_key
24
+ end
25
+ alias get_primary_key fetch_primary_key
26
+
27
+ def fetch_raw_info
28
+ index_hash = http_get indexes_path(id: @uid)
29
+ set_base_properties index_hash
30
+ index_hash
31
+ end
32
+
22
33
  def update(body)
23
- index_hash = http_put "/indexes/#{@uid}", body
24
- @primary_key = index_hash['primaryKey']
34
+ index_hash = http_put indexes_path(id: @uid), body
35
+ set_base_properties index_hash
25
36
  self
26
37
  end
38
+
27
39
  alias update_index update
28
40
 
29
41
  def delete
30
- http_delete "/indexes/#{@uid}"
42
+ http_delete indexes_path(id: @uid)
31
43
  end
32
44
  alias delete_index delete
33
45
 
34
- def fetch_primary_key
35
- fetch_info.primary_key
46
+ def indexes_path(id: nil)
47
+ "/indexes/#{id}"
36
48
  end
37
- alias get_primary_key fetch_primary_key
49
+ private :indexes_path
50
+
51
+ def set_base_properties(index_hash)
52
+ @primary_key = index_hash['primaryKey']
53
+ @created_at = Time.parse(index_hash['createdAt'])
54
+ @updated_at = Time.parse(index_hash['updatedAt'])
55
+ end
56
+ private :set_base_properties
38
57
 
39
58
  ### DOCUMENTS
40
59
 
@@ -76,6 +95,40 @@ module MeiliSearch
76
95
  end
77
96
  alias add_or_update_documents! update_documents!
78
97
 
98
+ def add_documents_in_batches(documents, batch_size = 1000, primary_key = nil)
99
+ update_ids = []
100
+ documents.each_slice(batch_size) do |batch|
101
+ update_ids.append(add_documents(batch, primary_key))
102
+ end
103
+ update_ids
104
+ end
105
+
106
+ def add_documents_in_batches!(documents, batch_size = 1000, primary_key = nil)
107
+ update_ids = add_documents_in_batches(documents, batch_size, primary_key)
108
+ responses = []
109
+ update_ids.each do |update_object|
110
+ responses.append(wait_for_pending_update(update_object['updateId']))
111
+ end
112
+ responses
113
+ end
114
+
115
+ def update_documents_in_batches(documents, batch_size = 1000, primary_key = nil)
116
+ update_ids = []
117
+ documents.each_slice(batch_size) do |batch|
118
+ update_ids.append(update_documents(batch, primary_key))
119
+ end
120
+ update_ids
121
+ end
122
+
123
+ def update_documents_in_batches!(documents, batch_size = 1000, primary_key = nil)
124
+ update_ids = update_documents_in_batches(documents, batch_size, primary_key)
125
+ responses = []
126
+ update_ids.each do |update_object|
127
+ responses.append(wait_for_pending_update(update_object['updateId']))
128
+ end
129
+ responses
130
+ end
131
+
79
132
  def delete_documents(documents_ids)
80
133
  if documents_ids.is_a?(Array)
81
134
  http_post "/indexes/#{@uid}/documents/delete-batch", documents_ids
@@ -129,11 +182,15 @@ module MeiliSearch
129
182
  http_get "/indexes/#{@uid}/updates"
130
183
  end
131
184
 
185
+ def achieved_upate?(update)
186
+ update['status'] != 'enqueued' && update['status'] != 'processing'
187
+ end
188
+
132
189
  def wait_for_pending_update(update_id, timeout_in_ms = 5000, interval_in_ms = 50)
133
190
  Timeout.timeout(timeout_in_ms.to_f / 1000) do
134
191
  loop do
135
192
  get_update = get_update_status(update_id)
136
- return get_update if get_update['status'] != 'enqueued'
193
+ return get_update if achieved_upate?(get_update)
137
194
 
138
195
  sleep interval_in_ms.to_f / 1000
139
196
  end
@@ -160,8 +217,8 @@ module MeiliSearch
160
217
  stats['lastUpdate']
161
218
  end
162
219
 
163
- def fields_distribution
164
- stats['fieldsDistribution']
220
+ def field_distribution
221
+ stats['fieldDistribution']
165
222
  end
166
223
 
167
224
  ### SETTINGS - GENERAL
@@ -220,7 +277,7 @@ module MeiliSearch
220
277
  alias get_stop_words stop_words
221
278
 
222
279
  def update_stop_words(stop_words)
223
- body = stop_words.is_a?(Array) ? stop_words : [stop_words]
280
+ body = stop_words.nil? || stop_words.is_a?(Array) ? stop_words : [stop_words]
224
281
  http_post "/indexes/#{@uid}/settings/stop-words", body
225
282
  end
226
283
  alias stop_words= update_stop_words
@@ -277,20 +334,36 @@ module MeiliSearch
277
334
  http_delete "/indexes/#{@uid}/settings/displayed-attributes"
278
335
  end
279
336
 
280
- ### SETTINGS - ATTRIBUTES FOR FACETING
337
+ ### SETTINGS - FILTERABLE ATTRIBUTES
338
+
339
+ def filterable_attributes
340
+ http_get "/indexes/#{@uid}/settings/filterable-attributes"
341
+ end
342
+ alias get_filterable_attributes filterable_attributes
343
+
344
+ def update_filterable_attributes(filterable_attributes)
345
+ http_post "/indexes/#{@uid}/settings/filterable-attributes", filterable_attributes
346
+ end
347
+ alias filterable_attributes= update_filterable_attributes
348
+
349
+ def reset_filterable_attributes
350
+ http_delete "/indexes/#{@uid}/settings/filterable-attributes"
351
+ end
352
+
353
+ ### SETTINGS - SORTABLE ATTRIBUTES
281
354
 
282
- def attributes_for_faceting
283
- http_get "/indexes/#{@uid}/settings/attributes-for-faceting"
355
+ def sortable_attributes
356
+ http_get "/indexes/#{@uid}/settings/sortable-attributes"
284
357
  end
285
- alias get_attributes_for_faceting attributes_for_faceting
358
+ alias get_sortable_attributes sortable_attributes
286
359
 
287
- def update_attributes_for_faceting(attributes_for_faceting)
288
- http_post "/indexes/#{@uid}/settings/attributes-for-faceting", attributes_for_faceting
360
+ def update_sortable_attributes(sortable_attributes)
361
+ http_post "/indexes/#{@uid}/settings/sortable-attributes", sortable_attributes
289
362
  end
290
- alias attributes_for_faceting= update_attributes_for_faceting
363
+ alias sortable_attributes= update_sortable_attributes
291
364
 
292
- def reset_attributes_for_faceting
293
- http_delete "/indexes/#{@uid}/settings/attributes-for-faceting"
365
+ def reset_sortable_attributes
366
+ http_delete "/indexes/#{@uid}/settings/sortable-attributes"
294
367
  end
295
368
  end
296
369
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MeiliSearch
4
- VERSION = '0.15.3'
4
+ VERSION = '0.17.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meilisearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.3
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Meili
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-03 00:00:00.000000000 Z
11
+ date: 2021-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 0.17.1
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 0.19.0
22
+ version: 0.21.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 0.17.1
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.19.0
32
+ version: 0.21.0
33
33
  description: An easy-to-use ruby client for Meilisearch API. See https://github.com/meilisearch/MeiliSearch
34
34
  email: bonjour@meilisearch.com
35
35
  executables: []