meilisearch 0.17.0 → 0.18.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: c6e2e138205c88de9e5a82459a9a9648c1170380eb977ca839b8ed25aa238a8f
4
- data.tar.gz: 642d60688d0000f6ae2d6f2ea64de47236eff9af6b34d71ec5bde38a2e76cc83
3
+ metadata.gz: df541b398ac4b031ec615d5e20c6a19cd7c370cdeaf83dcf63d789374827f012
4
+ data.tar.gz: 9277554fe2d6f388491d506cbfda456e71a1cc90b1945d933cfa72a2147ee52c
5
5
  SHA512:
6
- metadata.gz: 76f796decd057670599f0cbd8e3c921f73457717b0e49e4a58ee9a7faabbefeae5819ba701c59aff27b4bd22fe449d60623b68e7be47e99d70379caa140097c1
7
- data.tar.gz: 43734101c23e8fd5ab2fcf1b8044b5005b4e809f9947e16519e0f099e82491e75cce33499329faffb73fec91a589bf5de776566498b2c53054c793cbee7f0891
6
+ metadata.gz: bc0446c08444d6bf48d796c62c9b92e5f008835463b08c916e52f0ac9ce81156bcb835cd645fd78c31b98907a06230cf60fe2dbcfdcf0102b9c3fe8f9dfc2732
7
+ data.tar.gz: ac4f2136062699556c365e296db97fe54c6085a228c2b6831b963a72557918c100b83f7e5241183b5d9d173927e127b1e2ad402045dc64ec71056c7b57663fb2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019-2021 Meili
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,222 @@
1
+ <p align="center">
2
+ <img src="https://res.cloudinary.com/meilisearch/image/upload/v1587402338/SDKs/meilisearch_ruby.svg" alt="MeiliSearch-Ruby" width="200" height="200" />
3
+ </p>
4
+
5
+ <h1 align="center">MeiliSearch Ruby</h1>
6
+
7
+ <h4 align="center">
8
+ <a href="https://github.com/meilisearch/MeiliSearch">MeiliSearch</a> |
9
+ <a href="https://docs.meilisearch.com">Documentation</a> |
10
+ <a href="https://slack.meilisearch.com">Slack</a> |
11
+ <a href="https://roadmap.meilisearch.com/tabs/1-under-consideration">Roadmap</a> |
12
+ <a href="https://www.meilisearch.com">Website</a> |
13
+ <a href="https://docs.meilisearch.com/faq">FAQ</a>
14
+ </h4>
15
+
16
+ <p align="center">
17
+ <a href="https://badge.fury.io/rb/meilisearch"><img src="https://badge.fury.io/rb/meilisearch.svg" alt="Latest Stable Version"></a>
18
+ <a href="https://github.com/meilisearch/meilisearch-ruby/actions"><img src="https://github.com/meilisearch/meilisearch-ruby/workflows/Tests/badge.svg" alt="Test"></a>
19
+ <a href="https://github.com/meilisearch/meilisearch-ruby/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-informational" alt="License"></a>
20
+ <a href="https://ms-bors.herokuapp.com/repositories/6"><img src="https://bors.tech/images/badge_small.svg" alt="Bors enabled"></a>
21
+ </p>
22
+
23
+ <p align="center">⚡ The MeiliSearch API client written for Ruby 💎</p>
24
+
25
+ **MeiliSearch Ruby** is the MeiliSearch API client for Ruby developers.
26
+
27
+ **MeiliSearch** is an open-source search engine. [Discover what MeiliSearch is!](https://github.com/meilisearch/MeiliSearch)
28
+
29
+ ## Table of Contents <!-- omit in toc -->
30
+
31
+ - [📖 Documentation](#-documentation)
32
+ - [🔧 Installation](#-installation)
33
+ - [🚀 Getting Started](#-getting-started)
34
+ - [🤖 Compatibility with MeiliSearch](#-compatibility-with-meilisearch)
35
+ - [💡 Learn More](#-learn-more)
36
+ - [⚙️ Development Workflow and Contributing](#️-development-workflow-and-contributing)
37
+
38
+ ## 📖 Documentation
39
+
40
+ See our [Documentation](https://docs.meilisearch.com/learn/tutorials/getting_started.html) or our [API References](https://docs.meilisearch.com/reference/api/).
41
+
42
+ ## 🔧 Installation
43
+
44
+ This package requires Ruby version 2.6.0 or later.
45
+
46
+ With `gem` in command line:
47
+ ```bash
48
+ gem install meilisearch
49
+ ```
50
+
51
+ In your `Gemfile` with [bundler](https://bundler.io/):
52
+ ```ruby
53
+ source 'https://rubygems.org'
54
+
55
+ gem 'meilisearch'
56
+ ```
57
+
58
+ ### Run MeiliSearch <!-- omit in toc -->
59
+
60
+ There are many easy ways to [download and run a MeiliSearch instance](https://docs.meilisearch.com/reference/features/installation.html#download-and-launch).
61
+
62
+ For example, using the `curl` command in your [Terminal](https://itconnect.uw.edu/learn/workshops/online-tutorials/web-publishing/what-is-a-terminal/):
63
+
64
+ ```sh
65
+ #Install MeiliSearch
66
+ curl -L https://install.meilisearch.com | sh
67
+
68
+ # Launch MeiliSearch
69
+ ./meilisearch --master-key=masterKey
70
+ ```
71
+
72
+ NB: you can also download MeiliSearch from **Homebrew** or **APT** or even run it using **Docker**.
73
+
74
+ ## 🚀 Getting Started
75
+
76
+ #### Add documents <!-- omit in toc -->
77
+
78
+ ```ruby
79
+ require 'meilisearch'
80
+
81
+ client = MeiliSearch::Client.new('http://127.0.0.1:7700', 'masterKey')
82
+
83
+ # An index is where the documents are stored.
84
+ index = client.index('movies')
85
+
86
+ documents = [
87
+ { id: 1, title: 'Carol', genres: ['Romance', 'Drama'] },
88
+ { id: 2, title: 'Wonder Woman', genres: ['Action', 'Adventure'] },
89
+ { id: 3, title: 'Life of Pi', genres: ['Adventure', 'Drama'] },
90
+ { id: 4, title: 'Mad Max: Fury Road', genres: ['Adventure', 'Science Fiction'] },
91
+ { id: 5, title: 'Moana', genres: ['Fantasy', 'Action']},
92
+ { id: 6, title: 'Philadelphia', genres: ['Drama'] },
93
+ ]
94
+ # If the index 'movies' does not exist, MeiliSearch creates it when you first add the documents.
95
+ index.add_documents(documents) # => { "uid": 0 }
96
+ ```
97
+
98
+ With the `uid`, you can check the status (`enqueued`, `processing`, `succeeded` or `failed`) of your documents addition using the [task](https://docs.meilisearch.com/reference/api/tasks.html#get-task).
99
+
100
+ 💡 To customize the `Client`, for example, increasing the default timeout, please check out [this section](https://github.com/meilisearch/meilisearch-ruby/wiki/Client-Options) of the Wiki.
101
+
102
+ #### Basic Search <!-- omit in toc -->
103
+
104
+ ``` ruby
105
+ # MeiliSearch is typo-tolerant:
106
+ puts index.search('carlo')
107
+ ```
108
+ Output:
109
+
110
+ ```ruby
111
+ {
112
+ "hits" => [{
113
+ "id" => 1,
114
+ "title" => "Carol"
115
+ }],
116
+ "offset" => 0,
117
+ "limit" => 20,
118
+ "processingTimeMs" => 1,
119
+ "query" => "carlo"
120
+ }
121
+ ```
122
+
123
+ #### Custom search <!-- omit in toc -->
124
+
125
+ All the supported options are described in the [search parameters](https://docs.meilisearch.com/reference/features/search_parameters.html) section of the documentation.
126
+
127
+ ```ruby
128
+ index.search(
129
+ 'wonder',
130
+ attributes_to_highlight: ['*']
131
+ )
132
+ ```
133
+
134
+ JSON output:
135
+
136
+ ```json
137
+ {
138
+ "hits": [
139
+ {
140
+ "id": 2,
141
+ "title": "Wonder Woman",
142
+ "_formatted": {
143
+ "id": 2,
144
+ "title": "<em>Wonder</em> Woman"
145
+ }
146
+ }
147
+ ],
148
+ "offset": 0,
149
+ "limit": 20,
150
+ "processingTimeMs": 0,
151
+ "query": "wonder"
152
+ }
153
+ ```
154
+
155
+ #### Custom Search With Filters <!-- omit in toc -->
156
+
157
+ If you want to enable filtering, you must add your attributes to the `filterableAttributes` index setting.
158
+
159
+ ```ruby
160
+ index.update_filterable_attributes([
161
+ 'id',
162
+ 'genres'
163
+ ])
164
+ ```
165
+
166
+ You only need to perform this operation once.
167
+
168
+ Note that MeiliSearch will rebuild your index whenever you update `filterableAttributes`. Depending on the size of your dataset, this might take time. You can track the process using the [tasks](https://docs.meilisearch.com/reference/api/tasks.html#get-task)).
169
+
170
+ Then, you can perform the search:
171
+
172
+ ```ruby
173
+ index.search('wonder', { filter: ['id > 1 AND genres = Action'] })
174
+ ```
175
+
176
+ JSON output:
177
+
178
+ ```json
179
+ {
180
+ "hits": [
181
+ {
182
+ "id": 2,
183
+ "title": "Wonder Woman",
184
+ "genres": [
185
+ "Action",
186
+ "Adventure"
187
+ ]
188
+ }
189
+ ],
190
+ "nbHits": 1,
191
+ "exhaustiveNbHits": false,
192
+ "query": "wonder",
193
+ "limit": 20,
194
+ "offset": 0,
195
+ "processingTimeMs": 0
196
+ }
197
+ ```
198
+
199
+ ## 🤖 Compatibility with MeiliSearch
200
+
201
+ This package only guarantees the compatibility with the [version v0.25.0 of MeiliSearch](https://github.com/meilisearch/MeiliSearch/releases/tag/v0.25.0).
202
+
203
+ ## 💡 Learn More
204
+
205
+ The following sections may interest you:
206
+
207
+ - **Manipulate documents**: see the [API references](https://docs.meilisearch.com/reference/api/documents.html) or read more about [documents](https://docs.meilisearch.com/learn/core_concepts/documents.html).
208
+ - **Search**: see the [API references](https://docs.meilisearch.com/reference/api/search.html) or follow our guide on [search parameters](https://docs.meilisearch.com/reference/features/search_parameters.html).
209
+ - **Manage the indexes**: see the [API references](https://docs.meilisearch.com/reference/api/indexes.html) or read more about [indexes](https://docs.meilisearch.com/learn/core_concepts/indexes.html).
210
+ - **Configure the index settings**: see the [API references](https://docs.meilisearch.com/reference/api/settings.html) or follow our guide on [settings parameters](https://docs.meilisearch.com/reference/features/settings.html).
211
+
212
+ 📖 Also, check out the [Wiki](https://github.com/meilisearch/meilisearch-ruby/wiki) of this repository to know what this SDK provdes!
213
+
214
+ ## ⚙️ Development Workflow and Contributing
215
+
216
+ Any new contribution is more than welcome in this project!
217
+
218
+ If you want to know more about the development workflow or want to contribute, please visit our [contributing guidelines](/CONTRIBUTING.md) for detailed instructions!
219
+
220
+ <hr>
221
+
222
+ **MeiliSearch** provides and maintains many **SDKs and Integration tools** like this one. We want to provide everyone with an **amazing search experience for any kind of project**. If you want to contribute, make suggestions, or just know what's going on right now, visit us in the [integration-guides](https://github.com/meilisearch/integration-guides) repository.
@@ -20,37 +20,22 @@ module MeiliSearch
20
20
  # client.create_index('indexUID')
21
21
  # client.create_index('indexUID', primaryKey: 'id')
22
22
  def create_index(index_uid, options = {})
23
- body = options.merge(uid: index_uid)
24
- index_hash = http_post '/indexes', body
25
- index_object(index_hash['uid'], index_hash['primaryKey'])
26
- end
23
+ body = Utils.transform_attributes(options.merge(uid: index_uid))
27
24
 
28
- def get_or_create_index(index_uid, options = {})
29
- begin
30
- index_instance = fetch_index(index_uid)
31
- rescue ApiError => e
32
- raise e unless e.code == 'index_not_found'
25
+ http_post '/indexes', body
26
+ end
33
27
 
34
- index_instance = create_index(index_uid, options)
35
- end
36
- index_instance
28
+ # Synchronous version of create_index.
29
+ # Waits for the task to be achieved, be careful when using it.
30
+ def create_index!(index_uid, options = {})
31
+ task = create_index(index_uid, options)
32
+ wait_for_task(task['uid'])
37
33
  end
38
34
 
39
35
  def delete_index(index_uid)
40
36
  index_object(index_uid).delete
41
37
  end
42
38
 
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
-
54
39
  # Usage:
55
40
  # client.index('indexUID')
56
41
  def index(index_uid)
@@ -70,7 +55,26 @@ module MeiliSearch
70
55
  def keys
71
56
  http_get '/keys'
72
57
  end
73
- alias get_keys keys
58
+
59
+ def key(key_uid)
60
+ http_get "/keys/#{key_uid}"
61
+ end
62
+
63
+ def create_key(key_options)
64
+ body = Utils.transform_attributes(key_options)
65
+
66
+ http_post '/keys', body
67
+ end
68
+
69
+ def update_key(key_uid, key_options)
70
+ body = Utils.transform_attributes(key_options)
71
+
72
+ http_patch "/keys/#{key_uid}", body
73
+ end
74
+
75
+ def delete_key(key_uid)
76
+ http_delete "/keys/#{key_uid}"
77
+ end
74
78
 
75
79
  ### HEALTH
76
80
 
@@ -106,10 +110,28 @@ module MeiliSearch
106
110
  end
107
111
  alias get_dump_status dump_status
108
112
 
113
+ ### TASKS
114
+
115
+ def tasks
116
+ task_endpoint.task_list
117
+ end
118
+
119
+ def task(task_uid)
120
+ task_endpoint.task(task_uid)
121
+ end
122
+
123
+ def wait_for_task(task_uid, timeout_in_ms = 5000, interval_in_ms = 50)
124
+ task_endpoint.wait_for_task(task_uid, timeout_in_ms, interval_in_ms)
125
+ end
126
+
109
127
  private
110
128
 
111
129
  def index_object(uid, primary_key = nil)
112
130
  Index.new(uid, @base_url, @api_key, primary_key, @options)
113
131
  end
132
+
133
+ def task_endpoint
134
+ @task_endpoint ||= Task.new(@base_url, @api_key, @options)
135
+ end
114
136
  end
115
137
  end
@@ -12,14 +12,12 @@ module MeiliSearch
12
12
  def initialize(url, api_key = nil, options = {})
13
13
  @base_url = url
14
14
  @api_key = api_key
15
- @options = options
16
- @headers = {
17
- 'Content-Type' => 'application/json',
18
- 'X-Meili-API-Key' => api_key
19
- }.compact
20
- @headers_no_body = {
21
- 'X-Meili-API-Key' => api_key
22
- }.compact
15
+ @options = merge_options({
16
+ timeout: 1,
17
+ max_retries: 0,
18
+ headers: build_default_options_headers(api_key),
19
+ convert_body?: true
20
+ }, options)
23
21
  end
24
22
 
25
23
  def http_get(relative_path = '', query_params = {})
@@ -27,17 +25,17 @@ module MeiliSearch
27
25
  proc { |path, config| self.class.get(path, config) },
28
26
  relative_path,
29
27
  query_params: query_params,
30
- headers: @headers_no_body
28
+ options: remove_options_header(@options, 'Content-Type')
31
29
  )
32
30
  end
33
31
 
34
- def http_post(relative_path = '', body = nil, query_params = nil)
32
+ def http_post(relative_path = '', body = nil, query_params = nil, options = {})
35
33
  send_request(
36
34
  proc { |path, config| self.class.post(path, config) },
37
35
  relative_path,
38
36
  query_params: query_params,
39
37
  body: body,
40
- headers: @headers
38
+ options: merge_options(@options, options)
41
39
  )
42
40
  end
43
41
 
@@ -47,7 +45,17 @@ module MeiliSearch
47
45
  relative_path,
48
46
  query_params: query_params,
49
47
  body: body,
50
- headers: @headers
48
+ options: @options
49
+ )
50
+ end
51
+
52
+ def http_patch(relative_path = '', body = nil, query_params = nil)
53
+ send_request(
54
+ proc { |path, config| self.class.patch(path, config) },
55
+ relative_path,
56
+ query_params: query_params,
57
+ body: body,
58
+ options: @options
51
59
  )
52
60
  end
53
61
 
@@ -55,16 +63,41 @@ module MeiliSearch
55
63
  send_request(
56
64
  proc { |path, config| self.class.delete(path, config) },
57
65
  relative_path,
58
- headers: @headers_no_body
66
+ options: remove_options_header(@options, 'Content-Type')
59
67
  )
60
68
  end
61
69
 
62
70
  private
63
71
 
64
- SNAKE_CASE = /[^a-zA-Z0-9]+(.)/.freeze
72
+ def build_default_options_headers(api_key = nil)
73
+ header = {
74
+ 'Content-Type' => 'application/json'
75
+ }
76
+ header = header.merge('Authorization' => "Bearer #{api_key}") unless api_key.nil?
77
+ header
78
+ end
65
79
 
66
- def send_request(http_method, relative_path, query_params: nil, body: nil, headers: nil)
67
- config = http_config(query_params, body, headers)
80
+ def merge_options(default_options, added_options = {})
81
+ default_cloned_headers = default_options[:headers].clone
82
+ merged_options = default_options.merge(added_options)
83
+ merged_options[:headers] = default_cloned_headers.merge(added_options[:headers]) if added_options.key?(:headers)
84
+ merged_options
85
+ end
86
+
87
+ def remove_options_header(options, key)
88
+ cloned_options = clone_options(options)
89
+ cloned_options[:headers].tap { |headers| headers.delete(key) }
90
+ cloned_options
91
+ end
92
+
93
+ def clone_options(options)
94
+ cloned_options = options.clone
95
+ cloned_options[:headers] = options[:headers].clone
96
+ cloned_options
97
+ end
98
+
99
+ def send_request(http_method, relative_path, query_params: nil, body: nil, options: {})
100
+ config = http_config(query_params, body, options)
68
101
  begin
69
102
  response = http_method.call(@base_url + relative_path, config)
70
103
  rescue Errno::ECONNREFUSED => e
@@ -73,36 +106,17 @@ module MeiliSearch
73
106
  validate(response)
74
107
  end
75
108
 
76
- def http_config(query_params, body, headers)
77
- body = transform_attributes(body).to_json
109
+ def http_config(query_params, body, options)
110
+ body = body.to_json if options[:convert_body?] == true
78
111
  {
79
- headers: headers,
112
+ headers: options[:headers],
80
113
  query: query_params,
81
114
  body: body,
82
- timeout: @options[:timeout] || 1,
83
- max_retries: @options[:max_retries] || 0
115
+ timeout: options[:timeout],
116
+ max_retries: options[:max_retries]
84
117
  }.compact
85
118
  end
86
119
 
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
-
106
120
  def validate(response)
107
121
  raise ApiError.new(response.code, response.message, response.body) unless response.success?
108
122
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'meilisearch/http_request'
4
- require 'timeout'
5
4
 
6
5
  module MeiliSearch
7
6
  class Index < HTTPRequest
@@ -31,9 +30,7 @@ module MeiliSearch
31
30
  end
32
31
 
33
32
  def update(body)
34
- index_hash = http_put indexes_path(id: @uid), body
35
- set_base_properties index_hash
36
- self
33
+ http_put indexes_path(id: @uid), Utils.transform_attributes(body)
37
34
  end
38
35
 
39
36
  alias update_index update
@@ -65,7 +62,7 @@ module MeiliSearch
65
62
  alias get_one_document document
66
63
 
67
64
  def documents(options = {})
68
- http_get "/indexes/#{@uid}/documents", options
65
+ http_get "/indexes/#{@uid}/documents", Utils.transform_attributes(options)
69
66
  end
70
67
  alias get_documents documents
71
68
 
@@ -77,12 +74,33 @@ module MeiliSearch
77
74
  alias add_or_replace_documents add_documents
78
75
 
79
76
  def add_documents!(documents, primary_key = nil)
80
- update = add_documents(documents, primary_key)
81
- wait_for_pending_update(update['updateId'])
77
+ task = add_documents(documents, primary_key)
78
+ wait_for_task(task['uid'])
82
79
  end
83
80
  alias replace_documents! add_documents!
84
81
  alias add_or_replace_documents! add_documents!
85
82
 
83
+ def add_documents_json(documents, primary_key = nil)
84
+ options = { headers: { 'Content-Type' => 'application/json' }, convert_body?: false }
85
+ http_post "/indexes/#{@uid}/documents", documents, { primaryKey: primary_key }.compact, options
86
+ end
87
+ alias replace_documents_json add_documents_json
88
+ alias add_or_replace_documents_json add_documents_json
89
+
90
+ def add_documents_ndjson(documents, primary_key = nil)
91
+ options = { headers: { 'Content-Type' => 'application/x-ndjson' }, convert_body?: false }
92
+ http_post "/indexes/#{@uid}/documents", documents, { primaryKey: primary_key }.compact, options
93
+ end
94
+ alias replace_documents_ndjson add_documents_ndjson
95
+ alias add_or_replace_documents_ndjson add_documents_ndjson
96
+
97
+ def add_documents_csv(documents, primary_key = nil)
98
+ options = { headers: { 'Content-Type' => 'text/csv' }, convert_body?: false }
99
+ http_post "/indexes/#{@uid}/documents", documents, { primaryKey: primary_key }.compact, options
100
+ end
101
+ alias replace_documents_csv add_documents_csv
102
+ alias add_or_replace_documents_csv add_documents_csv
103
+
86
104
  def update_documents(documents, primary_key = nil)
87
105
  documents = [documents] if documents.is_a?(Hash)
88
106
  http_put "/indexes/#{@uid}/documents", documents, { primaryKey: primary_key }.compact
@@ -90,41 +108,41 @@ module MeiliSearch
90
108
  alias add_or_update_documents update_documents
91
109
 
92
110
  def update_documents!(documents, primary_key = nil)
93
- update = update_documents(documents, primary_key)
94
- wait_for_pending_update(update['updateId'])
111
+ task = update_documents(documents, primary_key)
112
+ wait_for_task(task['uid'])
95
113
  end
96
114
  alias add_or_update_documents! update_documents!
97
115
 
98
116
  def add_documents_in_batches(documents, batch_size = 1000, primary_key = nil)
99
- update_ids = []
117
+ tasks = []
100
118
  documents.each_slice(batch_size) do |batch|
101
- update_ids.append(add_documents(batch, primary_key))
119
+ tasks.append(add_documents(batch, primary_key))
102
120
  end
103
- update_ids
121
+ tasks
104
122
  end
105
123
 
106
124
  def add_documents_in_batches!(documents, batch_size = 1000, primary_key = nil)
107
- update_ids = add_documents_in_batches(documents, batch_size, primary_key)
125
+ tasks = add_documents_in_batches(documents, batch_size, primary_key)
108
126
  responses = []
109
- update_ids.each do |update_object|
110
- responses.append(wait_for_pending_update(update_object['updateId']))
127
+ tasks.each do |task_obj|
128
+ responses.append(wait_for_task(task_obj['uid']))
111
129
  end
112
130
  responses
113
131
  end
114
132
 
115
133
  def update_documents_in_batches(documents, batch_size = 1000, primary_key = nil)
116
- update_ids = []
134
+ tasks = []
117
135
  documents.each_slice(batch_size) do |batch|
118
- update_ids.append(update_documents(batch, primary_key))
136
+ tasks.append(update_documents(batch, primary_key))
119
137
  end
120
- update_ids
138
+ tasks
121
139
  end
122
140
 
123
141
  def update_documents_in_batches!(documents, batch_size = 1000, primary_key = nil)
124
- update_ids = update_documents_in_batches(documents, batch_size, primary_key)
142
+ tasks = update_documents_in_batches(documents, batch_size, primary_key)
125
143
  responses = []
126
- update_ids.each do |update_object|
127
- responses.append(wait_for_pending_update(update_object['updateId']))
144
+ tasks.each do |task_obj|
145
+ responses.append(wait_for_task(task_obj['uid']))
128
146
  end
129
147
  responses
130
148
  end
@@ -139,8 +157,8 @@ module MeiliSearch
139
157
  alias delete_multiple_documents delete_documents
140
158
 
141
159
  def delete_documents!(documents_ids)
142
- update = delete_documents(documents_ids)
143
- wait_for_pending_update(update['updateId'])
160
+ task = delete_documents(documents_ids)
161
+ wait_for_task(task['uid'])
144
162
  end
145
163
  alias delete_multiple_documents! delete_documents!
146
164
 
@@ -151,8 +169,8 @@ module MeiliSearch
151
169
  alias delete_one_document delete_document
152
170
 
153
171
  def delete_document!(document_id)
154
- update = delete_document(document_id)
155
- wait_for_pending_update(update['updateId'])
172
+ task = delete_document(document_id)
173
+ wait_for_task(task['uid'])
156
174
  end
157
175
  alias delete_one_document! delete_document!
158
176
 
@@ -161,42 +179,35 @@ module MeiliSearch
161
179
  end
162
180
 
163
181
  def delete_all_documents!
164
- update = delete_all_documents
165
- wait_for_pending_update(update['updateId'])
182
+ task = delete_all_documents
183
+ wait_for_task(task['uid'])
166
184
  end
167
185
 
168
186
  ### SEARCH
169
187
 
170
188
  def search(query, options = {})
171
- parsed_options = options.compact
172
- http_post "/indexes/#{@uid}/search", { q: query.to_s }.merge(parsed_options)
189
+ parsed_options = Utils.transform_attributes({ q: query.to_s }.merge(options.compact))
190
+
191
+ http_post "/indexes/#{@uid}/search", parsed_options
173
192
  end
174
193
 
175
- ### UPDATES
194
+ ### TASKS
176
195
 
177
- def get_update_status(update_id)
178
- http_get "/indexes/#{@uid}/updates/#{update_id}"
196
+ def task_endpoint
197
+ @task_endpoint ||= Task.new(@base_url, @api_key, @options)
179
198
  end
199
+ private :task_endpoint
180
200
 
181
- def get_all_update_status
182
- http_get "/indexes/#{@uid}/updates"
201
+ def task(task_uid)
202
+ task_endpoint.index_task(@uid, task_uid)
183
203
  end
184
204
 
185
- def achieved_upate?(update)
186
- update['status'] != 'enqueued' && update['status'] != 'processing'
205
+ def tasks
206
+ task_endpoint.index_tasks(@uid)
187
207
  end
188
208
 
189
- def wait_for_pending_update(update_id, timeout_in_ms = 5000, interval_in_ms = 50)
190
- Timeout.timeout(timeout_in_ms.to_f / 1000) do
191
- loop do
192
- get_update = get_update_status(update_id)
193
- return get_update if achieved_upate?(get_update)
194
-
195
- sleep interval_in_ms.to_f / 1000
196
- end
197
- end
198
- rescue Timeout::Error
199
- raise MeiliSearch::TimeoutError
209
+ def wait_for_task(task_uid, timeout_in_ms = 5000, interval_in_ms = 50)
210
+ task_endpoint.wait_for_task(task_uid, timeout_in_ms, interval_in_ms)
200
211
  end
201
212
 
202
213
  ### STATS
@@ -213,10 +224,6 @@ module MeiliSearch
213
224
  stats['isIndexing']
214
225
  end
215
226
 
216
- def last_update
217
- stats['lastUpdate']
218
- end
219
-
220
227
  def field_distribution
221
228
  stats['fieldDistribution']
222
229
  end
@@ -229,7 +236,7 @@ module MeiliSearch
229
236
  alias get_settings settings
230
237
 
231
238
  def update_settings(settings)
232
- http_post "/indexes/#{@uid}/settings", settings
239
+ http_post "/indexes/#{@uid}/settings", Utils.transform_attributes(settings)
233
240
  end
234
241
  alias settings= update_settings
235
242
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'meilisearch/http_request'
4
+ require 'timeout'
5
+
6
+ module MeiliSearch
7
+ class Task < HTTPRequest
8
+ def task_list
9
+ http_get '/tasks/'
10
+ end
11
+
12
+ def task(task_uid)
13
+ http_get "/tasks/#{task_uid}"
14
+ end
15
+
16
+ def index_tasks(index_uid)
17
+ http_get "/indexes/#{index_uid}/tasks"
18
+ end
19
+
20
+ def index_task(index_uid, task_uid)
21
+ http_get "/indexes/#{index_uid}/tasks/#{task_uid}"
22
+ end
23
+
24
+ def wait_for_task(task_uid, timeout_in_ms = 5000, interval_in_ms = 50)
25
+ Timeout.timeout(timeout_in_ms.to_f / 1000) do
26
+ loop do
27
+ task = task(task_uid)
28
+ return task if achieved_task?(task)
29
+
30
+ sleep interval_in_ms.to_f / 1000
31
+ end
32
+ end
33
+ rescue Timeout::Error
34
+ raise MeiliSearch::TimeoutError
35
+ end
36
+
37
+ private
38
+
39
+ def achieved_task?(task)
40
+ task['status'] != 'enqueued' && task['status'] != 'processing'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MeiliSearch
4
+ module Utils
5
+ SNAKE_CASE = /[^a-zA-Z0-9]+(.)/.freeze
6
+
7
+ def self.transform_attributes(body)
8
+ case body
9
+ when Array
10
+ body.map { |item| transform_attributes(item) }
11
+ when Hash
12
+ parse(body)
13
+ else
14
+ body
15
+ end
16
+ end
17
+
18
+ def self.parse(body)
19
+ body
20
+ .transform_keys(&:to_s)
21
+ .transform_keys do |key|
22
+ key.include?('_') ? key.downcase.gsub(SNAKE_CASE, &:upcase).gsub('_', '') : key
23
+ end
24
+ end
25
+
26
+ private_class_method :parse
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MeiliSearch
4
- VERSION = '0.17.0'
4
+ VERSION = '0.18.0'
5
5
  end
data/lib/meilisearch.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'meilisearch/version'
4
+ require 'meilisearch/utils'
5
+ require 'meilisearch/task'
4
6
  require 'meilisearch/client'
5
7
  require 'meilisearch/index'
6
8
 
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.17.0
4
+ version: 0.18.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-11-17 00:00:00.000000000 Z
11
+ date: 2022-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -36,11 +36,15 @@ executables: []
36
36
  extensions: []
37
37
  extra_rdoc_files: []
38
38
  files:
39
+ - LICENSE
40
+ - README.md
39
41
  - lib/meilisearch.rb
40
42
  - lib/meilisearch/client.rb
41
43
  - lib/meilisearch/error.rb
42
44
  - lib/meilisearch/http_request.rb
43
45
  - lib/meilisearch/index.rb
46
+ - lib/meilisearch/task.rb
47
+ - lib/meilisearch/utils.rb
44
48
  - lib/meilisearch/version.rb
45
49
  homepage: https://github.com/meilisearch/meilisearch-ruby
46
50
  licenses: