algoliasearch 1.8.1 → 1.9.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
  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