meilisearch 0.15.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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: []