meilisearch 0.17.0 → 0.18.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: 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: