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 +4 -4
- data/.travis.yml +1 -0
- data/ChangeLog +3 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +15 -19
- data/README.md +53 -15
- data/lib/algolia/index.rb +126 -64
- data/lib/algolia/protocol.rb +31 -11
- data/lib/algolia/version.rb +1 -1
- data/spec/client_spec.rb +121 -15
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1035690a7833a5235345419a3376a771932d281e
|
4
|
+
data.tar.gz: d7eb3c02b159845109c25b7e49b61191e72a2d2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57f1c99c147f759f6c6a70ecae88b30ec607ab886d720fcc426bf47b1a528028494b54d2edbfd40eb2e560074866b6453fd5727e117ecb9e26abe98ec14f91b3
|
7
|
+
data.tar.gz: b19a6880521c58d9fd74a4b62244b61aeb348b7f61c06aaf6d90e0565681c4daf2cb5f16d4fa8bcec60a3c2d9762b0d521ac12e742715f7028d56b2589b942f0
|
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
data/Gemfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
source '
|
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'
|
data/Gemfile.lock
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
GEM
|
2
|
-
remote:
|
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.
|
8
|
-
|
9
|
-
|
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.
|
36
|
-
json (1.8.
|
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.
|
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.
|
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.
|
277
|
+
simplecov (0.11.2)
|
281
278
|
docile (~> 1.1.0)
|
282
|
-
|
283
|
-
simplecov-html (~> 0.
|
284
|
-
simplecov-html (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.
|
285
|
+
term-ansicolor (1.3.2)
|
289
286
|
tins (~> 1.0)
|
290
287
|
thor (0.19.1)
|
291
|
-
tins (1.
|
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
|
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--
|
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>"name,address"</code>). You can also use a string array encoding (for example <code>["name","address"]</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
|
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>
|
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: ["tag1",["tag2","tag3"]]</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>{"_tags":["tag1","tag2"]}</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 > 0 AND author:"John Doe"</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, "
|
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", :
|
1951
|
-
|
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
|
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', {'
|
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', {'
|
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
|
|
data/lib/algolia/index.rb
CHANGED
@@ -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
|
-
|
194
|
-
hitsPerPage ||= 1000
|
195
|
-
client.get(Protocol.browse_uri(name,
|
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)
|
data/lib/algolia/protocol.rb
CHANGED
@@ -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
|
data/lib/algolia/version.rb
CHANGED
data/spec/client_spec.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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-
|
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.
|
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
|