algoliasearch 1.8.1 → 1.9.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
  SHA1:
3
- metadata.gz: 249bdbdfe9306f6396363418d097060519a2b666
4
- data.tar.gz: 29cd2a19ac2652eb9b19ec9a6e1a4e74aa9d6faf
3
+ metadata.gz: 1035690a7833a5235345419a3376a771932d281e
4
+ data.tar.gz: d7eb3c02b159845109c25b7e49b61191e72a2d2d
5
5
  SHA512:
6
- metadata.gz: c5fdf482ba1c5734c95009719c836132b7e1e18d810a7959d6d48bd767669648e8d75221103f2080359cbe8d611c17575b42da65ddfc60b22ed76a1a0e88e4ce
7
- data.tar.gz: c1ddb83fbc011fb659831ba67545d7ed2b65ed8d2320fc16f76749d46a097a7cc22b92e44908eff216d636ecdf3f0d49b3f43ac9178f72a65dc1d5b0eba03e35
6
+ metadata.gz: 57f1c99c147f759f6c6a70ecae88b30ec607ab886d720fcc426bf47b1a528028494b54d2edbfd40eb2e560074866b6453fd5727e117ecb9e26abe98ec14f91b3
7
+ data.tar.gz: b19a6880521c58d9fd74a4b62244b61aeb348b7f61c06aaf6d90e0565681c4daf2cb5f16d4fa8bcec60a3c2d9762b0d521ac12e742715f7028d56b2589b942f0
@@ -8,6 +8,7 @@ rvm:
8
8
  - 2.0.0
9
9
  - 2.1.5
10
10
  - 2.2.2
11
+ - 2.3.0
11
12
  - jruby
12
13
  - rbx-2
13
14
  install:
data/ChangeLog CHANGED
@@ -1,5 +1,8 @@
1
1
  CHANGELOG
2
2
 
3
+ 2016-06-17 1.9.0
4
+ * New synonyms API
5
+
3
6
  2016-04-14 1.8.1
4
7
  * Ensure we're using an absolute path for the `ca-bundle.crt` file (could fix some `OpenSSL::X509::StoreError: system lib` errors)
5
8
 
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gem 'httpclient', '~> 2.7.1'
4
4
  gem 'json', '>= 1.5.1'
@@ -8,7 +8,6 @@ group :development do
8
8
  gem 'highline', '< 1.7.0'
9
9
  gem 'coveralls'
10
10
  gem 'safe_yaml', '~> 1.0.4'
11
- gem 'rest-client', '1.6.8'
12
11
  gem 'travis'
13
12
  gem 'rake'
14
13
  gem 'rdoc'
@@ -1,15 +1,15 @@
1
1
  GEM
2
- remote: http://rubygems.org/
2
+ remote: https://rubygems.org/
3
3
  specs:
4
4
  addressable (2.3.7)
5
5
  backports (3.6.4)
6
6
  coderay (1.1.0)
7
- coveralls (0.7.11)
8
- multi_json (~> 1.10)
9
- rest-client (>= 1.6.8, < 2)
10
- simplecov (~> 0.9.1)
7
+ coveralls (0.8.13)
8
+ json (~> 1.8)
9
+ simplecov (~> 0.11.0)
11
10
  term-ansicolor (~> 1.3)
12
11
  thor (~> 0.19.1)
12
+ tins (~> 1.6.0)
13
13
  crack (0.4.2)
14
14
  safe_yaml (~> 1.0.0)
15
15
  diff-lcs (1.2.5)
@@ -32,8 +32,8 @@ GEM
32
32
  net-http-pipeline
33
33
  highline (1.6.21)
34
34
  httpclient (2.7.1)
35
- json (1.8.2)
36
- json (1.8.2-java)
35
+ json (1.8.3)
36
+ json (1.8.3-java)
37
37
  launchy (2.4.3)
38
38
  addressable (~> 2.3)
39
39
  launchy (2.4.3-java)
@@ -41,7 +41,7 @@ GEM
41
41
  spoon (~> 0.0.1)
42
42
  method_source (0.8.2)
43
43
  mime-types (1.25.1)
44
- multi_json (1.10.1)
44
+ multi_json (1.12.0)
45
45
  multipart-post (2.0.0)
46
46
  net-http-persistent (2.9.4)
47
47
  net-http-pipeline (1.0.1)
@@ -58,12 +58,9 @@ GEM
58
58
  json
59
59
  websocket (~> 1.0)
60
60
  rake (10.4.2)
61
- rdoc (4.2.0)
61
+ rdoc (4.2.2)
62
62
  json (~> 1.4)
63
63
  redgreen (1.2.2)
64
- rest-client (1.6.8)
65
- mime-types (~> 1.16)
66
- rdoc (>= 2.4.2)
67
64
  rspec (3.2.0)
68
65
  rspec-core (~> 3.2.0)
69
66
  rspec-expectations (~> 3.2.0)
@@ -277,18 +274,18 @@ GEM
277
274
  rubysl-yaml (2.1.0)
278
275
  rubysl-zlib (2.0.1)
279
276
  safe_yaml (1.0.4)
280
- simplecov (0.9.2)
277
+ simplecov (0.11.2)
281
278
  docile (~> 1.1.0)
282
- multi_json (~> 1.0)
283
- simplecov-html (~> 0.9.0)
284
- simplecov-html (0.9.0)
279
+ json (~> 1.8)
280
+ simplecov-html (~> 0.10.0)
281
+ simplecov-html (0.10.0)
285
282
  slop (3.6.0)
286
283
  spoon (0.0.4)
287
284
  ffi
288
- term-ansicolor (1.3.0)
285
+ term-ansicolor (1.3.2)
289
286
  tins (~> 1.0)
290
287
  thor (0.19.1)
291
- tins (1.3.4)
288
+ tins (1.6.0)
292
289
  travis (1.7.5)
293
290
  addressable (~> 2.3)
294
291
  backports
@@ -320,7 +317,6 @@ DEPENDENCIES
320
317
  rake
321
318
  rdoc
322
319
  redgreen
323
- rest-client (= 1.6.8)
324
320
  rspec (>= 2.5.0)
325
321
  rubysl (~> 2.0)
326
322
  safe_yaml (~> 1.0.4)
data/README.md CHANGED
@@ -18,8 +18,8 @@ Our Ruby client lets you easily use the [Algolia Search API](https://www.algolia
18
18
 
19
19
 
20
20
 
21
+ [![Build Status](https://travis-ci.org/algolia/algoliasearch-client-ruby.svg?branch=master)](https://travis-ci.org/algolia/algoliasearch-client-ruby) [![Gem Version](https://badge.fury.io/rb/algoliasearch.svg)](http://badge.fury.io/rb/algoliasearch) [![Code Climate](https://codeclimate.com/github/algolia/algoliasearch-client-ruby.svg)](https://codeclimate.com/github/algolia/algoliasearch-client-ruby) [![Coverage Status](https://coveralls.io/repos/algolia/algoliasearch-client-ruby/badge.svg)](https://coveralls.io/r/algolia/algoliasearch-client-ruby)
21
22
 
22
- [![Build Status](https://travis-ci.org/algolia/algoliasearch-client-ruby.svg?branch=master)](https://travis-ci.org/algolia/algoliasearch-client-ruby) [![Gem Version](https://badge.fury.io/rb/algoliasearch.svg)](http://badge.fury.io/rb/algoliasearch) [![Code Climate](https://codeclimate.com/github/algolia/algoliasearch-client-ruby.svg)](https://codeclimate.com/github/algolia/algoliasearch-client-ruby) [![Coverage Status](https://coveralls.io/repos/algolia/algoliasearch-client-ruby/badge.png)](https://coveralls.io/r/algolia/algoliasearch-client-ruby)
23
23
 
24
24
 
25
25
 
@@ -32,7 +32,6 @@ Table of Contents
32
32
 
33
33
  1. [Setup](#setup)
34
34
  1. [Quick Start](#quick-start)
35
-
36
35
  1. [Guides & Tutorials](#guides-tutorials)
37
36
 
38
37
 
@@ -196,7 +195,7 @@ Check our [online guides](https://www.algolia.com/doc):
196
195
  Add a new object to the Index
197
196
  ==================
198
197
 
199
- Each entry in an index has a unique identifier called `objectID`. There are two ways to add en entry to the index:
198
+ Each entry in an index has a unique identifier called `objectID`. There are two ways to add an entry to the index:
200
199
 
201
200
  1. Using automatic `objectID` assignment. You will be able to access it in the answer.
202
201
  2. Supplying your own `objectID`.
@@ -306,7 +305,7 @@ Search
306
305
 
307
306
  To perform a search, you only need to initialize the index and perform a call to the search function.
308
307
 
309
- The search query allows only to retrieve 1000 hits, if you need to retrieve more than 1000 hits for seo, you can use [Backup / Retrieve all index content](#backup--retrieve-of-all-index-content)
308
+ The search query allows only to retrieve 1000 hits, if you need to retrieve more than 1000 hits for seo, you can use [Backup / Retrieve all index content](#backup--export-an-index)
310
309
 
311
310
  ```ruby
312
311
  index = Algolia::Index.new("contacts")
@@ -801,6 +800,8 @@ You can use the following optional arguments:
801
800
 
802
801
  <p>Attributes are separated with a comma (for example <code>&quot;name,address&quot;</code>). You can also use a string array encoding (for example <code>[&quot;name&quot;,&quot;address&quot;]</code> ). By default, all attributes are retrieved. You can also use <code>*</code> to retrieve all values when an <strong>attributesToRetrieve</strong> setting is specified for your index.</p>
803
802
 
803
+ <p><code>objectID</code> is always retrieved even when not specified.</p>
804
+
804
805
  </td>
805
806
  </tr>
806
807
 
@@ -909,7 +910,7 @@ You can use the following optional arguments:
909
910
  </div>
910
911
  </td>
911
912
  <td class='client-readme-param-content'>
912
- <p>String used as an ellipsis indicator when a snippet is truncated (defaults to empty).</p>
913
+ <p>String used as an ellipsis indicator when a snippet is truncated. Defaults to an empty string for all accounts created before 10/2/2016, and to <code>…</code> (UTF-8 U+2026) for accounts created after that date.</p>
913
914
 
914
915
  </td>
915
916
  </tr>
@@ -963,7 +964,7 @@ You can also use a string array encoding (for example `numericFilters: ["price>1
963
964
  </div>
964
965
  </td>
965
966
  <td class='client-readme-param-content'>
966
- <p>Filter the query by a set of tags. You can AND tags by separating them with commas. To OR tags, you must add parentheses. For example, <code>tags=tag1,(tag2,tag3)</code> means <em>tag1 AND (tag2 OR tag3)</em>. You can also use a string array encoding. For example, <code>tagFilters: [&quot;tag1&quot;,[&quot;tag2&quot;,&quot;tag3&quot;]]</code> means <em>tag1 AND (tag2 OR tag3)</em>.</p>
967
+ <p>Filter the query by a set of tags. You can AND tags by separating them with commas. To OR tags, you must add parentheses. For example, <code>tagFilters=tag1,(tag2,tag3)</code> means <em>tag1 AND (tag2 OR tag3)</em>. You can also use a string array encoding. For example, <code>tagFilters: [&quot;tag1&quot;,[&quot;tag2&quot;,&quot;tag3&quot;]]</code> means <em>tag1 AND (tag2 OR tag3)</em>. Negations are supported via the <code>-</code> operator, prefixing the value. For example: <code>tagFilters=tag1,-tag2</code>.</p>
967
968
 
968
969
  <p>At indexing, tags should be added in the <strong>_tags</strong> attribute of objects. For example <code>{&quot;_tags&quot;:[&quot;tag1&quot;,&quot;tag2&quot;]}</code>.</p>
969
970
 
@@ -1047,9 +1048,11 @@ You can also use a string array encoding (for example `numericFilters: ["price>1
1047
1048
  </td>
1048
1049
  <td class='client-readme-param-content'>
1049
1050
  <p>Filter the query with numeric, facet or/and tag filters. The syntax is a SQL like syntax, you can use the OR and AND keywords. The syntax for the underlying numeric, facet and tag filters is the same than in the other filters:
1050
- <code>available=1 AND (category:Book OR NOT category:Ebook) AND public</code>
1051
+ <code>available=1 AND (category:Book OR NOT category:Ebook) AND _tags:public</code>
1051
1052
  <code>date: 1441745506 TO 1441755506 AND inStock &gt; 0 AND author:&quot;John Doe&quot;</code></p>
1052
1053
 
1054
+ <p>If no attribute name is specified, the filter applies to <code>_tags</code>. For example: <code>public OR user_42</code> will translate to <code>_tags:public OR _tags:user_42</code>.</p>
1055
+
1053
1056
  <p>The list of keywords is:</p>
1054
1057
 
1055
1058
  <ul>
@@ -1111,7 +1114,7 @@ You can send multiple queries with a single API call using a batch of queries:
1111
1114
  # - 1st query targets index `categories`
1112
1115
  # - 2nd and 3rd queries target index `products`
1113
1116
  res = Algolia.multiple_queries([{:index_name => "categories", "query" => my_query_string, "hitsPerPage" => 3}
1114
- , {:index_name => "products", "query" => my_query_string, "hitsPerPage" => 3, "tagFilters" => "promotion"}
1117
+ , {:index_name => "products", "query" => my_query_string, "hitsPerPage" => 3, "filters" => "_tags:promotion"}
1115
1118
  , {:index_name => "products", "query" => my_query_string, "hitsPerPage" => 10}])
1116
1119
 
1117
1120
  puts res["results"]
@@ -1154,12 +1157,12 @@ You can delete an object using its `objectID`:
1154
1157
  index.delete_object("myID")
1155
1158
  ```
1156
1159
 
1157
-
1158
1160
  Delete by query
1159
1161
  ==================
1160
1162
 
1161
1163
  You can delete all objects matching a single query with the following code. Internally, the API client performs the query, deletes all matching hits, and waits until the deletions have been applied.
1162
1164
 
1165
+
1163
1166
  ```ruby
1164
1167
  params = {}
1165
1168
  index.delete_by_query("John", params)
@@ -1472,6 +1475,38 @@ To get a full description of how the Ranking works, you can have a look at our <
1472
1475
  </tr>
1473
1476
 
1474
1477
 
1478
+ <tr>
1479
+ <td valign='top'>
1480
+ <div class='client-readme-param-container'>
1481
+ <div class='client-readme-param-container-inner'>
1482
+ <div class='client-readme-param-name'><code>disablePrefixOnAttributes</code></div>
1483
+ <div class="client-readme-param-meta"><div><em>Type: <strong>string array</strong></em></div></div>
1484
+ </div>
1485
+ </div>
1486
+ </td>
1487
+ <td class='client-readme-param-content'>
1488
+ <p>List of attributes on which you want to disable prefix matching (must be a subset of the <code>attributesToIndex</code> index setting). This setting is useful on attributes that contain string that should not be matched as a prefix (for example a product SKU). By default the list is empty.</p>
1489
+
1490
+ </td>
1491
+ </tr>
1492
+
1493
+
1494
+ <tr>
1495
+ <td valign='top'>
1496
+ <div class='client-readme-param-container'>
1497
+ <div class='client-readme-param-container-inner'>
1498
+ <div class='client-readme-param-name'><code>disableExactOnAttributes</code></div>
1499
+ <div class="client-readme-param-meta"><div><em>Type: <strong>string array</strong></em></div></div>
1500
+ </div>
1501
+ </div>
1502
+ </td>
1503
+ <td class='client-readme-param-content'>
1504
+ <p>List of attributes on which you want to disable the computation of <code>exact</code> criteria (must be a subset of the <code>attributesToIndex</code> index setting). By default the list is empty.</p>
1505
+
1506
+ </td>
1507
+ </tr>
1508
+
1509
+
1475
1510
  <tr>
1476
1511
  <td valign='top'>
1477
1512
  <div class='client-readme-param-container'>
@@ -1929,7 +1964,6 @@ The move command is particularly useful if you want to update a big index atomic
1929
1964
  puts Algolia.move_index("MyNewIndex", "MyIndex")
1930
1965
  ```
1931
1966
 
1932
-
1933
1967
  Backup / Export an index
1934
1968
  ==================
1935
1969
 
@@ -1947,18 +1981,20 @@ Example:
1947
1981
 
1948
1982
  ```ruby
1949
1983
  # Iterate with a filter over the index
1950
- index.browse({:query => "test", :numericFilters => 'i=42'}) do
1951
- # Do something
1984
+ index.browse({:query => "test", :filters => 'i=42'}) do |hit|
1985
+ # Do something
1952
1986
  end
1953
1987
  ```
1954
1988
 
1955
1989
 
1956
1990
 
1957
1991
 
1992
+
1958
1993
  API Keys
1959
1994
  ==================
1960
1995
 
1961
- The ADMIN API key provides full control of all your indices.
1996
+ The **admin** API key provides full control of all your indices. *The admin API key should always be kept secure; do NOT use it from outside your back-end.*
1997
+
1962
1998
  You can also generate user API keys to control security.
1963
1999
  These API keys can be restricted to a set of operations or/and restricted to a given index.
1964
2000
 
@@ -2183,7 +2219,7 @@ You may have a single index containing **per user** data. In that case, all reco
2183
2219
  ```ruby
2184
2220
  # generate a public API key for user 42. Here, records are tagged with:
2185
2221
  # - 'user_XXXX' if they are visible by user XXXX
2186
- public_key = Algolia.generate_secured_api_key 'YourSearchOnlyApiKey', {'tagFilters'=> 'user_42'}
2222
+ public_key = Algolia.generate_secured_api_key 'YourSearchOnlyApiKey', {'filters'=> '_tags:user_42'}
2187
2223
  ```
2188
2224
 
2189
2225
  This public API key can then be used in your JavaScript code as follow:
@@ -2208,7 +2244,7 @@ You can mix rate limits and secured API keys by setting a `userToken` query para
2208
2244
  ```ruby
2209
2245
  # generate a public API key for user 42. Here, records are tagged with:
2210
2246
  # - 'user_XXXX' if they are visible by user XXXX
2211
- public_key = Algolia.generate_secured_api_key 'YourSearchOnlyApiKey', {'tagFilters'=> 'user_42', 'userToken'=> 'user_42'}
2247
+ public_key = Algolia.generate_secured_api_key 'YourSearchOnlyApiKey', {'filters'=> '_tags:user_42', 'userToken'=> 'user_42'}
2212
2248
  ```
2213
2249
 
2214
2250
  This public API key can then be used in your JavaScript code as follow:
@@ -2229,6 +2265,8 @@ index.search('another query', function(err, content) {
2229
2265
  ```
2230
2266
 
2231
2267
 
2268
+
2269
+
2232
2270
  Logs
2233
2271
  ==================
2234
2272
 
@@ -176,23 +176,24 @@ module Algolia
176
176
  # @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
177
177
  #
178
178
  def browse(pageOrQueryParameters = nil, hitsPerPage = nil, &block)
179
+ params = {}
180
+ if pageOrQueryParameters.is_a?(Hash)
181
+ params.merge!(pageOrQueryParameters)
182
+ else
183
+ params[:page] = pageOrQueryParameters unless pageOrQueryParameters.nil?
184
+ end
185
+ if hitsPerPage.is_a?(Hash)
186
+ params.merge!(hitsPerPage)
187
+ else
188
+ params[:hitsPerPage] = hitsPerPage unless hitsPerPage.nil?
189
+ end
190
+
179
191
  if block_given?
180
- params = {}
181
- if pageOrQueryParameters.is_a?(Hash)
182
- params.merge!(pageOrQueryParameters)
183
- else
184
- params[:page] = pageOrQueryParameters unless pageOrQueryParameters.nil?
185
- end
186
- if hitsPerPage.is_a?(Hash)
187
- params.merge!(hitsPerPage)
188
- else
189
- params[:hitsPerPage] = hitsPerPage unless hitsPerPage.nil?
190
- end
191
192
  IndexBrowser.new(client, name, params).browse(&block)
192
193
  else
193
- pageOrQueryParameters ||= 0
194
- hitsPerPage ||= 1000
195
- client.get(Protocol.browse_uri(name, {:page => pageOrQueryParameters, :hitsPerPage => hitsPerPage}), :read)
194
+ params[:page] ||= 0
195
+ params[:hitsPerPage] ||= 1000
196
+ client.get(Protocol.browse_uri(name, params), :read)
196
197
  end
197
198
  end
198
199
 
@@ -378,7 +379,9 @@ module Algolia
378
379
  # @param query the query string
379
380
  # @param params the optional query parameters
380
381
  #
381
- def delete_by_query(query, params = {})
382
+ def delete_by_query(query, params = nil)
383
+ raise ArgumentError.new('query cannot be nil, use the `clear` method to wipe the entire index') if query.nil? && params.nil?
384
+ params ||= {}
382
385
  params.delete(:hitsPerPage)
383
386
  params.delete('hitsPerPage')
384
387
  params.delete(:attributesToRetrieve)
@@ -416,61 +419,21 @@ module Algolia
416
419
  #
417
420
  # Set settings for this index
418
421
  #
419
- # @param settigns the settings object that can contains :
420
- # - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
421
- # - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
422
- # - hitsPerPage: (integer) the number of hits per page (default = 10).
423
- # - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
424
- # If set to null, all attributes are retrieved.
425
- # - attributesToHighlight: (array of strings) default list of attributes to highlight.
426
- # If set to null, all indexed attributes are highlighted.
427
- # - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number of words to return (syntax is attributeName:nbWords).
428
- # By default no snippet is computed. If set to null, no snippet is computed.
429
- # - attributesToIndex: (array of strings) the list of fields you want to index.
430
- # If set to null, all textual and numerical attributes of your objects are indexed, but you should update it to get optimal results.
431
- # This parameter has two important uses:
432
- # - Limit the attributes to index: For example if you store a binary image in base64, you want to store it and be able to
433
- # retrieve it but you don't want to search in the base64 string.
434
- # - Control part of the ranking*: (see the ranking parameter for full explanation) Matches in attributes at the beginning of
435
- # the list will be considered more important than matches in attributes further down the list.
436
- # In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable
437
- # this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
438
- # - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
439
- # All strings in the attribute selected for faceting are extracted and added as a facet. If set to null, no attribute is used for faceting.
440
- # - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
441
- # in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
442
- # For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best one is kept and others are removed.
443
- # - ranking: (array of strings) controls the way results are sorted.
444
- # We have six available criteria:
445
- # - typo: sort according to number of typos,
446
- # - geo: sort according to decreassing distance when performing a geo-location based search,
447
- # - proximity: sort according to the proximity of query words in hits,
448
- # - attribute: sort according to the order of attributes defined by attributesToIndex,
449
- # - exact:
450
- # - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
451
- # For example if you search for the "V" TV show, you want to find it with the "V" query and avoid to have all popular TV
452
- # show starting by the v letter before it.
453
- # - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
454
- # - custom: sort according to a user defined formula set in **customRanking** attribute.
455
- # The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
456
- # - customRanking: (array of strings) lets you specify part of the ranking.
457
- # The syntax of this condition is an array of strings containing attributes prefixed by asc (ascending order) or desc (descending order) operator.
458
- # For example `"customRanking" => ["desc(population)", "asc(name)"]`
459
- # - queryType: Select how the query words are interpreted, it can be one of the following value:
460
- # - prefixAll: all query words are interpreted as prefixes,
461
- # - prefixLast: only the last word is interpreted as a prefix (default behavior),
462
- # - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
463
- # - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
464
- # - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
465
- # - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
466
- #
467
422
  def set_settings(new_settings)
468
423
  client.put(Protocol.settings_uri(name), new_settings.to_json)
469
424
  end
470
425
 
426
+ # Set settings for this index and wait end of indexing
427
+ #
428
+ def set_settings!(new_settings)
429
+ res = set_settings(new_settings)
430
+ wait_task(res["taskID"])
431
+ return res
432
+ end
433
+
471
434
  # Get settings of this index
472
435
  def get_settings
473
- client.get(Protocol.settings_uri(name), :read)
436
+ client.get("#{Protocol.settings_uri(name)}?getVersion=2", :read)
474
437
  end
475
438
 
476
439
  # List all existing user keys with their associated ACLs
@@ -683,6 +646,105 @@ module Algolia
683
646
  Algolia.list_indexes
684
647
  end
685
648
 
649
+ # Search synonyms
650
+ #
651
+ # @param query the query
652
+ # @param params an optional hash of :type, :page, :hitsPerPage
653
+ def search_synonyms(query, params = {})
654
+ type = params[:type] || params['type']
655
+ type = type.join(',') if type.is_a?(Array)
656
+ page = params[:page] || params['page'] || 0
657
+ hits_per_page = params[:hitsPerPage] || params['hitsPerPage'] || 20
658
+ params = {
659
+ :query => query,
660
+ :type => type.to_s,
661
+ :page => page,
662
+ :hitsPerPage => hits_per_page
663
+ }
664
+ client.post(Protocol.search_synonyms_uri(name), params.to_json, :read)
665
+ end
666
+
667
+ # Get a synonym
668
+ #
669
+ # @param objectID the synonym objectID
670
+ def get_synonym(objectID)
671
+ client.get(Protocol.synonym_uri(name, objectID), :read)
672
+ end
673
+
674
+ # Delete a synonym
675
+ #
676
+ # @param objectID the synonym objectID
677
+ # @param forward_to_slaves should we forward the delete to slave indices
678
+ def delete_synonym(objectID, forward_to_slaves = false)
679
+ client.delete("#{Protocol.synonym_uri(name, objectID)}?forwardToSlaves=#{forward_to_slaves}", :write)
680
+ end
681
+
682
+ # Delete a synonym and wait the end of indexing
683
+ #
684
+ # @param objectID the synonym objectID
685
+ # @param forward_to_slaves should we forward the delete to slave indices
686
+ def delete_synonym!(objectID, forward_to_slaves = false)
687
+ res = delete_synonym(objectID, forward_to_slaves)
688
+ wait_task(res["taskID"])
689
+ return res
690
+ end
691
+
692
+ # Save a synonym
693
+ #
694
+ # @param objectID the synonym objectID
695
+ # @param synonym the synonym
696
+ # @param forward_to_slaves should we forward the delete to slave indices
697
+ def save_synonym(objectID, synonym, forward_to_slaves = false)
698
+ client.put("#{Protocol.synonym_uri(name, objectID)}?forwardToSlaves=#{forward_to_slaves}", synonym.to_json, :write)
699
+ end
700
+
701
+ # Save a synonym and wait the end of indexing
702
+ #
703
+ # @param objectID the synonym objectID
704
+ # @param synonym the synonym
705
+ # @param forward_to_slaves should we forward the delete to slave indices
706
+ def save_synonym!(objectID, synonym, forward_to_slaves = false)
707
+ res = save_synonym(objectID, synonym, forward_to_slaves)
708
+ wait_task(res["taskID"])
709
+ return res
710
+ end
711
+
712
+ # Clear all synonyms
713
+ #
714
+ # @param forward_to_slaves should we forward the delete to slave indices
715
+ def clear_synonyms(forward_to_slaves = false)
716
+ client.post("#{Protocol.clear_synonyms_uri(name)}?forwardToSlaves=#{forward_to_slaves}", :write)
717
+ end
718
+
719
+ # Clear all synonyms and wait the end of indexing
720
+ #
721
+ # @param forward_to_slaves should we forward the delete to slave indices
722
+ def clear_synonyms!(forward_to_slaves = false)
723
+ res = clear_synonyms(forward_to_slaves)
724
+ wait_task(res["taskID"])
725
+ return res
726
+ end
727
+
728
+ # Add/Update an array of synonyms
729
+ #
730
+ # @param synonyms the array of synonyms to add/update
731
+ # @param forward_to_slaves should we forward the delete to slave indices
732
+ # @param replace_existing_synonyms should we replace the existing synonyms before adding the new ones
733
+ def batch_synonyms(synonyms, forward_to_slaves = false, replace_existing_synonyms = false)
734
+ client.post("#{Protocol.batch_synonyms_uri(name)}?forwardToSlaves=#{forward_to_slaves}&replaceExistingSynonyms=#{replace_existing_synonyms}", synonyms.to_json, :batch)
735
+ end
736
+
737
+ # Add/Update an array of synonyms and wait the end of indexing
738
+ #
739
+ # @param synonyms the array of synonyms to add/update
740
+ # @param forward_to_slaves should we forward the delete to slave indices
741
+ # @param replace_existing_synonyms should we replace the existing synonyms before adding the new ones
742
+ def batch_synonyms!(synonyms, forward_to_slaves = false, replace_existing_synonyms = false)
743
+ res = batch_synonyms(synonyms, forward_to_slaves, replace_existing_synonyms)
744
+ wait_task(res["taskID"])
745
+ return res
746
+ end
747
+
686
748
  private
687
749
  def check_array(objs)
688
750
  raise ArgumentError.new("argument must be an array of objects") if !objs.is_a?(Array)
@@ -5,7 +5,7 @@ module Algolia
5
5
  module Protocol
6
6
 
7
7
  # Basics
8
-
8
+
9
9
  # The version of the REST API implemented by this module.
10
10
  VERSION = 1
11
11
 
@@ -19,11 +19,11 @@ module Algolia
19
19
  # The HTTP header used for passing your API key to the
20
20
  # Algolia API.
21
21
  HEADER_API_KEY = "X-Algolia-API-Key"
22
-
22
+
23
23
  HEADER_FORWARDED_IP = "X-Forwarded-For"
24
24
 
25
25
  HEADER_FORWARDED_API_KEY = "X-Forwarded-API-Key"
26
-
26
+
27
27
  # HTTP ERROR CODES
28
28
  # ----------------------------------------
29
29
 
@@ -35,7 +35,7 @@ module Algolia
35
35
  # ----------------------------------------
36
36
 
37
37
  # Construct a uri to list available indexes
38
- def Protocol.indexes_uri
38
+ def Protocol.indexes_uri
39
39
  "/#{VERSION}/indexes"
40
40
  end
41
41
 
@@ -54,8 +54,8 @@ module Algolia
54
54
 
55
55
  def Protocol.batch_uri(index = nil)
56
56
  "#{index.nil? ? "/#{VERSION}/indexes/*" : index_uri(index)}/batch"
57
- end
58
-
57
+ end
58
+
59
59
  def Protocol.index_operation_uri(index)
60
60
  "#{index_uri(index)}/operation"
61
61
  end
@@ -63,7 +63,7 @@ module Algolia
63
63
  def Protocol.task_uri(index, task_id)
64
64
  "#{index_uri(index)}/task/#{task_id}"
65
65
  end
66
-
66
+
67
67
  def Protocol.object_uri(index, object_id, params = {})
68
68
  params = params.nil? || params.size == 0 ? "" : "?#{to_query(params)}"
69
69
  "#{index_uri(index)}/#{CGI.escape(object_id.to_s)}#{params}"
@@ -87,15 +87,15 @@ module Algolia
87
87
  params = create_if_not_exits ? "" : "?createIfNotExists=false"
88
88
  "#{index_uri(index)}/#{CGI.escape(object_id)}/partial#{params}"
89
89
  end
90
-
90
+
91
91
  def Protocol.settings_uri(index)
92
92
  "#{index_uri(index)}/settings"
93
93
  end
94
-
94
+
95
95
  def Protocol.clear_uri(index)
96
96
  "#{index_uri(index)}/clear"
97
97
  end
98
-
98
+
99
99
  def Protocol.logs(offset, length, only_errors = false)
100
100
  "/#{VERSION}/logs?offset=#{offset}&length=#{length}&onlyErrors=#{only_errors}"
101
101
  end
@@ -121,6 +121,26 @@ module Algolia
121
121
  "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
122
122
  end.join('&')
123
123
  end
124
-
124
+
125
+ def Protocol.synonyms_uri(index)
126
+ "#{index_uri(index)}/synonyms"
127
+ end
128
+
129
+ def Protocol.synonym_uri(index, object_id)
130
+ "#{synonyms_uri(index)}/#{CGI.escape(object_id)}"
131
+ end
132
+
133
+ def Protocol.search_synonyms_uri(index)
134
+ "#{synonyms_uri(index)}/search"
135
+ end
136
+
137
+ def Protocol.clear_synonyms_uri(index)
138
+ "#{synonyms_uri(index)}/clear"
139
+ end
140
+
141
+ def Protocol.batch_synonyms_uri(index)
142
+ "#{synonyms_uri(index)}/batch"
143
+ end
144
+
125
145
  end
126
146
  end
@@ -1,3 +1,3 @@
1
1
  module Algolia
2
- VERSION = "1.8.1"
2
+ VERSION = "1.9.0"
3
3
  end
@@ -225,6 +225,10 @@ describe 'Client' do
225
225
  @index.search('')['nbHits'].should eq(0)
226
226
  end
227
227
 
228
+ it "should not wipe the entire index with delete_by_query" do
229
+ expect { @index.delete_by_query(nil) }.to raise_error(ArgumentError)
230
+ end
231
+
228
232
  it "should copy the index" do
229
233
  index = Algolia::Index.new(safe_index_name("àlgol?à"))
230
234
  begin
@@ -373,52 +377,111 @@ describe 'Client' do
373
377
  res['facets']['g']['g2'].should be_nil
374
378
  end
375
379
 
376
- it "should test keys" do
380
+ def wait_key(index, key, &block)
381
+ 1.upto(60) do # do not wait too long
382
+ begin
383
+ k = index.get_user_key(key)
384
+ if block_given?
385
+ return if yield k
386
+ # not found
387
+ sleep 1
388
+ next
389
+ end
390
+ return
391
+ rescue
392
+ # not found
393
+ sleep 1
394
+ end
395
+ end
396
+ end
397
+
398
+ def wait_key_missing(index, key)
399
+ 1.upto(60) do # do not wait too long
400
+ begin
401
+ k = index.get_user_key(key)
402
+ sleep 1
403
+ rescue
404
+ # not found
405
+ return
406
+ end
407
+ end
408
+ end
409
+
410
+ def wait_global_key(key, &block)
411
+ 1.upto(60) do # do not wait too long
412
+ begin
413
+ k = Algolia.get_user_key(key)
414
+ if block_given?
415
+ return if yield k
416
+ # not found
417
+ sleep 1
418
+ next
419
+ end
420
+ return
421
+ rescue
422
+ # not found
423
+ sleep 1
424
+ end
425
+ end
426
+ end
427
+
428
+ def wait_global_key_missing(key)
429
+ 1.upto(60) do # do not wait too long
430
+ begin
431
+ k = Algolia.get_user_key(key)
432
+ sleep 1
433
+ rescue
434
+ # not found
435
+ return
436
+ end
437
+ end
438
+ end
439
+
440
+ it "should test index keys" do
441
+ @index.set_settings!({}) # ensure the index exists
377
442
  resIndex = @index.list_user_keys
378
443
  newIndexKey = @index.add_user_key(['search'])
379
444
  newIndexKey['key'].should_not eq("")
380
- sleep 5 # no task ID here
445
+ wait_key(@index, newIndexKey['key'])
381
446
  resIndexAfter = @index.list_user_keys
382
447
  is_include(resIndex['keys'], 'value', newIndexKey['key']).should eq(false)
383
448
  is_include(resIndexAfter['keys'], 'value', newIndexKey['key']).should eq(true)
384
449
  indexKey = @index.get_user_key(newIndexKey['key'])
385
450
  indexKey['acl'][0].should eq('search')
386
451
  @index.update_user_key(newIndexKey['key'], ['addObject'])
387
- sleep 5 # no task ID here
452
+ wait_key(@index, newIndexKey['key']) do |key|
453
+ key['acl'] == ['addObject']
454
+ end
388
455
  indexKey = @index.get_user_key(newIndexKey['key'])
389
456
  indexKey['acl'][0].should eq('addObject')
390
457
  @index.delete_user_key(newIndexKey['key'])
391
- sleep 5 # no task ID here
458
+ wait_key_missing(@index, newIndexKey['key'])
392
459
  resIndexEnd = @index.list_user_keys
393
460
  is_include(resIndexEnd['keys'], 'value', newIndexKey['key']).should eq(false)
461
+ end
394
462
 
395
-
463
+ it "should test global keys" do
396
464
  res = Algolia.list_user_keys
397
465
  newKey = Algolia.add_user_key(['search'])
398
466
  newKey['key'].should_not eq("")
399
- sleep 5 # no task ID here
467
+ wait_global_key(newKey['key'])
400
468
  resAfter = Algolia.list_user_keys
401
469
  is_include(res['keys'], 'value', newKey['key']).should eq(false)
402
470
  is_include(resAfter['keys'], 'value', newKey['key']).should eq(true)
403
471
  key = Algolia.get_user_key(newKey['key'])
404
472
  key['acl'][0].should eq('search')
405
473
  Algolia.update_user_key(newKey['key'], ['addObject'])
406
- sleep 5 # no task ID here
474
+ wait_global_key(newKey['key']) do |key|
475
+ key['acl'] == ['addObject']
476
+ end
407
477
  key = Algolia.get_user_key(newKey['key'])
408
478
  key['acl'][0].should eq('addObject')
409
479
  Algolia.delete_user_key(newKey['key'])
410
- sleep 5 # no task ID here
480
+ wait_global_key_missing(newKey['key'])
411
481
  resEnd = Algolia.list_user_keys
412
482
  is_include(resEnd['keys'], 'value', newKey['key']).should eq(false)
413
483
  end
414
484
 
415
- it "should check functions" do
416
- @index.get_settings
417
- @index.list_user_keys
418
- Algolia.list_user_keys
419
-
420
- end
421
-
422
485
  it "should handle slash in objectId" do
423
486
  @index.clear_index!()
424
487
  @index.add_object!({:firstname => "Robert", :objectID => "A/go/?a"})
@@ -808,4 +871,47 @@ describe 'Client' do
808
871
  @index.browse_from(answer['cursor'])['hits'].size.should eq(500)
809
872
  end
810
873
 
874
+ it "should test synonyms" do
875
+ @index.add_object! :name => '589 Howard St., San Francisco'
876
+ @index.search('Howard St San Francisco')['nbHits'].should eq(1)
877
+ @index.batch_synonyms! [
878
+ { :objectID => 'city', :type => 'synonym', :synonyms => ['San Francisco', 'SF'] },
879
+ { :objectID => 'street', :type => 'altCorrection1', :word => 'street', :corrections => ['st'] }
880
+ ]
881
+ @index.search_synonyms('')['nbHits'].should eq(2)
882
+ @index.search('Howard St SF')['nbHits'].should eq(1)
883
+
884
+ s = @index.get_synonym('city')
885
+ s['objectID'].should eq('city')
886
+ s['type'].should eq('synonym')
887
+
888
+ @index.search('Howard Street')['nbHits'].should eq(1)
889
+
890
+ @index.delete_synonym! 'city'
891
+ @index.search('Howard Street SF')['nbHits'].should eq(0)
892
+
893
+ @index.clear_synonyms!
894
+ @index.search_synonyms('')['nbHits'].should eq(0)
895
+ end
896
+
897
+ context 'DNS timeout' do
898
+ before(:all) do
899
+ @client = Algolia::Client.new :application_id => ENV['ALGOLIA_APPLICATION_ID'], :api_key => ENV['ALGOLIA_API_KEY'],
900
+ :hosts => [
901
+ "#{ENV['ALGOLIA_APPLICATION_ID']}.algolia.biz",
902
+ "#{ENV['ALGOLIA_APPLICATION_ID']}.algolia.net",
903
+ "#{ENV['ALGOLIA_APPLICATION_ID']}-1.algolianet.com",
904
+ "#{ENV['ALGOLIA_APPLICATION_ID']}-2.algolianet.com",
905
+ "#{ENV['ALGOLIA_APPLICATION_ID']}-3.algolianet.com"
906
+ ],
907
+ :connect_timeout => 5
908
+ end
909
+
910
+ it "should fallback to the 2nd host after a few seconds" do
911
+ start_time = Time.now
912
+ @client.list_indexes # fallback on the second host after 5 sec (connection timeout)
913
+ expect(start_time.to_i + 5).to be <= Time.now.to_i + 1
914
+ end
915
+ end
916
+
811
917
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: algoliasearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Algolia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-14 00:00:00.000000000 Z
11
+ date: 2016-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpclient
@@ -131,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
131
  version: '0'
132
132
  requirements: []
133
133
  rubyforge_project:
134
- rubygems_version: 2.4.8
134
+ rubygems_version: 2.5.1
135
135
  signing_key:
136
136
  specification_version: 4
137
137
  summary: A simple Ruby client for the algolia.com REST API