algoliasearch 1.1.16 → 1.1.17

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: 9a5b805f07b381464702a5730df66afaf269deab
4
- data.tar.gz: 20ec0ac8649ed6260de80004ffc71bda15f04f3c
3
+ metadata.gz: 14d536f146b031e91b22b0fa8fd6ae490d5313ca
4
+ data.tar.gz: 6363a928bb6e679e51b55e73fffdc5b8fdea4b6a
5
5
  SHA512:
6
- metadata.gz: ca22ebe2ea6f5bf41069c668119981da77bb2f36b982b1f4b12a8097bf0fcce98944664fd4f8d9ced69211eaca2f6ed56c6a171fc2544e6b05c873de1896363f
7
- data.tar.gz: 0502436b3c8f7ba247595b97aa7ac3d47ba172631dfd2267e8804544524ccbcac2161c76eee07473f407d3dc3b0b40109b62be5a91a643214da4b9b7ed69922f
6
+ metadata.gz: d618bbb9d33e9af749ba5a0b5a61bb7c94f3c3af1680d28969eb080df1ebc3abb9ccc48f5bb7e0c455eca13ba4080a8af18ea82c955c0ac00f58644253be1fba
7
+ data.tar.gz: 4492f1024fe3bdaeacc0523ea0d3e99cc63db2d03cbbc8c8ab5a19cc58b8bcad18f790a23708744e91b9db14d9ba5fe69badfc06bb41c2ccc434d27c31d3777e
data/ChangeLog CHANGED
@@ -1,5 +1,10 @@
1
1
  CHANGELOG
2
2
 
3
+ 2014-01-02 1.1.17
4
+
5
+ * Ability to use IP rate limit behind a proxy forwarding the end-user's IP
6
+ * Add documentation for the new 'attributeForDistinct' setting and 'distinct' search parameter
7
+
3
8
  2013-12-16 1.1.16
4
9
 
5
10
  * Add arguments type-checking
data/README.md CHANGED
@@ -148,6 +148,9 @@ You can use the following optional arguments:
148
148
  * **facetFilters**: filter the query by a list of facets. Facets are separated by commas and each facet is encoded as `attributeName:value`. For example: `facetFilters=category:Book,author:John%20Doe`. You can also use a string array encoding (for example `["category:Book","author:John%20Doe"]`).
149
149
  * **facets**: List of object attributes that you want to use for faceting. <br/>Attributes are separated with a comma (for example `"category,author"` ). You can also use a JSON string array encoding (for example `["category","author"]` ). Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter. You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
150
150
 
151
+ #### Distinct parameter
152
+ * **distinct**: If set to 1, enable the distinct feature (disabled by default) if the `attributeForDistinct` index setting is set. This feature is similar to the SQL "distinct" keyword: when enabled in a query with the `distinct=1` parameter, all hits containing a duplicate value for the attributeForDistinct attribute are removed from results. 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.
153
+
151
154
  ```ruby
152
155
  index = Algolia::Index.new("contacts")
153
156
  res = index.search("query string")
@@ -273,6 +276,7 @@ You can retrieve all settings using the `getSettings` function. The result will
273
276
  * *Limit the attributes to index*.<br/>For example if you store a binary image in base64, you want to store it and be able to retrieve it but you don't want to search in the base64 string.
274
277
  * *Control part of the ranking*.<br/>(see the ranking parameter for full explanation) Matches in attributes at the beginning of the list will be considered more important than matches in attributes further down the list. In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable this behavior if you add your attribute inside `unordered(AttributeName)`, for example `attributesToIndex: ["title", "unordered(text)"]`.
275
278
  * **attributesForFaceting**: (array of strings) The list of fields you want to use for faceting. 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.
279
+ * **attributeForDistinct**: The attribute name used for the `Distinct` feature. This feature is similar to the SQL "distinct" keyword: when enabled in query with the `distinct=1` parameter, all hits containing a duplicate value for this attribute are removed from results. 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.
276
280
  * **ranking**: (array of strings) controls the way results are sorted.<br/>We have six available criteria:
277
281
  * **typo**: sort according to number of typos,
278
282
  * **geo**: sort according to decreassing distance when performing a geo-location based search,
@@ -422,6 +426,8 @@ You can also create an API Key with advanced restrictions:
422
426
 
423
427
  * Add a validity period: the key will be valid only for a specific period of time (in seconds),
424
428
  * Specify the maximum number of API calls allowed from an IP address per hour. Each time an API call is performed with this key, a check is performed. If the IP at the origin of the call did more than this number of calls in the last hour, a 403 code is returned. Defaults to 0 (no rate limit). This parameter can be used to protect you from attempts at retrieving your entire content by massively querying the index.
429
+
430
+ Note: If you are sending the query through your servers, you must use the `Algolia.enable_rate_limit_forward("TheAdminAPIKey", "EndUserIP", "APIKeyWithRateLimit")` function to enable rate-limit.
425
431
  * Specify the maximum number of hits this API key can retrieve in one call. Defaults to 0 (unlimited). This parameter can be used to protect you from attempts at retrieving your entire content by massively querying the index.
426
432
 
427
433
  ```ruby
@@ -2,15 +2,15 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: algoliasearch 1.1.16 ruby lib
5
+ # stub: algoliasearch 1.1.17 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "algoliasearch"
9
- s.version = "1.1.16"
9
+ s.version = "1.1.17"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.authors = ["Algolia"]
13
- s.date = "2013-12-20"
13
+ s.date = "2014-01-02"
14
14
  s.description = "A simple Ruby client for the algolia.com REST API"
15
15
  s.email = "contact@algolia.com"
16
16
  s.extra_rdoc_files = [
@@ -39,7 +39,8 @@ Gem::Specification.new do |s|
39
39
  "resources/ca-bundle.crt",
40
40
  "spec/client_spec.rb",
41
41
  "spec/mock_spec.rb",
42
- "spec/spec_helper.rb"
42
+ "spec/spec_helper.rb",
43
+ "spec/stub_spec.rb"
43
44
  ]
44
45
  s.homepage = "http://github.com/algolia/algoliasearch-client-ruby"
45
46
  s.licenses = ["MIT"]
@@ -72,8 +72,6 @@ module Algolia
72
72
  request(uri, :DELETE)
73
73
  end
74
74
 
75
- private
76
-
77
75
  # this method returns a thread-local array of sessions
78
76
  def thread_local_hosts
79
77
  if Thread.current[:algolia_hosts].nil?
@@ -108,12 +106,55 @@ module Algolia
108
106
  # Initialize the singleton instance of Client which is used
109
107
  # by all API methods.
110
108
  def Algolia.init(options = {})
111
- defaulted = { :api_id => ENV["ALGOLIA_API_ID"], :api_key => ENV["ALGOLIA_REST_API_KEY"] }
109
+ application_id = ENV["ALGOLIA_API_ID"] || ENV["ALGOLIA_APPLICATION_ID"]
110
+ api_key = ENV["ALGOLIA_REST_API_KEY"] || ENV['ALGOLIA_API_KEY']
111
+
112
+ defaulted = { :application_id => application_id, :api_key => api_key }
112
113
  defaulted.merge!(options)
113
114
 
114
115
  @@client = Client.new(defaulted)
115
116
  end
116
117
 
118
+ #
119
+ # Allow to use IP rate limit when you have a proxy between end-user and Algolia.
120
+ # This option will set the X-Forwarded-For HTTP header with the client IP and the X-Forwarded-API-Key with the API Key having rate limits.
121
+ # @param admin_api_key the admin API Key you can find in your dashboard
122
+ # @param end_user_ip the end user IP (you can use both IPV4 or IPV6 syntax)
123
+ # @param rate_limit_api_key the API key on which you have a rate limit
124
+ #
125
+ def Algolia.enable_rate_limit_forward(admin_api_key, end_user_ip, rate_limit_api_key)
126
+ Algolia.client.thread_local_hosts.each do |host|
127
+ session = host["session"]
128
+ session.headers[Protocol::HEADER_API_KEY] = admin_api_key
129
+ session.headers[Protocol::HEADER_FORWARDED_IP] = end_user_ip
130
+ session.headers[Protocol::HEADER_FORWARDED_API_KEY] = rate_limit_api_key
131
+ end
132
+ end
133
+
134
+ #
135
+ # Disable IP rate limit enabled with enableRateLimitForward() function
136
+ #
137
+ def Algolia.disable_rate_limit_forward
138
+ Algolia.client.thread_local_hosts.each do |host|
139
+ session = host["session"]
140
+ session.headers[Protocol::HEADER_API_KEY] = Algolia.client.api_key
141
+ session.headers.delete(Protocol::HEADER_FORWARDED_IP)
142
+ session.headers.delete(Protocol::HEADER_FORWARDED_API_KEY)
143
+ end
144
+ end
145
+
146
+ #
147
+ # Convenience method thats wraps enable_rate_limit_forward/disable_rate_limit_forward
148
+ #
149
+ def Algolia.with_rate_limits(end_user_ip, rate_limit_api_key, &block)
150
+ Algolia.enable_rate_limit_forward(Algolia.client.api_key, end_user_ip, rate_limit_api_key)
151
+ begin
152
+ yield
153
+ ensure
154
+ Algolia.disable_rate_limit_forward
155
+ end
156
+ end
157
+
117
158
  #
118
159
  # List all existing indexes
119
160
  # return an Answer object with answer in the form
@@ -126,8 +167,8 @@ module Algolia
126
167
 
127
168
  #
128
169
  # Move an existing index.
129
- # @param srcIndexName the name of index to copy.
130
- # @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
170
+ # @param src_index the name of index to copy.
171
+ # @param dst_index the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
131
172
  #
132
173
  def Algolia.move_index(src_index, dst_index)
133
174
  request = {"operation" => "move", "destination" => dst_index};
@@ -136,8 +177,8 @@ module Algolia
136
177
 
137
178
  #
138
179
  # Copy an existing index.
139
- # @param srcIndexName the name of index to copy.
140
- # @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
180
+ # @param src_index the name of index to copy.
181
+ # @param dst_index the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
141
182
  #
142
183
  def Algolia.copy_index(src_index, dst_index)
143
184
  request = {"operation" => "copy", "destination" => dst_index};
data/lib/algolia/index.rb CHANGED
@@ -124,7 +124,12 @@ module Algolia
124
124
  # - prefixLast: only the last word is interpreted as a prefix (default behavior),
125
125
  # - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
126
126
  # - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
127
- # The list of words is comma separated. #
127
+ # The list of words is comma separated.
128
+ # - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
129
+ # This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
130
+ # all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
131
+ # For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
132
+ # one is kept and others are removed.
128
133
  def search(query, params = {})
129
134
  encoded_params = Hash[params.map { |k,v| [k.to_s, v.is_a?(Array) ? v.to_json : v] }]
130
135
  Algolia.client.get(Protocol.search_uri(name, query, encoded_params))
@@ -336,13 +341,20 @@ module Algolia
336
341
  # this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
337
342
  # - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
338
343
  # 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.
344
+ # - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
345
+ # in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
346
+ # 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.
339
347
  # - ranking: (array of strings) controls the way results are sorted.
340
348
  # We have six available criteria:
341
349
  # - typo: sort according to number of typos,
342
350
  # - geo: sort according to decreassing distance when performing a geo-location based search,
343
351
  # - proximity: sort according to the proximity of query words in hits,
344
352
  # - attribute: sort according to the order of attributes defined by attributesToIndex,
345
- # - exact: sort according to the number of words that are matched identical to query word (and not as a prefix),
353
+ # - exact:
354
+ # - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
355
+ # 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
356
+ # show starting by the v letter before it.
357
+ # - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
346
358
  # - custom: sort according to a user defined formula set in **customRanking** attribute.
347
359
  # The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
348
360
  # - customRanking: (array of strings) lets you specify part of the ranking.
@@ -20,6 +20,10 @@ module Algolia
20
20
  # Algolia API.
21
21
  HEADER_API_KEY = "X-Algolia-API-Key"
22
22
 
23
+ HEADER_FORWARDED_IP = "X-Forwarded-For"
24
+
25
+ HEADER_FORWARDED_API_KEY = "X-Forwarded-API-Key"
26
+
23
27
  # HTTP ERROR CODES
24
28
  # ----------------------------------------
25
29
 
@@ -1,3 +1,3 @@
1
1
  module Algolia
2
- VERSION = "1.1.16"
2
+ VERSION = "1.1.17"
3
3
  end
data/spec/stub_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ require 'webmock'
4
+
5
+ describe 'With a rate limited client' do
6
+
7
+ before(:each) do
8
+ WebMock.enable!
9
+ Thread.current[:algolia_hosts] = nil
10
+ end
11
+
12
+ it "should pass the right headers" do
13
+ WebMock.stub_request(:get, %r{https://.*\.algolia\.io/1/indexes/friends\?query=.*}).
14
+ with(:headers => {'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>"Algolia for Ruby #{Algolia::VERSION}", 'X-Algolia-Api-Key'=>ENV['ALGOLIA_API_KEY'], 'X-Algolia-Application-Id'=>ENV['ALGOLIA_APPLICATION_ID'], 'X-Forwarded-Api-Key'=>'ratelimitapikey', 'X-Forwarded-For'=>'1.2.3.4'}).
15
+ to_return(:status => 200, :body => "{ \"hits\": [], \"fakeAttribute\": 1 }", :headers => {})
16
+ Algolia.enable_rate_limit_forward ENV['ALGOLIA_API_KEY'], "1.2.3.4", "ratelimitapikey"
17
+ index = Algolia::Index.new("friends")
18
+ index.search('foo')['fakeAttribute'].should == 1
19
+ index.search('bar')['fakeAttribute'].should == 1
20
+ end
21
+
22
+ it "should use original headers" do
23
+ WebMock.stub_request(:get, %r{https://.*\.algolia\.io/1/indexes/friends\?query=.*}).
24
+ with(:headers => {'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>"Algolia for Ruby #{Algolia::VERSION}", 'X-Algolia-Api-Key'=>ENV['ALGOLIA_API_KEY'], 'X-Algolia-Application-Id'=>ENV['ALGOLIA_APPLICATION_ID'] }).
25
+ to_return(:status => 200, :body => "{ \"hits\": [], \"fakeAttribute\": 2 }", :headers => {})
26
+ Algolia.disable_rate_limit_forward
27
+ index = Algolia::Index.new("friends")
28
+ index.search('bar')['fakeAttribute'].should == 2
29
+ end
30
+
31
+ it "should pass the right headers in the scope" do
32
+ WebMock.stub_request(:get, %r{https://.*\.algolia\.io/1/indexes/friends\?query=.*}).
33
+ with(:headers => {'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>"Algolia for Ruby #{Algolia::VERSION}", 'X-Algolia-Api-Key'=>ENV['ALGOLIA_API_KEY'], 'X-Algolia-Application-Id'=>ENV['ALGOLIA_APPLICATION_ID'], 'X-Forwarded-Api-Key'=>'ratelimitapikey', 'X-Forwarded-For'=>'1.2.3.4'}).
34
+ to_return(:status => 200, :body => "{ \"hits\": [], \"fakeAttribute\": 1 }", :headers => {})
35
+ Algolia.with_rate_limits "1.2.3.4", "ratelimitapikey" do
36
+ index = Algolia::Index.new("friends")
37
+ index.search('foo')['fakeAttribute'].should == 1
38
+ index.search('bar')['fakeAttribute'].should == 1
39
+ end
40
+ end
41
+
42
+ after(:each) do
43
+ WebMock.disable!
44
+ end
45
+
46
+ 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.1.16
4
+ version: 1.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Algolia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-20 00:00:00.000000000 Z
11
+ date: 2014-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curb
@@ -96,6 +96,7 @@ files:
96
96
  - spec/client_spec.rb
97
97
  - spec/mock_spec.rb
98
98
  - spec/spec_helper.rb
99
+ - spec/stub_spec.rb
99
100
  homepage: http://github.com/algolia/algoliasearch-client-ruby
100
101
  licenses:
101
102
  - MIT