algoliasearch-rails 1.7.1 → 1.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +5 -0
- data/Gemfile.lock +21 -18
- data/README.md +10 -0
- data/VERSION +1 -1
- data/algoliasearch-rails.gemspec +1 -1
- data/lib/algoliasearch-rails.rb +44 -19
- data/vendor/assets/javascripts/algolia/algoliasearch.js +1130 -405
- data/vendor/assets/javascripts/algolia/algoliasearch.min.js +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9862c9e947777afdb827b6ca9c9879e989573ce3
|
4
|
+
data.tar.gz: bbfcbd83a12f2ecaf38ed346a80167b214303d30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce9950505a350f386a8a778213a2352c98a0fac56fbdef99b0ba899270ed939b4bc92202e72ce16ad214fb7cad2ecbd9b816a7b811c3136ce12b263a9373a116
|
7
|
+
data.tar.gz: c1d747eedcec3a4fb90d9394e6f1476c72f22142e1dd2551c6661219c5a765f0472cf2c7ee583a115f0da47061eabfca4865ceb1b4be2ac5d4f9533025dfaf24
|
data/ChangeLog
CHANGED
data/Gemfile.lock
CHANGED
@@ -17,10 +17,10 @@ GEM
|
|
17
17
|
activesupport (= 4.0.2)
|
18
18
|
arel (~> 4.0.0)
|
19
19
|
activerecord-deprecated_finders (1.0.3)
|
20
|
-
activerecord-jdbc-adapter (1.3.
|
20
|
+
activerecord-jdbc-adapter (1.3.6)
|
21
21
|
activerecord (>= 2.2)
|
22
|
-
activerecord-jdbcsqlite3-adapter (1.3.
|
23
|
-
activerecord-jdbc-adapter (~> 1.3.
|
22
|
+
activerecord-jdbcsqlite3-adapter (1.3.6)
|
23
|
+
activerecord-jdbc-adapter (~> 1.3.6)
|
24
24
|
jdbc-sqlite3 (~> 3.7.2)
|
25
25
|
activesupport (4.0.2)
|
26
26
|
i18n (~> 0.6, >= 0.6.4)
|
@@ -29,7 +29,7 @@ GEM
|
|
29
29
|
thread_safe (~> 0.1)
|
30
30
|
tzinfo (~> 0.3.37)
|
31
31
|
addressable (2.3.5)
|
32
|
-
algoliasearch (1.2.
|
32
|
+
algoliasearch (1.2.2)
|
33
33
|
httpclient (~> 2.3)
|
34
34
|
json (>= 1.5.1)
|
35
35
|
arel (4.0.1)
|
@@ -40,7 +40,7 @@ GEM
|
|
40
40
|
autotest-fsevent (0.2.9)
|
41
41
|
sys-uname
|
42
42
|
autotest-growl (0.2.16)
|
43
|
-
backports (3.
|
43
|
+
backports (3.5.0)
|
44
44
|
builder (3.1.4)
|
45
45
|
coderay (1.1.0)
|
46
46
|
diff-lcs (1.2.5)
|
@@ -48,14 +48,14 @@ GEM
|
|
48
48
|
ethon (0.6.2)
|
49
49
|
ffi (>= 1.3.0)
|
50
50
|
mime-types (~> 1.18)
|
51
|
-
faraday (0.8.
|
51
|
+
faraday (0.8.9)
|
52
52
|
multipart-post (~> 1.2.0)
|
53
53
|
faraday_middleware (0.9.0)
|
54
54
|
faraday (>= 0.7.4, < 0.9)
|
55
55
|
ffi (1.9.3)
|
56
56
|
ffi (1.9.3-java)
|
57
57
|
ffi2-generators (0.1.1)
|
58
|
-
gh (0.13.
|
58
|
+
gh (0.13.2)
|
59
59
|
addressable
|
60
60
|
backports
|
61
61
|
faraday (~> 0.8)
|
@@ -68,23 +68,26 @@ GEM
|
|
68
68
|
jdbc-sqlite3 (3.7.2.1)
|
69
69
|
json (1.8.1)
|
70
70
|
json (1.8.1-java)
|
71
|
-
kaminari (0.15.
|
71
|
+
kaminari (0.15.1)
|
72
72
|
actionpack (>= 3.0.0)
|
73
73
|
activesupport (>= 3.0.0)
|
74
74
|
launchy (2.4.2)
|
75
75
|
addressable (~> 2.3)
|
76
|
+
launchy (2.4.2-java)
|
77
|
+
addressable (~> 2.3)
|
78
|
+
spoon (~> 0.0.1)
|
76
79
|
method_source (0.8.2)
|
77
80
|
mime-types (1.25.1)
|
78
81
|
minitest (4.7.5)
|
79
|
-
multi_json (1.8.
|
82
|
+
multi_json (1.8.4)
|
80
83
|
multipart-post (1.2.0)
|
81
|
-
net-http-persistent (2.9)
|
84
|
+
net-http-persistent (2.9.1)
|
82
85
|
net-http-pipeline (1.0.1)
|
83
|
-
pry (0.9.12.
|
86
|
+
pry (0.9.12.6)
|
84
87
|
coderay (~> 1.0)
|
85
88
|
method_source (~> 0.8)
|
86
89
|
slop (~> 3.4)
|
87
|
-
pry (0.9.12.
|
90
|
+
pry (0.9.12.6-java)
|
88
91
|
coderay (~> 1.0)
|
89
92
|
method_source (~> 0.8)
|
90
93
|
slop (~> 3.4)
|
@@ -95,7 +98,7 @@ GEM
|
|
95
98
|
rack-test (0.6.2)
|
96
99
|
rack (>= 1.0)
|
97
100
|
rake (10.1.1)
|
98
|
-
rdoc (4.1.
|
101
|
+
rdoc (4.1.1)
|
99
102
|
json (~> 1.4)
|
100
103
|
redgreen (1.2.2)
|
101
104
|
rspec (2.14.1)
|
@@ -103,9 +106,9 @@ GEM
|
|
103
106
|
rspec-expectations (~> 2.14.0)
|
104
107
|
rspec-mocks (~> 2.14.0)
|
105
108
|
rspec-core (2.14.7)
|
106
|
-
rspec-expectations (2.14.
|
109
|
+
rspec-expectations (2.14.5)
|
107
110
|
diff-lcs (>= 1.1.3, < 2.0)
|
108
|
-
rspec-mocks (2.14.
|
111
|
+
rspec-mocks (2.14.5)
|
109
112
|
rubysl (2.0.15)
|
110
113
|
rubysl-abbrev (~> 2.0)
|
111
114
|
rubysl-base64 (~> 2.0)
|
@@ -264,7 +267,7 @@ GEM
|
|
264
267
|
rubysl-observer (2.0.0)
|
265
268
|
rubysl-open-uri (2.0.0)
|
266
269
|
rubysl-open3 (2.0.0)
|
267
|
-
rubysl-openssl (2.0
|
270
|
+
rubysl-openssl (2.1.0)
|
268
271
|
rubysl-optparse (2.0.1)
|
269
272
|
rubysl-shellwords (~> 2.0)
|
270
273
|
rubysl-ostruct (2.0.4)
|
@@ -279,7 +282,7 @@ GEM
|
|
279
282
|
rubysl-readline (2.0.2)
|
280
283
|
rubysl-resolv (2.0.0)
|
281
284
|
rubysl-rexml (2.0.2)
|
282
|
-
rubysl-rinda (2.0.
|
285
|
+
rubysl-rinda (2.0.1)
|
283
286
|
rubysl-rss (2.0.0)
|
284
287
|
rubysl-scanf (2.0.0)
|
285
288
|
rubysl-securerandom (2.0.0)
|
@@ -318,7 +321,7 @@ GEM
|
|
318
321
|
atomic
|
319
322
|
thread_safe (0.1.3-java)
|
320
323
|
atomic
|
321
|
-
travis (1.6.
|
324
|
+
travis (1.6.7)
|
322
325
|
addressable (~> 2.3)
|
323
326
|
backports
|
324
327
|
faraday (~> 0.8.7)
|
data/README.md
CHANGED
@@ -102,10 +102,20 @@ class Product < ActiveRecord::Base
|
|
102
102
|
end
|
103
103
|
```
|
104
104
|
|
105
|
+
A search returns ORM-compliant objects reloading them from your database.
|
106
|
+
|
105
107
|
```ruby
|
106
108
|
p Contact.search("jon doe")
|
107
109
|
```
|
108
110
|
|
111
|
+
If you want to retrieve the raw JSON answer from the API, without re-loading the objects from the database, you can use:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
p Contact.raw_search("jon doe")
|
115
|
+
```
|
116
|
+
|
117
|
+
By the way, we recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-js) to perform queries.
|
118
|
+
|
109
119
|
**Notes:** All methods injected by the ```AlgoliaSearch``` include are prefixed by ```algolia_``` and aliased to the associated short names if they aren't already defined.
|
110
120
|
|
111
121
|
```ruby
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.7.
|
1
|
+
1.7.2
|
data/algoliasearch-rails.gemspec
CHANGED
data/lib/algoliasearch-rails.rb
CHANGED
@@ -128,16 +128,19 @@ module AlgoliaSearch
|
|
128
128
|
alias_method :remove_from_index!, :algolia_remove_from_index! unless method_defined? :remove_from_index!
|
129
129
|
alias_method :clear_index!, :algolia_clear_index! unless method_defined? :clear_index!
|
130
130
|
alias_method :search, :algolia_search unless method_defined? :search
|
131
|
+
alias_method :raw_search, :algolia_raw_search unless method_defined? :raw_search
|
131
132
|
alias_method :index, :algolia_index unless method_defined? :index
|
132
133
|
alias_method :index_name, :algolia_index_name unless method_defined? :index_name
|
133
134
|
alias_method :must_reindex?, :algolia_must_reindex? unless method_defined? :must_reindex?
|
134
135
|
end
|
136
|
+
|
137
|
+
base.cattr_accessor :algolia_options, :algolia_settings, :algolia_index_settings
|
135
138
|
end
|
136
139
|
|
137
140
|
def algoliasearch(options = {}, &block)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
+
self.algolia_index_settings = IndexSettings.new(block_given? ? Proc.new : nil)
|
142
|
+
self.algolia_settings = algolia_index_settings.to_settings
|
143
|
+
self.algolia_options = { type: algolia_full_const_get(model_name.to_s), per_page: algolia_index_settings.get_setting(:hitsPerPage) || 10, page: 1 }.merge(options)
|
141
144
|
|
142
145
|
attr_accessor :highlight_result
|
143
146
|
|
@@ -167,9 +170,10 @@ module AlgoliaSearch
|
|
167
170
|
return if @algolia_without_auto_index_scope
|
168
171
|
algolia_ensure_init
|
169
172
|
last_task = nil
|
170
|
-
|
173
|
+
|
174
|
+
algolia_find_in_batches(batch_size) do |group|
|
171
175
|
group.select! { |o| algolia_indexable?(o) } if algolia_conditional_index?
|
172
|
-
objects = group.map { |o|
|
176
|
+
objects = group.map { |o| algolia_index_settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o) }
|
173
177
|
last_task = @algolia_index.save_objects(objects)
|
174
178
|
end
|
175
179
|
@algolia_index.wait_task(last_task["taskID"]) if last_task and synchronous == true
|
@@ -179,9 +183,9 @@ module AlgoliaSearch
|
|
179
183
|
return if @algolia_without_auto_index_scope || !algolia_indexable?(object)
|
180
184
|
algolia_ensure_init
|
181
185
|
if synchronous
|
182
|
-
@algolia_index.add_object!(
|
186
|
+
@algolia_index.add_object!(algolia_index_settings.get_attributes(object), algolia_object_id_of(object))
|
183
187
|
else
|
184
|
-
@algolia_index.add_object(
|
188
|
+
@algolia_index.add_object(algolia_index_settings.get_attributes(object), algolia_object_id_of(object))
|
185
189
|
end
|
186
190
|
end
|
187
191
|
|
@@ -201,17 +205,21 @@ module AlgoliaSearch
|
|
201
205
|
@algolia_index = nil
|
202
206
|
end
|
203
207
|
|
204
|
-
def
|
208
|
+
def algolia_raw_search(q, settings = {})
|
205
209
|
algolia_ensure_init
|
206
|
-
|
210
|
+
@algolia_index.search(q, Hash[settings.map { |k,v| [k.to_s, v.to_s] }])
|
211
|
+
end
|
212
|
+
|
213
|
+
def algolia_search(q, settings = {})
|
214
|
+
json = algolia_raw_search(q, settings)
|
207
215
|
results = json['hits'].map do |hit|
|
208
|
-
o =
|
216
|
+
o = algolia_options[:type].where(algolia_object_id_method => hit['objectID']).first
|
209
217
|
if o
|
210
218
|
o.highlight_result = hit['_highlightResult']
|
211
219
|
o
|
212
220
|
end
|
213
221
|
end.compact
|
214
|
-
AlgoliaSearch::Pagination.create(results, json['nbHits'].to_i,
|
222
|
+
AlgoliaSearch::Pagination.create(results, json['nbHits'].to_i, algolia_options)
|
215
223
|
end
|
216
224
|
|
217
225
|
def algolia_index
|
@@ -220,14 +228,14 @@ module AlgoliaSearch
|
|
220
228
|
end
|
221
229
|
|
222
230
|
def algolia_index_name
|
223
|
-
name =
|
224
|
-
name = "#{name}_#{Rails.env.to_s}" if
|
231
|
+
name = algolia_options[:index_name] || model_name.to_s.gsub('::', '_')
|
232
|
+
name = "#{name}_#{Rails.env.to_s}" if algolia_options[:per_environment]
|
225
233
|
name
|
226
234
|
end
|
227
235
|
|
228
236
|
def algolia_must_reindex?(object)
|
229
237
|
return true if algolia_object_id_changed?(object)
|
230
|
-
|
238
|
+
algolia_index_settings.get_attributes(object).each do |k, v|
|
231
239
|
changed_method = "#{k}_changed?"
|
232
240
|
return true if object.respond_to?(changed_method) && object.send(changed_method)
|
233
241
|
end
|
@@ -240,13 +248,13 @@ module AlgoliaSearch
|
|
240
248
|
return if @algolia_index
|
241
249
|
@algolia_index = Algolia::Index.new(algolia_index_name)
|
242
250
|
current_settings = @algolia_index.get_settings rescue nil # if the index doesn't exist
|
243
|
-
@algolia_index.set_settings(
|
251
|
+
@algolia_index.set_settings(algolia_settings) if algolia_index_settings_changed?(current_settings, algolia_settings)
|
244
252
|
end
|
245
253
|
|
246
254
|
private
|
247
255
|
|
248
256
|
def algolia_object_id_method
|
249
|
-
|
257
|
+
algolia_options[:id] || algolia_options[:object_id] || :id
|
250
258
|
end
|
251
259
|
|
252
260
|
def algolia_object_id_of(o)
|
@@ -285,12 +293,12 @@ module AlgoliaSearch
|
|
285
293
|
end
|
286
294
|
|
287
295
|
def algolia_conditional_index?
|
288
|
-
|
296
|
+
algolia_options[:if].present? || algolia_options[:unless].present?
|
289
297
|
end
|
290
298
|
|
291
299
|
def algolia_indexable?(object)
|
292
|
-
if_passes =
|
293
|
-
unless_passes =
|
300
|
+
if_passes = algolia_options[:if].blank? || algolia_constraint_passes?(object, algolia_options[:if])
|
301
|
+
unless_passes = algolia_options[:unless].blank? || !algolia_constraint_passes?(object, algolia_options[:unless])
|
294
302
|
if_passes && unless_passes
|
295
303
|
end
|
296
304
|
|
@@ -311,6 +319,23 @@ module AlgoliaSearch
|
|
311
319
|
end
|
312
320
|
end
|
313
321
|
end
|
322
|
+
|
323
|
+
def algolia_find_in_batches(batch_size, &block)
|
324
|
+
if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches)
|
325
|
+
find_in_batches(batch_size: batch_size, &block)
|
326
|
+
else
|
327
|
+
# don't worry, mongoid has its own underlying cursor/streaming mechanism
|
328
|
+
items = []
|
329
|
+
all.each do |item|
|
330
|
+
items << item
|
331
|
+
if items.length % batch_size == 0
|
332
|
+
yield items
|
333
|
+
items = []
|
334
|
+
end
|
335
|
+
yield items unless items.empty?
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
314
339
|
end
|
315
340
|
|
316
341
|
# these are the instance methods included
|
@@ -1,74 +1,216 @@
|
|
1
|
-
|
2
|
-
*
|
3
|
-
*
|
4
|
-
*
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2013 Algolia
|
3
|
+
* http://www.algolia.com/
|
4
|
+
*
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
10
|
+
* furnished to do so, subject to the following conditions:
|
11
|
+
*
|
12
|
+
* The above copyright notice and this permission notice shall be included in
|
13
|
+
* all copies or substantial portions of the Software.
|
14
|
+
*
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
* THE SOFTWARE.
|
5
22
|
*/
|
6
23
|
|
7
|
-
var
|
24
|
+
var ALGOLIA_VERSION = '2.3.8';
|
8
25
|
|
26
|
+
/*
|
27
|
+
* Copyright (c) 2013 Algolia
|
28
|
+
* http://www.algolia.com/
|
29
|
+
*
|
30
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
31
|
+
* of this software and associated documentation files (the "Software"), to deal
|
32
|
+
* in the Software without restriction, including without limitation the rights
|
33
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
34
|
+
* copies of the Software, and to permit persons to whom the Software is
|
35
|
+
* furnished to do so, subject to the following conditions:
|
36
|
+
*
|
37
|
+
* The above copyright notice and this permission notice shall be included in
|
38
|
+
* all copies or substantial portions of the Software.
|
39
|
+
*
|
40
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
41
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
42
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
43
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
44
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
45
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
46
|
+
* THE SOFTWARE.
|
47
|
+
*/
|
48
|
+
|
49
|
+
/*
|
50
|
+
* Algolia Search library initialization
|
51
|
+
* @param applicationID the application ID you have in your admin interface
|
52
|
+
* @param apiKey a valid API key for the service
|
53
|
+
* @param method specify if the protocol used is http or https (http by default to make the first search query faster).
|
54
|
+
* You need to use https is you are doing something else than just search queries.
|
55
|
+
* @param resolveDNS let you disable first empty query that is launch to warmup the service
|
56
|
+
* @param hostsArray (optionnal) the list of hosts that you have received for the service
|
57
|
+
*/
|
9
58
|
var AlgoliaSearch = function(applicationID, apiKey, method, resolveDNS, hostsArray) {
|
10
59
|
this.applicationID = applicationID;
|
11
60
|
this.apiKey = apiKey;
|
61
|
+
|
12
62
|
if (this._isUndefined(hostsArray)) {
|
13
|
-
hostsArray = [
|
63
|
+
hostsArray = [applicationID + '-1.algolia.io',
|
64
|
+
applicationID + '-2.algolia.io',
|
65
|
+
applicationID + '-3.algolia.io'];
|
14
66
|
}
|
15
67
|
this.hosts = [];
|
68
|
+
// Add hosts in random order
|
16
69
|
for (var i = 0; i < hostsArray.length; ++i) {
|
17
|
-
if (Math.random() > .5) {
|
70
|
+
if (Math.random() > 0.5) {
|
18
71
|
this.hosts.reverse();
|
19
72
|
}
|
20
73
|
if (this._isUndefined(method) || method == null) {
|
21
|
-
this.hosts.push((
|
22
|
-
} else if (method ===
|
23
|
-
this.hosts.push(
|
74
|
+
this.hosts.push(('https:' == document.location.protocol ? 'https' : 'http') + '://' + hostsArray[i]);
|
75
|
+
} else if (method === 'https' || method === 'HTTPS') {
|
76
|
+
this.hosts.push('https://' + hostsArray[i]);
|
24
77
|
} else {
|
25
|
-
this.hosts.push(
|
78
|
+
this.hosts.push('http://' + hostsArray[i]);
|
26
79
|
}
|
27
80
|
}
|
28
|
-
if (Math.random() > .5) {
|
81
|
+
if (Math.random() > 0.5) {
|
29
82
|
this.hosts.reverse();
|
30
83
|
}
|
31
84
|
if (this._isUndefined(resolveDNS) || resolveDNS) {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
});
|
85
|
+
// Perform a call to solve DNS (avoid to slow down the first user query)
|
86
|
+
this._jsonRequest({ method: 'GET',
|
87
|
+
url: '/1/isalive' });
|
36
88
|
}
|
37
89
|
this.extraHeaders = [];
|
38
90
|
};
|
39
91
|
|
92
|
+
function AlgoliaExplainResults(hit, titleAttribute, otherAttributes) {
|
93
|
+
|
94
|
+
function _getHitAxplainationForOneAttr_recurse(obj, foundWords) {
|
95
|
+
if (typeof obj === 'object' && 'matchedWords' in obj && 'value' in obj) {
|
96
|
+
var match = false;
|
97
|
+
for (var j = 0; j < obj.matchedWords.length; ++j) {
|
98
|
+
var word = obj.matchedWords[j];
|
99
|
+
if (!(word in foundWords)) {
|
100
|
+
foundWords[word] = 1;
|
101
|
+
match = true;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
return match ? [obj.value] : [];
|
105
|
+
} else if (obj instanceof Array) {
|
106
|
+
var res = [];
|
107
|
+
for (var i = 0; i < obj.length; ++i) {
|
108
|
+
var array = _getHitAxplainationForOneAttr_recurse(obj[i], foundWords);
|
109
|
+
res = res.concat(array);
|
110
|
+
}
|
111
|
+
return res;
|
112
|
+
} else if (typeof obj === 'object') {
|
113
|
+
var res = [];
|
114
|
+
for (prop in obj) {
|
115
|
+
if (obj.hasOwnProperty(prop)){
|
116
|
+
res = res.concat(_getHitAxplainationForOneAttr_recurse(obj[prop], foundWords));
|
117
|
+
}
|
118
|
+
}
|
119
|
+
return res;
|
120
|
+
}
|
121
|
+
return [];
|
122
|
+
}
|
123
|
+
|
124
|
+
function _getHitAxplainationForOneAttr(hit, foundWords, attr) {
|
125
|
+
if (attr.indexOf('.') === -1) {
|
126
|
+
if (attr in hit._highlightResult) {
|
127
|
+
return _getHitAxplainationForOneAttr_recurse(hit._highlightResult[attr], foundWords);
|
128
|
+
}
|
129
|
+
return [];
|
130
|
+
}
|
131
|
+
var array = attr.split('.');
|
132
|
+
var obj = hit._highlightResult;
|
133
|
+
for (var i = 0; i < array.length; ++i) {
|
134
|
+
if (array[i] in obj) {
|
135
|
+
obj = obj[array[i]];
|
136
|
+
} else {
|
137
|
+
return [];
|
138
|
+
}
|
139
|
+
}
|
140
|
+
return _getHitAxplainationForOneAttr_recurse(obj, foundWords);
|
141
|
+
}
|
142
|
+
|
143
|
+
var res = {};
|
144
|
+
var foundWords = {};
|
145
|
+
var title = _getHitAxplainationForOneAttr(hit, foundWords, titleAttribute);
|
146
|
+
res.title = (title.length > 0) ? title[0] : "";
|
147
|
+
res.subtitles = [];
|
148
|
+
|
149
|
+
if (typeof otherAttributes !== 'undefined') {
|
150
|
+
for (var i = 0; i < otherAttributes.length; ++i) {
|
151
|
+
var attr = _getHitAxplainationForOneAttr(hit, foundWords, otherAttributes[i]);
|
152
|
+
for (var j = 0; j < attr.length; ++j) {
|
153
|
+
res.subtitles.push({ attr: otherAttributes[i], value: attr[j] });
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
157
|
+
return res;
|
158
|
+
}
|
159
|
+
|
160
|
+
|
40
161
|
AlgoliaSearch.prototype = {
|
162
|
+
/*
|
163
|
+
* Delete an index
|
164
|
+
*
|
165
|
+
* @param indexName the name of index to delete
|
166
|
+
* @param callback the result callback with two arguments
|
167
|
+
* success: boolean set to true if the request was successfull
|
168
|
+
* content: the server answer that contains the task ID
|
169
|
+
*/
|
41
170
|
deleteIndex: function(indexName, callback) {
|
42
|
-
this._jsonRequest({
|
43
|
-
|
44
|
-
|
45
|
-
callback: callback
|
46
|
-
});
|
171
|
+
this._jsonRequest({ method: 'DELETE',
|
172
|
+
url: '/1/indexes/' + encodeURIComponent(indexName),
|
173
|
+
callback: callback });
|
47
174
|
},
|
175
|
+
/**
|
176
|
+
* Move an existing index.
|
177
|
+
* @param srcIndexName the name of index to copy.
|
178
|
+
* @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
|
179
|
+
* @param callback the result callback with two arguments
|
180
|
+
* success: boolean set to true if the request was successfull
|
181
|
+
* content: the server answer that contains the task ID
|
182
|
+
*/
|
48
183
|
moveIndex: function(srcIndexName, dstIndexName, callback) {
|
49
|
-
var postObj = {
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
url: "/1/indexes/" + encodeURIComponent(srcIndexName) + "/operation",
|
56
|
-
body: postObj,
|
57
|
-
callback: callback
|
58
|
-
});
|
184
|
+
var postObj = {operation: 'move', destination: dstIndexName};
|
185
|
+
this._jsonRequest({ method: 'POST',
|
186
|
+
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
|
187
|
+
body: postObj,
|
188
|
+
callback: callback });
|
189
|
+
|
59
190
|
},
|
191
|
+
/**
|
192
|
+
* Copy an existing index.
|
193
|
+
* @param srcIndexName the name of index to copy.
|
194
|
+
* @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
|
195
|
+
* @param callback the result callback with two arguments
|
196
|
+
* success: boolean set to true if the request was successfull
|
197
|
+
* content: the server answer that contains the task ID
|
198
|
+
*/
|
60
199
|
copyIndex: function(srcIndexName, dstIndexName, callback) {
|
61
|
-
var postObj = {
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
method: "POST",
|
67
|
-
url: "/1/indexes/" + encodeURIComponent(srcIndexName) + "/operation",
|
68
|
-
body: postObj,
|
69
|
-
callback: callback
|
70
|
-
});
|
200
|
+
var postObj = {operation: 'copy', destination: dstIndexName};
|
201
|
+
this._jsonRequest({ method: 'POST',
|
202
|
+
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
|
203
|
+
body: postObj,
|
204
|
+
callback: callback });
|
71
205
|
},
|
206
|
+
/**
|
207
|
+
* Return last log entries.
|
208
|
+
* @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
|
209
|
+
* @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
|
210
|
+
* @param callback the result callback with two arguments
|
211
|
+
* success: boolean set to true if the request was successfull
|
212
|
+
* content: the server answer that contains the task ID
|
213
|
+
*/
|
72
214
|
getLogs: function(callback, offset, length) {
|
73
215
|
if (this._isUndefined(offset)) {
|
74
216
|
offset = 0;
|
@@ -76,53 +218,110 @@ AlgoliaSearch.prototype = {
|
|
76
218
|
if (this._isUndefined(length)) {
|
77
219
|
length = 10;
|
78
220
|
}
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
});
|
221
|
+
|
222
|
+
this._jsonRequest({ method: 'GET',
|
223
|
+
url: '/1/logs?offset=' + offset + '&length=' + length,
|
224
|
+
callback: callback });
|
84
225
|
},
|
226
|
+
/*
|
227
|
+
* List all existing indexes
|
228
|
+
*
|
229
|
+
* @param callback the result callback with two arguments
|
230
|
+
* success: boolean set to true if the request was successfull
|
231
|
+
* content: the server answer with index list or error description if success is false.
|
232
|
+
*/
|
85
233
|
listIndexes: function(callback) {
|
86
|
-
this._jsonRequest({
|
87
|
-
|
88
|
-
|
89
|
-
callback: callback
|
90
|
-
});
|
234
|
+
this._jsonRequest({ method: 'GET',
|
235
|
+
url: '/1/indexes/',
|
236
|
+
callback: callback });
|
91
237
|
},
|
238
|
+
|
239
|
+
/*
|
240
|
+
* Get the index object initialized
|
241
|
+
*
|
242
|
+
* @param indexName the name of index
|
243
|
+
* @param callback the result callback with one argument (the Index instance)
|
244
|
+
*/
|
92
245
|
initIndex: function(indexName) {
|
93
246
|
return new this.Index(this, indexName);
|
94
247
|
},
|
248
|
+
/*
|
249
|
+
* List all existing user keys with their associated ACLs
|
250
|
+
*
|
251
|
+
* @param callback the result callback with two arguments
|
252
|
+
* success: boolean set to true if the request was successfull
|
253
|
+
* content: the server answer with user keys list or error description if success is false.
|
254
|
+
*/
|
95
255
|
listUserKeys: function(callback) {
|
96
|
-
this._jsonRequest({
|
97
|
-
|
98
|
-
|
99
|
-
callback: callback
|
100
|
-
});
|
256
|
+
this._jsonRequest({ method: 'GET',
|
257
|
+
url: '/1/keys',
|
258
|
+
callback: callback });
|
101
259
|
},
|
260
|
+
/*
|
261
|
+
* Get ACL of a user key
|
262
|
+
*
|
263
|
+
* @param callback the result callback with two arguments
|
264
|
+
* success: boolean set to true if the request was successfull
|
265
|
+
* content: the server answer with user keys list or error description if success is false.
|
266
|
+
*/
|
102
267
|
getUserKeyACL: function(key, callback) {
|
103
|
-
this._jsonRequest({
|
104
|
-
|
105
|
-
|
106
|
-
callback: callback
|
107
|
-
});
|
268
|
+
this._jsonRequest({ method: 'GET',
|
269
|
+
url: '/1/keys/' + key,
|
270
|
+
callback: callback });
|
108
271
|
},
|
272
|
+
/*
|
273
|
+
* Delete an existing user key
|
274
|
+
*
|
275
|
+
* @param callback the result callback with two arguments
|
276
|
+
* success: boolean set to true if the request was successfull
|
277
|
+
* content: the server answer with user keys list or error description if success is false.
|
278
|
+
*/
|
109
279
|
deleteUserKey: function(key, callback) {
|
110
|
-
this._jsonRequest({
|
111
|
-
|
112
|
-
|
113
|
-
callback: callback
|
114
|
-
});
|
280
|
+
this._jsonRequest({ method: 'DELETE',
|
281
|
+
url: '/1/keys/' + key,
|
282
|
+
callback: callback });
|
115
283
|
},
|
284
|
+
/*
|
285
|
+
* Add an existing user key
|
286
|
+
*
|
287
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
288
|
+
* can contains the following values:
|
289
|
+
* - search: allow to search (https and http)
|
290
|
+
* - addObject: allows to add/update an object in the index (https only)
|
291
|
+
* - deleteObject : allows to delete an existing object (https only)
|
292
|
+
* - deleteIndex : allows to delete index content (https only)
|
293
|
+
* - settings : allows to get index settings (https only)
|
294
|
+
* - editSettings : allows to change index settings (https only)
|
295
|
+
* @param callback the result callback with two arguments
|
296
|
+
* success: boolean set to true if the request was successfull
|
297
|
+
* content: the server answer with user keys list or error description if success is false.
|
298
|
+
*/
|
116
299
|
addUserKey: function(acls, callback) {
|
117
300
|
var aclsObject = {};
|
118
301
|
aclsObject.acl = acls;
|
119
|
-
this._jsonRequest({
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
callback: callback
|
124
|
-
});
|
302
|
+
this._jsonRequest({ method: 'POST',
|
303
|
+
url: '/1/keys',
|
304
|
+
body: aclsObject,
|
305
|
+
callback: callback });
|
125
306
|
},
|
307
|
+
/*
|
308
|
+
* Add an existing user key
|
309
|
+
*
|
310
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
311
|
+
* can contains the following values:
|
312
|
+
* - search: allow to search (https and http)
|
313
|
+
* - addObject: allows to add/update an object in the index (https only)
|
314
|
+
* - deleteObject : allows to delete an existing object (https only)
|
315
|
+
* - deleteIndex : allows to delete index content (https only)
|
316
|
+
* - settings : allows to get index settings (https only)
|
317
|
+
* - editSettings : allows to change index settings (https only)
|
318
|
+
* @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
|
319
|
+
* @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
|
320
|
+
* @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
|
321
|
+
* @param callback the result callback with two arguments
|
322
|
+
* success: boolean set to true if the request was successfull
|
323
|
+
* content: the server answer with user keys list or error description if success is false.
|
324
|
+
*/
|
126
325
|
addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
|
127
326
|
var indexObj = this;
|
128
327
|
var aclsObject = {};
|
@@ -130,42 +329,64 @@ AlgoliaSearch.prototype = {
|
|
130
329
|
aclsObject.validity = validity;
|
131
330
|
aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
|
132
331
|
aclsObject.maxHitsPerQuery = maxHitsPerQuery;
|
133
|
-
this._jsonRequest({
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
callback: callback
|
138
|
-
});
|
332
|
+
this._jsonRequest({ method: 'POST',
|
333
|
+
url: '/1/indexes/' + indexObj.indexName + '/keys',
|
334
|
+
body: aclsObject,
|
335
|
+
callback: callback });
|
139
336
|
},
|
337
|
+
/*
|
338
|
+
* Initialize a new batch of search queries
|
339
|
+
*/
|
140
340
|
startQueriesBatch: function() {
|
141
341
|
this.batch = [];
|
142
342
|
},
|
343
|
+
/*
|
344
|
+
* Add a search query in the batch
|
345
|
+
*
|
346
|
+
* @param query the full text query
|
347
|
+
* @param args (optional) if set, contains an object with query parameters:
|
348
|
+
* - attributes: an array of object attribute names to retrieve
|
349
|
+
* (if not set all attributes are retrieve)
|
350
|
+
* - attributesToHighlight: an array of object attribute names to highlight
|
351
|
+
* (if not set indexed attributes are highlighted)
|
352
|
+
* - minWordSizefor1Typo: the minimum number of characters to accept one typo.
|
353
|
+
* Defaults to 3.
|
354
|
+
* - minWordSizefor2Typos: the minimum number of characters to accept two typos.
|
355
|
+
* Defaults to 7.
|
356
|
+
* - getRankingInfo: if set, the result hits will contain ranking information in
|
357
|
+
* _rankingInfo attribute
|
358
|
+
* - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
|
359
|
+
* - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
|
360
|
+
*/
|
143
361
|
addQueryInBatch: function(indexName, query, args) {
|
144
|
-
var params =
|
362
|
+
var params = 'query=' + encodeURIComponent(query);
|
145
363
|
if (!this._isUndefined(args) && args != null) {
|
146
364
|
params = this._getSearchParams(args, params);
|
147
365
|
}
|
148
|
-
this.batch.push({
|
149
|
-
indexName: indexName,
|
150
|
-
params: params
|
151
|
-
});
|
366
|
+
this.batch.push({ indexName: indexName, params: params });
|
152
367
|
},
|
368
|
+
/*
|
369
|
+
* Clear all queries in cache
|
370
|
+
*/
|
153
371
|
clearCache: function() {
|
154
372
|
this.cache = {};
|
155
373
|
},
|
374
|
+
/*
|
375
|
+
* Launch the batch of queries using XMLHttpRequest.
|
376
|
+
* (Optimized for browser using a POST query to minimize number of OPTIONS queries)
|
377
|
+
*
|
378
|
+
* @param callback the function that will receive results
|
379
|
+
* @param delay (optional) if set, wait for this delay (in ms) and only send the batch if there was no other in the meantime.
|
380
|
+
*/
|
156
381
|
sendQueriesBatch: function(callback, delay) {
|
157
382
|
var as = this;
|
158
|
-
var params = {
|
159
|
-
requests: [],
|
160
|
-
apiKey: this.apiKey,
|
161
|
-
appID: this.applicationID
|
162
|
-
};
|
383
|
+
var params = {requests: [], apiKey: this.apiKey, appID: this.applicationID};
|
163
384
|
for (var i = 0; i < as.batch.length; ++i) {
|
164
385
|
params.requests.push(as.batch[i]);
|
165
386
|
}
|
166
387
|
window.clearTimeout(as.onDelayTrigger);
|
167
388
|
if (!this._isUndefined(delay) && delay != null && delay > 0) {
|
168
|
-
var onDelayTrigger = window.setTimeout(function() {
|
389
|
+
var onDelayTrigger = window.setTimeout( function() {
|
169
390
|
as._sendQueriesBatch(params, callback);
|
170
391
|
}, delay);
|
171
392
|
as.onDelayTrigger = onDelayTrigger;
|
@@ -173,34 +394,38 @@ AlgoliaSearch.prototype = {
|
|
173
394
|
this._sendQueriesBatch(params, callback);
|
174
395
|
}
|
175
396
|
},
|
397
|
+
/*
|
398
|
+
* Index class constructor.
|
399
|
+
* You should not use this method directly but use initIndex() function
|
400
|
+
*/
|
176
401
|
Index: function(algoliasearch, indexName) {
|
177
402
|
this.indexName = indexName;
|
178
403
|
this.as = algoliasearch;
|
179
404
|
this.typeAheadArgs = null;
|
180
405
|
this.typeAheadValueOption = null;
|
181
406
|
},
|
407
|
+
|
182
408
|
setExtraHeader: function(key, value) {
|
183
|
-
this.extraHeaders.push({
|
184
|
-
key: key,
|
185
|
-
value: value
|
186
|
-
});
|
409
|
+
this.extraHeaders.push({ key: key, value: value});
|
187
410
|
},
|
411
|
+
|
188
412
|
_sendQueriesBatch: function(params, callback) {
|
189
|
-
this._jsonRequest({
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
callback: callback
|
195
|
-
});
|
413
|
+
this._jsonRequest({ cache: this.cache,
|
414
|
+
method: 'POST',
|
415
|
+
url: '/1/indexes/*/queries',
|
416
|
+
body: params,
|
417
|
+
callback: callback });
|
196
418
|
},
|
419
|
+
/*
|
420
|
+
* Wrapper that try all hosts to maximize the quality of service
|
421
|
+
*/
|
197
422
|
_jsonRequest: function(opts) {
|
198
423
|
var self = this;
|
199
424
|
var callback = opts.callback;
|
200
425
|
var cache = null;
|
201
426
|
var cacheID = opts.url;
|
202
427
|
if (!this._isUndefined(opts.body)) {
|
203
|
-
cacheID = opts.url +
|
428
|
+
cacheID = opts.url + '_body_' + JSON.stringify(opts.body);
|
204
429
|
}
|
205
430
|
if (!this._isUndefined(opts.cache)) {
|
206
431
|
cache = opts.cache;
|
@@ -211,6 +436,7 @@ AlgoliaSearch.prototype = {
|
|
211
436
|
return;
|
212
437
|
}
|
213
438
|
}
|
439
|
+
|
214
440
|
var impl = function(position) {
|
215
441
|
var idx = 0;
|
216
442
|
if (!self._isUndefined(position)) {
|
@@ -218,20 +444,18 @@ AlgoliaSearch.prototype = {
|
|
218
444
|
}
|
219
445
|
if (self.hosts.length <= idx) {
|
220
446
|
if (!self._isUndefined(callback)) {
|
221
|
-
callback(false, {
|
222
|
-
message: "Cannot contact server"
|
223
|
-
});
|
447
|
+
callback(false, { message: 'Cannot contact server'});
|
224
448
|
}
|
225
449
|
return;
|
226
450
|
}
|
227
451
|
opts.callback = function(retry, success, res, body) {
|
228
452
|
if (!success && !self._isUndefined(body)) {
|
229
|
-
console.log(
|
453
|
+
console.log('Error: ' + body.message);
|
230
454
|
}
|
231
455
|
if (success && !self._isUndefined(opts.cache)) {
|
232
456
|
cache[cacheID] = body;
|
233
457
|
}
|
234
|
-
if (!success && retry && idx + 1 < self.hosts.length) {
|
458
|
+
if (!success && retry && (idx + 1) < self.hosts.length) {
|
235
459
|
impl(idx + 1);
|
236
460
|
} else {
|
237
461
|
if (!self._isUndefined(callback)) {
|
@@ -244,6 +468,7 @@ AlgoliaSearch.prototype = {
|
|
244
468
|
};
|
245
469
|
impl();
|
246
470
|
},
|
471
|
+
|
247
472
|
_jsonRequestByHost: function(opts) {
|
248
473
|
var body = null;
|
249
474
|
var self = this;
|
@@ -252,47 +477,53 @@ AlgoliaSearch.prototype = {
|
|
252
477
|
}
|
253
478
|
var url = opts.hostname + opts.url;
|
254
479
|
var xmlHttp = null;
|
480
|
+
|
255
481
|
xmlHttp = new XMLHttpRequest();
|
256
|
-
if (
|
257
|
-
xmlHttp.open(opts.method, url, true);
|
258
|
-
xmlHttp.setRequestHeader(
|
259
|
-
xmlHttp.setRequestHeader(
|
482
|
+
if ('withCredentials' in xmlHttp) {
|
483
|
+
xmlHttp.open(opts.method, url , true);
|
484
|
+
xmlHttp.setRequestHeader('X-Algolia-API-Key', this.apiKey);
|
485
|
+
xmlHttp.setRequestHeader('X-Algolia-Application-Id', this.applicationID);
|
260
486
|
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
261
487
|
xmlHttp.setRequestHeader(this.extraHeaders[i].key, this.extraHeaders[i].value);
|
262
488
|
}
|
263
489
|
if (body != null) {
|
264
|
-
xmlHttp.setRequestHeader(
|
490
|
+
xmlHttp.setRequestHeader('Content-type', 'application/json');
|
265
491
|
}
|
266
|
-
} else if (typeof XDomainRequest !=
|
492
|
+
} else if (typeof XDomainRequest != 'undefined') {
|
493
|
+
// Handle IE8/IE9
|
494
|
+
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
|
267
495
|
xmlHttp = new XDomainRequest();
|
268
496
|
xmlHttp.open(opts.method, url);
|
269
497
|
} else {
|
270
|
-
|
498
|
+
// very old browser, not supported
|
499
|
+
console.log('your browser is too old to support CORS requests');
|
271
500
|
}
|
272
501
|
xmlHttp.send(body);
|
273
502
|
xmlHttp.onload = function(event) {
|
274
503
|
if (!self._isUndefined(event) && event.target != null) {
|
275
|
-
var retry = event.target.status === 0 || event.target.status === 503;
|
276
|
-
var success = event.target.status === 200 || event.target.status === 201;
|
504
|
+
var retry = (event.target.status === 0 || event.target.status === 503);
|
505
|
+
var success = (event.target.status === 200 || event.target.status === 201);
|
277
506
|
opts.callback(retry, success, event.target, event.target.response != null ? JSON.parse(event.target.response) : null);
|
278
507
|
} else {
|
279
508
|
opts.callback(false, true, event, JSON.parse(xmlHttp.responseText));
|
280
509
|
}
|
281
510
|
};
|
282
511
|
xmlHttp.onerror = function() {
|
283
|
-
opts.callback(true, false, null, {
|
284
|
-
message: "Could not connect to Host"
|
285
|
-
});
|
512
|
+
opts.callback(true, false, null, { 'message': 'Could not connect to Host'} );
|
286
513
|
};
|
287
514
|
},
|
515
|
+
|
516
|
+
/*
|
517
|
+
* Transform search param object in query string
|
518
|
+
*/
|
288
519
|
_getSearchParams: function(args, params) {
|
289
520
|
if (this._isUndefined(args) || args == null) {
|
290
521
|
return params;
|
291
522
|
}
|
292
523
|
for (var key in args) {
|
293
524
|
if (key != null && args.hasOwnProperty(key)) {
|
294
|
-
params += params.length === 0 ?
|
295
|
-
params += key +
|
525
|
+
params += (params.length === 0) ? '?' : '&';
|
526
|
+
params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? JSON.stringify(args[key]) : args[key]);
|
296
527
|
}
|
297
528
|
}
|
298
529
|
return params;
|
@@ -300,6 +531,8 @@ AlgoliaSearch.prototype = {
|
|
300
531
|
_isUndefined: function(obj) {
|
301
532
|
return obj === void 0;
|
302
533
|
},
|
534
|
+
|
535
|
+
/// internal attributes
|
303
536
|
applicationID: null,
|
304
537
|
apiKey: null,
|
305
538
|
hosts: [],
|
@@ -307,311 +540,803 @@ AlgoliaSearch.prototype = {
|
|
307
540
|
extraHeaders: []
|
308
541
|
};
|
309
542
|
|
543
|
+
/*
|
544
|
+
* Contains all the functions related to one index
|
545
|
+
* You should use AlgoliaSearch.initIndex(indexName) to retrieve this object
|
546
|
+
*/
|
310
547
|
AlgoliaSearch.prototype.Index.prototype = {
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
548
|
+
/*
|
549
|
+
* Clear all queries in cache
|
550
|
+
*/
|
551
|
+
clearCache: function() {
|
552
|
+
this.cache = {};
|
553
|
+
},
|
554
|
+
/*
|
555
|
+
* Add an object in this index
|
556
|
+
*
|
557
|
+
* @param content contains the javascript object to add inside the index
|
558
|
+
* @param callback (optional) the result callback with two arguments:
|
559
|
+
* success: boolean set to true if the request was successfull
|
560
|
+
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
561
|
+
* @param objectID (optional) an objectID you want to attribute to this object
|
562
|
+
* (if the attribute already exist the old object will be overwrite)
|
563
|
+
*/
|
564
|
+
addObject: function(content, callback, objectID) {
|
565
|
+
var indexObj = this;
|
566
|
+
if (this.as._isUndefined(objectID)) {
|
567
|
+
this.as._jsonRequest({ method: 'POST',
|
568
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName),
|
569
|
+
body: content,
|
570
|
+
callback: callback });
|
571
|
+
} else {
|
572
|
+
this.as._jsonRequest({ method: 'PUT',
|
573
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
|
574
|
+
body: content,
|
575
|
+
callback: callback });
|
576
|
+
}
|
577
|
+
|
578
|
+
},
|
579
|
+
/*
|
580
|
+
* Add several objects
|
581
|
+
*
|
582
|
+
* @param objects contains an array of objects to add
|
583
|
+
* @param callback (optional) the result callback with two arguments:
|
584
|
+
* success: boolean set to true if the request was successfull
|
585
|
+
* content: the server answer that updateAt and taskID
|
586
|
+
*/
|
587
|
+
addObjects: function(objects, callback) {
|
588
|
+
var indexObj = this;
|
589
|
+
var postObj = {requests:[]};
|
590
|
+
for (var i = 0; i < objects.length; ++i) {
|
591
|
+
var request = { action: 'addObject',
|
592
|
+
body: objects[i] };
|
593
|
+
postObj.requests.push(request);
|
594
|
+
}
|
595
|
+
this.as._jsonRequest({ method: 'POST',
|
596
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
597
|
+
body: postObj,
|
598
|
+
callback: callback });
|
599
|
+
},
|
600
|
+
/*
|
601
|
+
* Get an object from this index
|
602
|
+
*
|
603
|
+
* @param objectID the unique identifier of the object to retrieve
|
604
|
+
* @param callback (optional) the result callback with two arguments
|
605
|
+
* success: boolean set to true if the request was successfull
|
606
|
+
* content: the object to retrieve or the error message if a failure occured
|
607
|
+
* @param attributes (optional) if set, contains the array of attribute names to retrieve
|
608
|
+
*/
|
609
|
+
getObject: function(objectID, callback, attributes) {
|
610
|
+
var indexObj = this;
|
611
|
+
var params = '';
|
612
|
+
if (!this.as._isUndefined(attributes)) {
|
613
|
+
params = '?attributes=';
|
614
|
+
for (var i = 0; i < attributes.length; ++i) {
|
615
|
+
if (i !== 0) {
|
616
|
+
params += ',';
|
617
|
+
}
|
618
|
+
params += attributes[i];
|
359
619
|
}
|
360
|
-
params += attributes[i];
|
361
620
|
}
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
}
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
}
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
621
|
+
this.as._jsonRequest({ method: 'GET',
|
622
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
|
623
|
+
callback: callback });
|
624
|
+
},
|
625
|
+
|
626
|
+
/*
|
627
|
+
* Update partially an object (only update attributes passed in argument)
|
628
|
+
*
|
629
|
+
* @param partialObject contains the javascript attributes to override, the
|
630
|
+
* object must contains an objectID attribute
|
631
|
+
* @param callback (optional) the result callback with two arguments:
|
632
|
+
* success: boolean set to true if the request was successfull
|
633
|
+
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
634
|
+
*/
|
635
|
+
partialUpdateObject: function(partialObject, callback) {
|
636
|
+
var indexObj = this;
|
637
|
+
this.as._jsonRequest({ method: 'POST',
|
638
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
|
639
|
+
body: partialObject,
|
640
|
+
callback: callback });
|
641
|
+
},
|
642
|
+
/*
|
643
|
+
* Partially Override the content of several objects
|
644
|
+
*
|
645
|
+
* @param objects contains an array of objects to update (each object must contains a objectID attribute)
|
646
|
+
* @param callback (optional) the result callback with two arguments:
|
647
|
+
* success: boolean set to true if the request was successfull
|
648
|
+
* content: the server answer that updateAt and taskID
|
649
|
+
*/
|
650
|
+
partialUpdateObjects: function(objects, callback) {
|
651
|
+
var indexObj = this;
|
652
|
+
var postObj = {requests:[]};
|
653
|
+
for (var i = 0; i < objects.length; ++i) {
|
654
|
+
var request = { action: 'partialUpdateObject',
|
655
|
+
objectID: objects[i].objectID,
|
656
|
+
body: objects[i] };
|
657
|
+
postObj.requests.push(request);
|
658
|
+
}
|
659
|
+
this.as._jsonRequest({ method: 'POST',
|
660
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
661
|
+
body: postObj,
|
662
|
+
callback: callback });
|
663
|
+
},
|
664
|
+
/*
|
665
|
+
* Override the content of object
|
666
|
+
*
|
667
|
+
* @param object contains the javascript object to save, the object must contains an objectID attribute
|
668
|
+
* @param callback (optional) the result callback with two arguments:
|
669
|
+
* success: boolean set to true if the request was successfull
|
670
|
+
* content: the server answer that updateAt and taskID
|
671
|
+
*/
|
672
|
+
saveObject: function(object, callback) {
|
673
|
+
var indexObj = this;
|
674
|
+
this.as._jsonRequest({ method: 'PUT',
|
675
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
|
676
|
+
body: object,
|
677
|
+
callback: callback });
|
678
|
+
},
|
679
|
+
/*
|
680
|
+
* Override the content of several objects
|
681
|
+
*
|
682
|
+
* @param objects contains an array of objects to update (each object must contains a objectID attribute)
|
683
|
+
* @param callback (optional) the result callback with two arguments:
|
684
|
+
* success: boolean set to true if the request was successfull
|
685
|
+
* content: the server answer that updateAt and taskID
|
686
|
+
*/
|
687
|
+
saveObjects: function(objects, callback) {
|
688
|
+
var indexObj = this;
|
689
|
+
var postObj = {requests:[]};
|
690
|
+
for (var i = 0; i < objects.length; ++i) {
|
691
|
+
var request = { action: 'updateObject',
|
692
|
+
objectID: objects[i].objectID,
|
693
|
+
body: objects[i] };
|
694
|
+
postObj.requests.push(request);
|
695
|
+
}
|
696
|
+
this.as._jsonRequest({ method: 'POST',
|
697
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
698
|
+
body: postObj,
|
699
|
+
callback: callback });
|
700
|
+
},
|
701
|
+
/*
|
702
|
+
* Delete an object from the index
|
703
|
+
*
|
704
|
+
* @param objectID the unique identifier of object to delete
|
705
|
+
* @param callback (optional) the result callback with two arguments:
|
706
|
+
* success: boolean set to true if the request was successfull
|
707
|
+
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
708
|
+
*/
|
709
|
+
deleteObject: function(objectID, callback) {
|
710
|
+
if (objectID == null || objectID.length === 0) {
|
711
|
+
callback(false, { message: 'empty objectID'});
|
712
|
+
return;
|
713
|
+
}
|
714
|
+
var indexObj = this;
|
715
|
+
this.as._jsonRequest({ method: 'DELETE',
|
716
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
|
717
|
+
callback: callback });
|
718
|
+
},
|
719
|
+
/*
|
720
|
+
* Search inside the index using XMLHttpRequest request (Using a POST query to
|
721
|
+
* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
|
722
|
+
*
|
723
|
+
* @param query the full text query
|
724
|
+
* @param callback the result callback with two arguments:
|
725
|
+
* success: boolean set to true if the request was successfull. If false, the content contains the error.
|
726
|
+
* content: the server answer that contains the list of results.
|
727
|
+
* @param args (optional) if set, contains an object with query parameters:
|
728
|
+
* - page: (integer) Pagination parameter used to select the page to retrieve.
|
729
|
+
* Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
|
730
|
+
* - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
|
731
|
+
* - attributesToRetrieve: a string that contains the list of object attributes you want to retrieve (let you minimize the answer size).
|
732
|
+
* Attributes are separated with a comma (for example "name,address").
|
733
|
+
* You can also use a string array encoding (for example ["name","address"]).
|
734
|
+
* By default, all attributes are retrieved. You can also use '*' to retrieve all values when an attributesToRetrieve setting is specified for your index.
|
735
|
+
* - attributesToHighlight: a string that contains the list of attributes you want to highlight according to the query.
|
736
|
+
* Attributes are separated by a comma. You can also use a string array encoding (for example ["name","address"]).
|
737
|
+
* If an attribute has no match for the query, the raw value is returned. By default all indexed text attributes are highlighted.
|
738
|
+
* You can use `*` if you want to highlight all textual attributes. Numerical attributes are not highlighted.
|
739
|
+
* A matchLevel is returned for each highlighted attribute and can contain:
|
740
|
+
* - full: if all the query terms were found in the attribute,
|
741
|
+
* - partial: if only some of the query terms were found,
|
742
|
+
* - none: if none of the query terms were found.
|
743
|
+
* - attributesToSnippet: a string that contains the list of attributes to snippet alongside the number of words to return (syntax is `attributeName:nbWords`).
|
744
|
+
* Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
|
745
|
+
* You can also use a string array encoding (Example: attributesToSnippet: ["name:10","content:10"]). By default no snippet is computed.
|
746
|
+
* - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word. Defaults to 3.
|
747
|
+
* - minWordSizefor2Typos: the minimum number of characters in a query word to accept two typos in this word. Defaults to 7.
|
748
|
+
* - getRankingInfo: if set to 1, the result hits will contain ranking information in _rankingInfo attribute.
|
749
|
+
* - aroundLatLng: search for entries around a given latitude/longitude (specified as two floats separated by a comma).
|
750
|
+
* For example aroundLatLng=47.316669,5.016670).
|
751
|
+
* You can specify the maximum distance in meters with the aroundRadius parameter (in meters) and the precision for ranking with aroundPrecision
|
752
|
+
* (for example if you set aroundPrecision=100, two objects that are distant of less than 100m will be considered as identical for "geo" ranking parameter).
|
753
|
+
* At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
|
754
|
+
* - insideBoundingBox: search entries inside a given area defined by the two extreme points of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
|
755
|
+
* For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
|
756
|
+
* At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
|
757
|
+
* - numericFilters: a string that contains the list of numeric filters you want to apply separated by a comma.
|
758
|
+
* The syntax of one filter is `attributeName` followed by `operand` followed by `value`. Supported operands are `<`, `<=`, `=`, `>` and `>=`.
|
759
|
+
* You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
|
760
|
+
* You can also use a string array encoding (for example numericFilters: ["price>100","price<1000"]).
|
761
|
+
* - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
|
762
|
+
* To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
|
763
|
+
* You can also use a string array encoding, for example tagFilters: ["tag1",["tag2","tag3"]] means tag1 AND (tag2 OR tag3).
|
764
|
+
* At indexing, tags should be added in the _tags** attribute of objects (for example {"_tags":["tag1","tag2"]}).
|
765
|
+
* - facetFilters: filter the query by a list of facets.
|
766
|
+
* Facets are separated by commas and each facet is encoded as `attributeName:value`.
|
767
|
+
* For example: `facetFilters=category:Book,author:John%20Doe`.
|
768
|
+
* You can also use a string array encoding (for example `["category:Book","author:John%20Doe"]`).
|
769
|
+
* - facets: List of object attributes that you want to use for faceting.
|
770
|
+
* Attributes are separated with a comma (for example `"category,author"` ).
|
771
|
+
* You can also use a JSON string array encoding (for example ["category","author"]).
|
772
|
+
* Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter.
|
773
|
+
* You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
|
774
|
+
* - queryType: select how the query words are interpreted, it can be one of the following value:
|
775
|
+
* - prefixAll: all query words are interpreted as prefixes,
|
776
|
+
* - prefixLast: only the last word is interpreted as a prefix (default behavior),
|
777
|
+
* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
|
778
|
+
* - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
|
779
|
+
* The list of words is comma separated.
|
780
|
+
* - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
|
781
|
+
* This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
|
782
|
+
* all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
|
783
|
+
* For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
|
784
|
+
* one is kept and others are removed.
|
785
|
+
* @param delay (optional) if set, wait for this delay (in ms) and only send the query if there was no other in the meantime.
|
786
|
+
*/
|
787
|
+
search: function(query, callback, args, delay) {
|
788
|
+
var indexObj = this;
|
789
|
+
var params = 'query=' + encodeURIComponent(query);
|
790
|
+
if (!this.as._isUndefined(args) && args != null) {
|
791
|
+
params = this.as._getSearchParams(args, params);
|
792
|
+
}
|
793
|
+
window.clearTimeout(indexObj.onDelayTrigger);
|
794
|
+
if (!this.as._isUndefined(delay) && delay != null && delay > 0) {
|
795
|
+
var onDelayTrigger = window.setTimeout( function() {
|
796
|
+
indexObj._search(params, callback);
|
797
|
+
}, delay);
|
798
|
+
indexObj.onDelayTrigger = onDelayTrigger;
|
799
|
+
} else {
|
800
|
+
this._search(params, callback);
|
801
|
+
}
|
802
|
+
},
|
803
|
+
|
804
|
+
/*
|
805
|
+
* Browse all index content
|
806
|
+
*
|
807
|
+
* @param page Pagination parameter used to select the page to retrieve.
|
808
|
+
* Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
|
809
|
+
* @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
|
810
|
+
*/
|
811
|
+
browse: function(page, callback, hitsPerPage) {
|
812
|
+
var indexObj = this;
|
813
|
+
var params = '?page=' + page;
|
814
|
+
if (!_.isUndefined(hitsPerPage)) {
|
815
|
+
params += '&hitsPerPage=' + hitsPerPage;
|
816
|
+
}
|
817
|
+
this.as._jsonRequest({ method: 'GET',
|
818
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
|
819
|
+
callback: callback });
|
820
|
+
},
|
821
|
+
|
822
|
+
/*
|
823
|
+
* Get transport layer for Typeahead.js
|
824
|
+
* @param args (optional) if set, contains an object with query parameters (see search for details)
|
825
|
+
* @param propertyName(optional) if set, contains the name of property that will be used for
|
826
|
+
*/
|
827
|
+
getTypeaheadTransport: function(args, valueOption) {
|
828
|
+
this.typeAheadArgs = args;
|
829
|
+
if (typeof valueOption !== 'undefined') {
|
830
|
+
this.typeAheadValueOption = valueOption;
|
831
|
+
}
|
832
|
+
return this;
|
833
|
+
},
|
834
|
+
/*
|
835
|
+
* Update parameter of transport layer for Typeahead.js
|
836
|
+
* @param args contains an object with query parameters (see search for details)
|
837
|
+
* @param propertyName(optional) if set, contains the name of property that will be used for
|
838
|
+
*/
|
839
|
+
setTypeaheadParams: function(args, valueOption) {
|
840
|
+
this.typeAHeadArgs = args;
|
841
|
+
if (typeof valueOption !== 'undefined') {
|
842
|
+
this.typeAheadValueOption = valueOption;
|
843
|
+
}
|
844
|
+
},
|
845
|
+
// Method used by Typeahead.js.
|
846
|
+
get: function(query, processRemoteData, that, cb, suggestions) {
|
847
|
+
self = this;
|
848
|
+
this.search(query, function(success, content) {
|
849
|
+
if (success) {
|
850
|
+
for (var i = 0; i < content.hits.length; ++i) {
|
851
|
+
// Add an attribute value with the first string
|
852
|
+
var obj = content.hits[i],
|
853
|
+
found = false;
|
854
|
+
|
855
|
+
if (typeof obj.value === 'undefined') {
|
856
|
+
if (self.typeAheadValueOption != null) {
|
857
|
+
if (typeof self.typeAheadValueOption === 'function') {
|
858
|
+
obj.value = self.typeAheadValueOption(obj);
|
859
|
+
found = true;
|
860
|
+
} else if (typeof obj[self.typeAheadValueOption] !== 'undefined') {
|
861
|
+
obj.value = obj[self.typeAheadValueOption];
|
496
862
|
found = true;
|
497
863
|
}
|
498
864
|
}
|
865
|
+
if (! found) {
|
866
|
+
for (var propertyName in obj) {
|
867
|
+
if (!found && obj.hasOwnProperty(propertyName) && typeof obj[propertyName] === 'string') {
|
868
|
+
obj.value = obj[propertyName];
|
869
|
+
found = true;
|
870
|
+
}
|
871
|
+
}
|
872
|
+
}
|
499
873
|
}
|
874
|
+
suggestions.push(that._transformDatum(obj));
|
500
875
|
}
|
501
|
-
suggestions
|
876
|
+
cb && cb(suggestions);
|
502
877
|
}
|
503
|
-
|
504
|
-
|
505
|
-
},
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
878
|
+
}, self.typeAheadArgs);
|
879
|
+
return true;
|
880
|
+
},
|
881
|
+
/*
|
882
|
+
* Wait the publication of a task on the server.
|
883
|
+
* All server task are asynchronous and you can check with this method that the task is published.
|
884
|
+
*
|
885
|
+
* @param taskID the id of the task returned by server
|
886
|
+
* @param callback the result callback with with two arguments:
|
887
|
+
* success: boolean set to true if the request was successfull
|
888
|
+
* content: the server answer that contains the list of results
|
889
|
+
*/
|
890
|
+
waitTask: function(taskID, callback) {
|
891
|
+
var indexObj = this;
|
892
|
+
this.as._jsonRequest({ method: 'GET',
|
893
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID,
|
894
|
+
callback: function(success, body) {
|
895
|
+
if (success && body.status === 'published') {
|
515
896
|
callback(true, body);
|
516
897
|
} else if (success && body.pendingTask) {
|
517
898
|
return indexObj.waitTask(taskID, callback);
|
518
899
|
} else {
|
519
900
|
callback(false, body);
|
520
901
|
}
|
521
|
-
}
|
522
|
-
}
|
902
|
+
}});
|
903
|
+
},
|
904
|
+
|
905
|
+
/*
|
906
|
+
* This function deletes the index content. Settings and index specific API keys are kept untouched.
|
907
|
+
*
|
908
|
+
* @param callback (optional) the result callback with two arguments
|
909
|
+
* success: boolean set to true if the request was successfull
|
910
|
+
* content: the settings object or the error message if a failure occured
|
911
|
+
*/
|
912
|
+
clearIndex: function(callback) {
|
913
|
+
var indexObj = this;
|
914
|
+
this.as._jsonRequest({ method: 'POST',
|
915
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
|
916
|
+
callback: callback });
|
917
|
+
},
|
918
|
+
/*
|
919
|
+
* Get settings of this index
|
920
|
+
*
|
921
|
+
* @param callback (optional) the result callback with two arguments
|
922
|
+
* success: boolean set to true if the request was successfull
|
923
|
+
* content: the settings object or the error message if a failure occured
|
924
|
+
*/
|
925
|
+
getSettings: function(callback) {
|
926
|
+
var indexObj = this;
|
927
|
+
this.as._jsonRequest({ method: 'GET',
|
928
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
|
929
|
+
callback: callback });
|
930
|
+
},
|
931
|
+
|
932
|
+
/*
|
933
|
+
* Set settings for this index
|
934
|
+
*
|
935
|
+
* @param settigns the settings object that can contains :
|
936
|
+
* - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
|
937
|
+
* - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
|
938
|
+
* - hitsPerPage: (integer) the number of hits per page (default = 10).
|
939
|
+
* - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
|
940
|
+
* If set to null, all attributes are retrieved.
|
941
|
+
* - attributesToHighlight: (array of strings) default list of attributes to highlight.
|
942
|
+
* If set to null, all indexed attributes are highlighted.
|
943
|
+
* - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number of words to return (syntax is attributeName:nbWords).
|
944
|
+
* By default no snippet is computed. If set to null, no snippet is computed.
|
945
|
+
* - attributesToIndex: (array of strings) the list of fields you want to index.
|
946
|
+
* If set to null, all textual and numerical attributes of your objects are indexed, but you should update it to get optimal results.
|
947
|
+
* This parameter has two important uses:
|
948
|
+
* - Limit the attributes to index: For example if you store a binary image in base64, you want to store it and be able to
|
949
|
+
* retrieve it but you don't want to search in the base64 string.
|
950
|
+
* - Control part of the ranking*: (see the ranking parameter for full explanation) Matches in attributes at the beginning of
|
951
|
+
* the list will be considered more important than matches in attributes further down the list.
|
952
|
+
* In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable
|
953
|
+
* this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
|
954
|
+
* - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
|
955
|
+
* 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.
|
956
|
+
* - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
|
957
|
+
* in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
|
958
|
+
* 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.
|
959
|
+
* - ranking: (array of strings) controls the way results are sorted.
|
960
|
+
* We have six available criteria:
|
961
|
+
* - typo: sort according to number of typos,
|
962
|
+
* - geo: sort according to decreassing distance when performing a geo-location based search,
|
963
|
+
* - proximity: sort according to the proximity of query words in hits,
|
964
|
+
* - attribute: sort according to the order of attributes defined by attributesToIndex,
|
965
|
+
* - exact:
|
966
|
+
* - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
|
967
|
+
* 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
|
968
|
+
* show starting by the v letter before it.
|
969
|
+
* - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
|
970
|
+
* - custom: sort according to a user defined formula set in **customRanking** attribute.
|
971
|
+
* The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
|
972
|
+
* - customRanking: (array of strings) lets you specify part of the ranking.
|
973
|
+
* The syntax of this condition is an array of strings containing attributes prefixed by asc (ascending order) or desc (descending order) operator.
|
974
|
+
* For example `"customRanking" => ["desc(population)", "asc(name)"]`
|
975
|
+
* - queryType: Select how the query words are interpreted, it can be one of the following value:
|
976
|
+
* - prefixAll: all query words are interpreted as prefixes,
|
977
|
+
* - prefixLast: only the last word is interpreted as a prefix (default behavior),
|
978
|
+
* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
|
979
|
+
* - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
|
980
|
+
* - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
|
981
|
+
* - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
|
982
|
+
* @param callback (optional) the result callback with two arguments
|
983
|
+
* success: boolean set to true if the request was successfull
|
984
|
+
* content: the server answer or the error message if a failure occured
|
985
|
+
*/
|
986
|
+
setSettings: function(settings, callback) {
|
987
|
+
var indexObj = this;
|
988
|
+
this.as._jsonRequest({ method: 'PUT',
|
989
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
|
990
|
+
body: settings,
|
991
|
+
callback: callback });
|
992
|
+
},
|
993
|
+
/*
|
994
|
+
* List all existing user keys associated to this index
|
995
|
+
*
|
996
|
+
* @param callback the result callback with two arguments
|
997
|
+
* success: boolean set to true if the request was successfull
|
998
|
+
* content: the server answer with user keys list or error description if success is false.
|
999
|
+
*/
|
1000
|
+
listUserKeys: function(callback) {
|
1001
|
+
var indexObj = this;
|
1002
|
+
this.as._jsonRequest({ method: 'GET',
|
1003
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
|
1004
|
+
callback: callback });
|
1005
|
+
},
|
1006
|
+
/*
|
1007
|
+
* Get ACL of a user key associated to this index
|
1008
|
+
*
|
1009
|
+
* @param callback the result callback with two arguments
|
1010
|
+
* success: boolean set to true if the request was successfull
|
1011
|
+
* content: the server answer with user keys list or error description if success is false.
|
1012
|
+
*/
|
1013
|
+
getUserKeyACL: function(key, callback) {
|
1014
|
+
var indexObj = this;
|
1015
|
+
this.as._jsonRequest({ method: 'GET',
|
1016
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
|
1017
|
+
callback: callback });
|
1018
|
+
},
|
1019
|
+
/*
|
1020
|
+
* Delete an existing user key associated to this index
|
1021
|
+
*
|
1022
|
+
* @param callback the result callback with two arguments
|
1023
|
+
* success: boolean set to true if the request was successfull
|
1024
|
+
* content: the server answer with user keys list or error description if success is false.
|
1025
|
+
*/
|
1026
|
+
deleteUserKey: function(key, callback) {
|
1027
|
+
var indexObj = this;
|
1028
|
+
this.as._jsonRequest({ method: 'DELETE',
|
1029
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
|
1030
|
+
callback: callback });
|
1031
|
+
},
|
1032
|
+
/*
|
1033
|
+
* Add an existing user key associated to this index
|
1034
|
+
*
|
1035
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
1036
|
+
* can contains the following values:
|
1037
|
+
* - search: allow to search (https and http)
|
1038
|
+
* - addObject: allows to add/update an object in the index (https only)
|
1039
|
+
* - deleteObject : allows to delete an existing object (https only)
|
1040
|
+
* - deleteIndex : allows to delete index content (https only)
|
1041
|
+
* - settings : allows to get index settings (https only)
|
1042
|
+
* - editSettings : allows to change index settings (https only)
|
1043
|
+
* @param callback the result callback with two arguments
|
1044
|
+
* success: boolean set to true if the request was successfull
|
1045
|
+
* content: the server answer with user keys list or error description if success is false.
|
1046
|
+
*/
|
1047
|
+
addUserKey: function(acls, callback) {
|
1048
|
+
var indexObj = this;
|
1049
|
+
var aclsObject = {};
|
1050
|
+
aclsObject.acl = acls;
|
1051
|
+
this.as._jsonRequest({ method: 'POST',
|
1052
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
|
1053
|
+
body: aclsObject,
|
1054
|
+
callback: callback });
|
1055
|
+
},
|
1056
|
+
/*
|
1057
|
+
* Add an existing user key associated to this index
|
1058
|
+
*
|
1059
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
1060
|
+
* can contains the following values:
|
1061
|
+
* - search: allow to search (https and http)
|
1062
|
+
* - addObject: allows to add/update an object in the index (https only)
|
1063
|
+
* - deleteObject : allows to delete an existing object (https only)
|
1064
|
+
* - deleteIndex : allows to delete index content (https only)
|
1065
|
+
* - settings : allows to get index settings (https only)
|
1066
|
+
* - editSettings : allows to change index settings (https only)
|
1067
|
+
* @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
|
1068
|
+
* @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
|
1069
|
+
* @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
|
1070
|
+
* @param callback the result callback with two arguments
|
1071
|
+
* success: boolean set to true if the request was successfull
|
1072
|
+
* content: the server answer with user keys list or error description if success is false.
|
1073
|
+
*/
|
1074
|
+
addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
|
1075
|
+
var indexObj = this;
|
1076
|
+
var aclsObject = {};
|
1077
|
+
aclsObject.acl = acls;
|
1078
|
+
aclsObject.validity = validity;
|
1079
|
+
aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
|
1080
|
+
aclsObject.maxHitsPerQuery = maxHitsPerQuery;
|
1081
|
+
this.as._jsonRequest({ method: 'POST',
|
1082
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
|
1083
|
+
body: aclsObject,
|
1084
|
+
callback: callback });
|
1085
|
+
},
|
1086
|
+
///
|
1087
|
+
/// Internal methods only after this line
|
1088
|
+
///
|
1089
|
+
_search: function(params, callback) {
|
1090
|
+
this.as._jsonRequest({ cache: this.cache,
|
1091
|
+
method: 'POST',
|
1092
|
+
url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
|
1093
|
+
body: {params: params, apiKey: this.as.apiKey, appID: this.as.applicationID},
|
1094
|
+
callback: callback });
|
1095
|
+
},
|
1096
|
+
|
1097
|
+
// internal attributes
|
1098
|
+
as: null,
|
1099
|
+
indexName: null,
|
1100
|
+
cache: {},
|
1101
|
+
typeAheadArgs: null,
|
1102
|
+
typeAheadValueOption: null,
|
1103
|
+
emptyConstructor: function() {}
|
1104
|
+
};
|
1105
|
+
|
1106
|
+
/*
|
1107
|
+
* Copyright (c) 2014 Algolia
|
1108
|
+
* http://www.algolia.com/
|
1109
|
+
*
|
1110
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
1111
|
+
* of this software and associated documentation files (the "Software"), to deal
|
1112
|
+
* in the Software without restriction, including without limitation the rights
|
1113
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
1114
|
+
* copies of the Software, and to permit persons to whom the Software is
|
1115
|
+
* furnished to do so, subject to the following conditions:
|
1116
|
+
*
|
1117
|
+
* The above copyright notice and this permission notice shall be included in
|
1118
|
+
* all copies or substantial portions of the Software.
|
1119
|
+
*
|
1120
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
1121
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
1122
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
1123
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
1124
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
1125
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
1126
|
+
* THE SOFTWARE.
|
1127
|
+
*/
|
1128
|
+
|
1129
|
+
(function($) {
|
1130
|
+
var self;
|
1131
|
+
|
1132
|
+
/**
|
1133
|
+
* Algolia Search Helper providing faceting and disjunctive faceting
|
1134
|
+
* @param {AlgoliaSearch} client an AlgoliaSearch client
|
1135
|
+
* @param {string} index the index name to query
|
1136
|
+
* @param {hash} options an associative array defining the hitsPerPage, list of facets and list of disjunctive facets
|
1137
|
+
*/
|
1138
|
+
window.AlgoliaSearchHelper = function(client, index, options) {
|
1139
|
+
/// Default options
|
1140
|
+
var defaults = {
|
1141
|
+
facets: [], // list of facets to compute
|
1142
|
+
disjunctiveFacets: [], // list of disjunctive facets to compute
|
1143
|
+
hitsPerPage: 20 // number of hits per page
|
1144
|
+
};
|
1145
|
+
|
1146
|
+
this.init(client, index, $.extend({}, defaults, options));
|
1147
|
+
self = this;
|
1148
|
+
};
|
1149
|
+
|
1150
|
+
AlgoliaSearchHelper.prototype = {
|
1151
|
+
/**
|
1152
|
+
* Initialize a new AlgoliaSearchHelper
|
1153
|
+
* @param {AlgoliaSearch} client an AlgoliaSearch client
|
1154
|
+
* @param {string} index the index name to query
|
1155
|
+
* @param {hash} options an associative array defining the hitsPerPage, list of facets and list of disjunctive facets
|
1156
|
+
* @return {AlgoliaSearchHelper}
|
1157
|
+
*/
|
1158
|
+
init: function(client, index, options) {
|
1159
|
+
this.client = client;
|
1160
|
+
this.index = index;
|
1161
|
+
this.options = options;
|
1162
|
+
this.page = 0;
|
1163
|
+
this.refinements = {};
|
1164
|
+
this.disjunctiveRefinements = {};
|
523
1165
|
},
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
1166
|
+
|
1167
|
+
/**
|
1168
|
+
* Perform a query
|
1169
|
+
* @param {string} q the user query
|
1170
|
+
* @param {function} searchCallback the result callback called with two arguments:
|
1171
|
+
* success: boolean set to true if the request was successfull
|
1172
|
+
* content: the query answer with an extra 'disjunctiveFacets' attribute
|
1173
|
+
*/
|
1174
|
+
search: function(q, searchCallback) {
|
1175
|
+
this.q = q;
|
1176
|
+
this.searchCallback = searchCallback;
|
1177
|
+
this.page = 0;
|
1178
|
+
this.refinements = {};
|
1179
|
+
this.disjunctiveRefinements = {};
|
1180
|
+
this._search();
|
531
1181
|
},
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
1182
|
+
|
1183
|
+
/**
|
1184
|
+
* Toggle refinement state of a facet
|
1185
|
+
* @param {string} facet the facet to refine
|
1186
|
+
* @param {string} value the associated value
|
1187
|
+
* @return {boolean} true if the facet has been found
|
1188
|
+
*/
|
1189
|
+
toggleRefine: function(facet, value) {
|
1190
|
+
for (var i = 0; i < this.options.facets.length; ++i) {
|
1191
|
+
if (this.options.facets[i] == facet) {
|
1192
|
+
var refinement = facet + ':' + value;
|
1193
|
+
this.refinements[refinement] = !this.refinements[refinement];
|
1194
|
+
this.page = 0;
|
1195
|
+
this._search();
|
1196
|
+
return true;
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
|
1200
|
+
for (var j = 0; j < this.options.disjunctiveFacets.length; ++j) {
|
1201
|
+
if (this.options.disjunctiveFacets[j] == facet) {
|
1202
|
+
this.disjunctiveRefinements[facet][value] = !this.disjunctiveRefinements[facet][value];
|
1203
|
+
this.page = 0;
|
1204
|
+
this._search();
|
1205
|
+
return true;
|
1206
|
+
}
|
1207
|
+
}
|
1208
|
+
return false;
|
539
1209
|
},
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
1210
|
+
|
1211
|
+
/**
|
1212
|
+
* Check the refinement state of a facet
|
1213
|
+
* @param {string} facet the facet
|
1214
|
+
* @param {string} value the associated value
|
1215
|
+
* @return {boolean} true if refined
|
1216
|
+
*/
|
1217
|
+
isRefined: function(facet, value) {
|
1218
|
+
var refinement = facet + ':' + value;
|
1219
|
+
if (this.refinements[refinement]) {
|
1220
|
+
return true;
|
1221
|
+
}
|
1222
|
+
if (this.disjunctiveRefinements[facet] && this.disjunctiveRefinements[facet][value]) {
|
1223
|
+
return true;
|
1224
|
+
}
|
1225
|
+
return false;
|
548
1226
|
},
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
});
|
1227
|
+
|
1228
|
+
/**
|
1229
|
+
* Go to next page
|
1230
|
+
*/
|
1231
|
+
nextPage: function() {
|
1232
|
+
this._gotoPage(this.page + 1);
|
556
1233
|
},
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
1234
|
+
|
1235
|
+
/**
|
1236
|
+
* Go to previous page
|
1237
|
+
*/
|
1238
|
+
previousPage: function() {
|
1239
|
+
if (this.page > 0) {
|
1240
|
+
this._gotoPage(this.page - 1);
|
1241
|
+
}
|
564
1242
|
},
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
1243
|
+
|
1244
|
+
///////////// PRIVATE
|
1245
|
+
|
1246
|
+
/**
|
1247
|
+
* Goto a page
|
1248
|
+
* @param {integer} page The page number
|
1249
|
+
*/
|
1250
|
+
_gotoPage: function(page) {
|
1251
|
+
this.page = page;
|
1252
|
+
this._search();
|
572
1253
|
},
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
1254
|
+
|
1255
|
+
/**
|
1256
|
+
* Perform the underlying queries
|
1257
|
+
*/
|
1258
|
+
_search: function() {
|
1259
|
+
this.client.startQueriesBatch();
|
1260
|
+
this.client.addQueryInBatch(this.index, this.q, this._getHitsSearchParams());
|
1261
|
+
for (var i = 0; i < this.options.disjunctiveFacets.length; ++i) {
|
1262
|
+
this.client.addQueryInBatch(this.index, this.q, this._getDisjunctiveFacetSearchParams(this.options.disjunctiveFacets[i]));
|
1263
|
+
}
|
1264
|
+
this.client.sendQueriesBatch(function(success, content) {
|
1265
|
+
if (!success) {
|
1266
|
+
self.searchCallback(false, content);
|
1267
|
+
return;
|
1268
|
+
}
|
1269
|
+
var aggregatedAnswer = content.results[0];
|
1270
|
+
aggregatedAnswer.disjunctiveFacets = {};
|
1271
|
+
for (var i = 1; i < content.results.length; ++i) {
|
1272
|
+
for (var facet in content.results[i].facets) {
|
1273
|
+
aggregatedAnswer.disjunctiveFacets[facet] = content.results[i].facets[facet];
|
1274
|
+
if (self.disjunctiveRefinements[facet]) {
|
1275
|
+
for (var value in self.disjunctiveRefinements[facet]) {
|
1276
|
+
if (!aggregatedAnswer.disjunctiveFacets[facet][value] && self.disjunctiveRefinements[facet][value]) {
|
1277
|
+
aggregatedAnswer.disjunctiveFacets[facet][value] = 0;
|
1278
|
+
}
|
1279
|
+
}
|
1280
|
+
}
|
1281
|
+
}
|
1282
|
+
}
|
1283
|
+
self.searchCallback(true, aggregatedAnswer);
|
1284
|
+
});
|
583
1285
|
},
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
this.
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
},
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
1286
|
+
|
1287
|
+
/**
|
1288
|
+
* Build search parameters used to fetch hits
|
1289
|
+
* @return {hash}
|
1290
|
+
*/
|
1291
|
+
_getHitsSearchParams: function() {
|
1292
|
+
return {
|
1293
|
+
hitsPerPage: this.options.hitsPerPage,
|
1294
|
+
page: this.page,
|
1295
|
+
facets: this.options.facets,
|
1296
|
+
facetFilters: this._getFacetFilters()
|
1297
|
+
};
|
1298
|
+
},
|
1299
|
+
|
1300
|
+
/**
|
1301
|
+
* Build search parameters used to fetch a disjunctive facet
|
1302
|
+
* @param {string} facet the associated facet name
|
1303
|
+
* @return {hash}
|
1304
|
+
*/
|
1305
|
+
_getDisjunctiveFacetSearchParams: function(facet) {
|
1306
|
+
return {
|
1307
|
+
hitsPerPage: 1,
|
1308
|
+
page: 0,
|
1309
|
+
facets: facet,
|
1310
|
+
facetFilters: this._getFacetFilters(facet)
|
1311
|
+
};
|
1312
|
+
},
|
1313
|
+
|
1314
|
+
/**
|
1315
|
+
* Build facetFilters parameter based on current refinements
|
1316
|
+
* @param {string} facet if set, the current disjunctive facet
|
1317
|
+
* @return {hash}
|
1318
|
+
*/
|
1319
|
+
_getFacetFilters: function(facet) {
|
1320
|
+
var facetFilters = [];
|
1321
|
+
for (var refinement in this.refinements) {
|
1322
|
+
if (this.refinements[refinement]) {
|
1323
|
+
facetFilters.push(refinement);
|
1324
|
+
}
|
1325
|
+
}
|
1326
|
+
for (var disjunctiveRefinement in this.disjunctiveRefinements) {
|
1327
|
+
if (disjunctiveRefinement != facet) {
|
1328
|
+
var refinements = [];
|
1329
|
+
for (var value in this.disjunctiveRefinements[disjunctiveRefinement]) {
|
1330
|
+
if (this.disjunctiveRefinements[disjunctiveRefinement][value]) {
|
1331
|
+
refinements.push(disjunctiveRefinement + ':' + value);
|
1332
|
+
}
|
1333
|
+
}
|
1334
|
+
if (refinements.length > 0) {
|
1335
|
+
facetFilters.push(refinements);
|
1336
|
+
}
|
1337
|
+
}
|
1338
|
+
}
|
1339
|
+
return facetFilters;
|
1340
|
+
}
|
1341
|
+
};
|
1342
|
+
})(jQuery);
|