algoliasearch-rails 1.11.17 → 1.11.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +4 -0
- data/README.md +48 -6
- data/VERSION +1 -1
- data/algoliasearch-rails.gemspec +22 -6
- data/vendor/assets/javascripts/algolia/algoliasearch.angular.js +1446 -1378
- data/vendor/assets/javascripts/algolia/algoliasearch.angular.min.js +2 -2
- data/vendor/assets/javascripts/algolia/algoliasearch.jquery.js +1446 -1378
- data/vendor/assets/javascripts/algolia/algoliasearch.jquery.min.js +2 -2
- data/vendor/assets/javascripts/algolia/algoliasearch.js +1446 -1378
- data/vendor/assets/javascripts/algolia/algoliasearch.min.js +2 -2
- data/vendor/assets/javascripts/algolia/bloodhound.min.js +7 -0
- data/vendor/assets/javascripts/algolia/typeahead.jquery.min.js +7 -0
- data/vendor/assets/javascripts/algolia/v2/algoliasearch.angular.js +2667 -0
- data/vendor/assets/javascripts/algolia/v2/algoliasearch.angular.min.js +7 -0
- data/vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.js +2667 -0
- data/vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.min.js +7 -0
- data/vendor/assets/javascripts/algolia/v2/algoliasearch.js +2653 -0
- data/vendor/assets/javascripts/algolia/v2/algoliasearch.min.js +7 -0
- data/vendor/assets/javascripts/algolia/v3/algoliasearch.angular.js +1717 -0
- data/vendor/assets/javascripts/algolia/v3/algoliasearch.angular.min.js +37 -0
- data/vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.js +1702 -0
- data/vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.min.js +37 -0
- data/vendor/assets/javascripts/algolia/v3/algoliasearch.js +2732 -0
- data/vendor/assets/javascripts/algolia/v3/algoliasearch.min.js +50 -0
- metadata +36 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b90eda1033e39094342631bc26f4356b2fb77723
|
4
|
+
data.tar.gz: b2b8a9acdc2814b526f69c30c7bb85de3998f039
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fae998c2943cbaa65c7fd588a96aaee86fe7b5c8937dda84c3708e5c09fe6c94367361230a3e264023f4d6d585cc3b7641ac41b11b14a7e4b4cc689e23cfc7c3
|
7
|
+
data.tar.gz: d079747d3a7fcc582b4dce2ec76a225abf6c9c46dd8f340d893448e67e7d87d73a231ab2bb89b3c5631993f5dc9dc6964191fc9473f9cdac74b8382ba2104ef9
|
data/ChangeLog
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
CHANGELOG
|
2
2
|
|
3
|
+
2015-03-31 1.11.18
|
4
|
+
|
5
|
+
* Embed AlgoliaSearch JS API client v3 as well. Default is still the v2 to keep the backward compatibility.
|
6
|
+
|
3
7
|
2015-03-08 1.11.17
|
4
8
|
|
5
9
|
* Add missing index settings (including removeWordsIfNoResults, unretrievableAttributes and ignorePlurals)
|
data/README.md
CHANGED
@@ -14,6 +14,7 @@ Table of Content
|
|
14
14
|
1. [Install](#install)
|
15
15
|
1. [Setup](#setup)
|
16
16
|
1. [Quick Start](#quick-start)
|
17
|
+
1. [Ranking & Relevance](#ranking--relevance)
|
17
18
|
1. [Options](#options)
|
18
19
|
1. [Configuration example](#configuration-example)
|
19
20
|
1. [Indexing](#indexing)
|
@@ -105,15 +106,56 @@ class Product < ActiveRecord::Base
|
|
105
106
|
end
|
106
107
|
```
|
107
108
|
|
109
|
+
#### Ranking & Relevance
|
110
|
+
|
111
|
+
We provide many ways to configure your index allowing you to tune your overall index relevancy. The most important ones are the **searchable attributes** and the attributes reflecting **record popularity**.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
class Product < ActiveRecord::Base
|
115
|
+
include AlgoliaSearch
|
116
|
+
|
117
|
+
algoliasearch do
|
118
|
+
# list of attribute used to build an Algolia record
|
119
|
+
attributes :title, :subtitle, :description, :likes_count, :seller_name
|
120
|
+
|
121
|
+
# the attributesToIndex` setting defines the attributes
|
122
|
+
# you want to search in: here `title`, `subtitle` & `description`.
|
123
|
+
# You need to list them by order of importance. `description` is tagged as
|
124
|
+
# `unordered` to avoid taking the position of a match into account in that attribute.
|
125
|
+
attributesToIndex ['title', 'subtitle', 'unordered(description)']
|
126
|
+
|
127
|
+
# the `customRanking` setting defines the ranking criteria use to compare two matching
|
128
|
+
# records in case their text-relevance is equal. It should reflect your record popularity.
|
129
|
+
customRanking ['desc(likes_count)']
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
108
135
|
#### Frontend Search (realtime experience)
|
109
136
|
|
137
|
+
Traditional search implementations tend to have search logic and functionality on the backend. This made sense when the search experience consisted of a user entering a search query, executing that search, and then being redirected to a search result page.
|
138
|
+
|
139
|
+
Implementing search on the backend is no longer necessary. In fact, in most cases it is harmful to performance because of added network and processing latency. We highly recommend the usage of our [JavaScript API Client](https://github.com/algolia/algoliasearch-client-js) issuing all search requests directly from the end user's browser, mobile device, or client. It will reduce the overall search latency while offloading your servers at the same time.
|
110
140
|
|
111
|
-
|
141
|
+
The JS API client is part of the gem, just require ```algolia/v3/algoliasearch.min``` somewhere in your JavaScript manifest, for example in ```application.js``` if you are using Rails 3.1+:
|
112
142
|
|
113
143
|
```javascript
|
114
|
-
//= require algolia/algoliasearch.min
|
144
|
+
//= require algolia/v3/algoliasearch.min
|
115
145
|
```
|
116
146
|
|
147
|
+
Then in your JavaScript code you can do:
|
148
|
+
|
149
|
+
```js
|
150
|
+
var client = new AlgoliaSearch('ApplicationID', 'Search-Only-API-Key');
|
151
|
+
var index = client.initIndex('YourIndexName');
|
152
|
+
index.search('something', function(success, hits) {
|
153
|
+
console.log(success, hits)
|
154
|
+
}, { hitsPerPage: 10, page: 0 });
|
155
|
+
```
|
156
|
+
|
157
|
+
**We recently (March 2015) released a new version (V3) of our JavaScript client, if you were using our previous version (V2), [read the migration guide](https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x)**
|
158
|
+
|
117
159
|
#### Backend Search
|
118
160
|
|
119
161
|
A search returns ORM-compliant objects reloading them from your database.
|
@@ -393,7 +435,7 @@ class Item < ActiveRecord::Base
|
|
393
435
|
|
394
436
|
algoliasearch per_environment: true do
|
395
437
|
# the list of attributes sent to Algolia's API
|
396
|
-
attribute :created_at, :title, :url, :author, :points, :story_text, :comment_text, :author, :num_comments, :story_id, :story_title
|
438
|
+
attribute :created_at, :title, :url, :author, :points, :story_text, :comment_text, :author, :num_comments, :story_id, :story_title
|
397
439
|
|
398
440
|
# integer version of the created_at datetime field, to use numerical filtering
|
399
441
|
attribute :created_at_i do
|
@@ -703,10 +745,10 @@ At query time, specify <code>{ aroundLatLng: "37.33, -121.89", aroundRadius: 500
|
|
703
745
|
Typeahead UI
|
704
746
|
-------------
|
705
747
|
|
706
|
-
Require ```algolia/algoliasearch.min``` (see [algoliasearch-client-js](https://github.com/algolia/algoliasearch-client-js)) and ```algolia/typeahead.jquery.js``` somewhere in your JavaScript manifest, for example in ```application.js``` if you are using Rails 3.1+:
|
748
|
+
Require ```algolia/v3/algoliasearch.min``` (see [algoliasearch-client-js](https://github.com/algolia/algoliasearch-client-js)) and ```algolia/typeahead.jquery.js``` somewhere in your JavaScript manifest, for example in ```application.js``` if you are using Rails 3.1+:
|
707
749
|
|
708
750
|
```javascript
|
709
|
-
//= require algolia/algoliasearch.min
|
751
|
+
//= require algolia/v3/algoliasearch.min
|
710
752
|
//= require algolia/typeahead.jquery
|
711
753
|
```
|
712
754
|
|
@@ -723,7 +765,7 @@ Turns any ```input[type="text"]``` element into a typeahead, for example:
|
|
723
765
|
|
724
766
|
<script type="text/javascript">
|
725
767
|
$(document).ready(function() {
|
726
|
-
var client =
|
768
|
+
var client = algoliasearch('YourApplicationID', 'SearchOnlyApplicationKey');
|
727
769
|
var template = Hogan.compile('{{{_highlightResult.email.value}}} ({{{_highlightResult.first_name.value}}} {{{_highlightResult.last_name.value}}})');
|
728
770
|
$('input#user_email').typeahead(null, {
|
729
771
|
source: client.initIndex('<%= Contact.index_name %>').ttAdapter(),
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.11.
|
1
|
+
1.11.18
|
data/algoliasearch-rails.gemspec
CHANGED
@@ -38,16 +38,32 @@ Gem::Specification.new do |s|
|
|
38
38
|
"lib/algoliasearch/utilities.rb",
|
39
39
|
"spec/spec_helper.rb",
|
40
40
|
"spec/utilities_spec.rb",
|
41
|
-
"vendor/assets/javascripts/algolia/algoliasearch.js",
|
42
|
-
"vendor/assets/javascripts/algolia/algoliasearch.min.js",
|
43
|
-
"vendor/assets/javascripts/algolia/algoliasearch.jquery.js",
|
44
|
-
"vendor/assets/javascripts/algolia/algoliasearch.jquery.min.js",
|
45
41
|
"vendor/assets/javascripts/algolia/algoliasearch.angular.js",
|
46
42
|
"vendor/assets/javascripts/algolia/algoliasearch.angular.min.js",
|
47
|
-
"vendor/assets/javascripts/algolia/
|
43
|
+
"vendor/assets/javascripts/algolia/algoliasearch.jquery.js",
|
44
|
+
"vendor/assets/javascripts/algolia/algoliasearch.jquery.min.js",
|
45
|
+
"vendor/assets/javascripts/algolia/algoliasearch.js",
|
46
|
+
"vendor/assets/javascripts/algolia/algoliasearch.min.js",
|
47
|
+
"vendor/assets/javascripts/algolia/bloodhound.js",
|
48
|
+
"vendor/assets/javascripts/algolia/bloodhound.min.js",
|
48
49
|
"vendor/assets/javascripts/algolia/typeahead.bundle.js",
|
49
50
|
"vendor/assets/javascripts/algolia/typeahead.bundle.min.js",
|
50
|
-
"vendor/assets/javascripts/algolia/
|
51
|
+
"vendor/assets/javascripts/algolia/typeahead.jquery.js",
|
52
|
+
"vendor/assets/javascripts/algolia/typeahead.jquery.min.js",
|
53
|
+
"vendor/assets/javascripts/algolia/v2",
|
54
|
+
"vendor/assets/javascripts/algolia/v2/algoliasearch.angular.js",
|
55
|
+
"vendor/assets/javascripts/algolia/v2/algoliasearch.angular.min.js",
|
56
|
+
"vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.js",
|
57
|
+
"vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.min.js",
|
58
|
+
"vendor/assets/javascripts/algolia/v2/algoliasearch.js",
|
59
|
+
"vendor/assets/javascripts/algolia/v2/algoliasearch.min.js",
|
60
|
+
"vendor/assets/javascripts/algolia/v3",
|
61
|
+
"vendor/assets/javascripts/algolia/v3/algoliasearch.angular.js",
|
62
|
+
"vendor/assets/javascripts/algolia/v3/algoliasearch.angular.min.js",
|
63
|
+
"vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.js",
|
64
|
+
"vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.min.js",
|
65
|
+
"vendor/assets/javascripts/algolia/v3/algoliasearch.js",
|
66
|
+
"vendor/assets/javascripts/algolia/v3/algoliasearch.min.js"
|
51
67
|
]
|
52
68
|
s.homepage = "http://github.com/algolia/algoliasearch-rails"
|
53
69
|
s.licenses = ["MIT"]
|
@@ -21,7 +21,7 @@
|
|
21
21
|
* THE SOFTWARE.
|
22
22
|
*/
|
23
23
|
|
24
|
-
var ALGOLIA_VERSION = '2.9.
|
24
|
+
var ALGOLIA_VERSION = '2.9.4';
|
25
25
|
|
26
26
|
/*
|
27
27
|
* Copyright (c) 2013 Algolia
|
@@ -58,892 +58,959 @@ var ALGOLIA_VERSION = '2.9.2';
|
|
58
58
|
* - dsnHost (optional) override the automatic computation of dsn hostname
|
59
59
|
*/
|
60
60
|
var AlgoliaSearch = function(applicationID, apiKey, methodOrOptions, resolveDNS, hosts) {
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
}
|
87
|
-
if (!this._isUndefined(options.tld)) {
|
88
|
-
tld = options.tld;
|
89
|
-
}
|
90
|
-
if (!this._isUndefined(options.dsn)) {
|
91
|
-
this.dsn = options.dsn;
|
92
|
-
}
|
93
|
-
if (!this._isUndefined(options.hosts)) {
|
94
|
-
hosts = options.hosts;
|
95
|
-
}
|
96
|
-
if (!this._isUndefined(options.dsnHost)) {
|
97
|
-
this.dsnHost = options.dsnHost;
|
98
|
-
}
|
99
|
-
if (!this._isUndefined(options.requestTimeoutInMs)) {
|
100
|
-
this.requestTimeoutInMs = +options.requestTimeoutInMs;
|
101
|
-
}
|
102
|
-
if (!this._isUndefined(options.jsonp)) {
|
103
|
-
this.jsonp = options.jsonp;
|
104
|
-
}
|
61
|
+
var self = this;
|
62
|
+
this.applicationID = applicationID;
|
63
|
+
this.apiKey = apiKey;
|
64
|
+
this.dsn = true;
|
65
|
+
this.dsnHost = null;
|
66
|
+
this.hosts = [];
|
67
|
+
this.currentHostIndex = 0;
|
68
|
+
this.requestTimeoutInMs = 2000;
|
69
|
+
this.extraHeaders = [];
|
70
|
+
this.jsonp = null;
|
71
|
+
this.options = {};
|
72
|
+
|
73
|
+
// make sure every client instance has it's own cache
|
74
|
+
this.cache = {};
|
75
|
+
|
76
|
+
var method;
|
77
|
+
var tld = 'net';
|
78
|
+
if (typeof methodOrOptions === 'string') { // Old initialization
|
79
|
+
method = methodOrOptions;
|
80
|
+
} else {
|
81
|
+
// Take all option from the hash
|
82
|
+
var options = methodOrOptions || {};
|
83
|
+
this.options = options;
|
84
|
+
if (!this._isUndefined(options.method)) {
|
85
|
+
method = options.method;
|
105
86
|
}
|
106
|
-
|
107
|
-
|
108
|
-
hosts = [
|
109
|
-
this.applicationID + '-1.algolia.' + tld,
|
110
|
-
this.applicationID + '-2.algolia.' + tld,
|
111
|
-
this.applicationID + '-3.algolia.' + tld
|
112
|
-
];
|
113
|
-
}
|
114
|
-
// detect is we use http or https
|
115
|
-
this.host_protocol = 'http://';
|
116
|
-
if (this._isUndefined(method) || method === null) {
|
117
|
-
this.host_protocol = ('https:' == document.location.protocol ? 'https' : 'http') + '://';
|
118
|
-
} else if (method === 'https' || method === 'HTTPS') {
|
119
|
-
this.host_protocol = 'https://';
|
120
|
-
}
|
121
|
-
// Add hosts in random order
|
122
|
-
for (var i = 0; i < hosts.length; ++i) {
|
123
|
-
if (Math.random() > 0.5) {
|
124
|
-
this.hosts.reverse();
|
125
|
-
}
|
126
|
-
this.hosts.push(this.host_protocol + hosts[i]);
|
87
|
+
if (!this._isUndefined(options.tld)) {
|
88
|
+
tld = options.tld;
|
127
89
|
}
|
128
|
-
if (
|
129
|
-
|
90
|
+
if (!this._isUndefined(options.dsn)) {
|
91
|
+
this.dsn = options.dsn;
|
130
92
|
}
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
93
|
+
if (!this._isUndefined(options.hosts)) {
|
94
|
+
hosts = options.hosts;
|
95
|
+
}
|
96
|
+
if (!this._isUndefined(options.dsnHost)) {
|
97
|
+
this.dsnHost = options.dsnHost;
|
98
|
+
}
|
99
|
+
if (!this._isUndefined(options.requestTimeoutInMs)) {
|
100
|
+
this.requestTimeoutInMs = +options.requestTimeoutInMs;
|
101
|
+
}
|
102
|
+
if (!this._isUndefined(options.jsonp)) {
|
103
|
+
this.jsonp = options.jsonp;
|
138
104
|
}
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
105
|
+
}
|
106
|
+
// If hosts is undefined, initialize it with applicationID
|
107
|
+
if (this._isUndefined(hosts)) {
|
108
|
+
hosts = [
|
109
|
+
this.applicationID + '-1.algolia.' + tld,
|
110
|
+
this.applicationID + '-2.algolia.' + tld,
|
111
|
+
this.applicationID + '-3.algolia.' + tld
|
112
|
+
];
|
113
|
+
}
|
114
|
+
// detect is we use http or https
|
115
|
+
this.host_protocol = 'http://';
|
116
|
+
if (this._isUndefined(method) || method === null) {
|
117
|
+
this.host_protocol = ('https:' == document.location.protocol ? 'https' : 'http') + '://';
|
118
|
+
} else if (method === 'https' || method === 'HTTPS') {
|
119
|
+
this.host_protocol = 'https://';
|
120
|
+
}
|
121
|
+
// Add hosts in random order
|
122
|
+
for (var i = 0; i < hosts.length; ++i) {
|
123
|
+
if (Math.random() > 0.5) {
|
124
|
+
this.hosts.reverse();
|
125
|
+
}
|
126
|
+
this.hosts.push(this.host_protocol + hosts[i]);
|
127
|
+
}
|
128
|
+
if (Math.random() > 0.5) {
|
129
|
+
this.hosts.reverse();
|
130
|
+
}
|
131
|
+
// then add Distributed Search Network host if there is one
|
132
|
+
if (this.dsn || this.dsnHost != null) {
|
133
|
+
if (this.dsnHost) {
|
134
|
+
this.hosts.unshift(this.host_protocol + this.dsnHost);
|
135
|
+
} else {
|
136
|
+
this.hosts.unshift(this.host_protocol + this.applicationID + '-dsn.algolia.' + tld);
|
145
137
|
}
|
138
|
+
}
|
139
|
+
// angular dependencies injection
|
140
|
+
if (this.options.angular) {
|
141
|
+
this.options.angular.$injector.invoke(['$http', '$q', function ($http, $q) {
|
142
|
+
self.options.angular.$q = $q;
|
143
|
+
self.options.angular.$http = $http;
|
144
|
+
}]);
|
145
|
+
}
|
146
146
|
};
|
147
147
|
|
148
|
+
// This holds the number of JSONP requests done accross clients
|
149
|
+
// It's used as part of the ?callback=JSONP_$JSONPCounter when we do JSONP requests
|
150
|
+
AlgoliaSearch.JSONPCounter = 0;
|
151
|
+
|
148
152
|
function AlgoliaExplainResults(hit, titleAttribute, otherAttributes) {
|
149
153
|
|
150
|
-
|
154
|
+
function _getHitExplanationForOneAttr_recurse(obj, foundWords) {
|
155
|
+
var res = [];
|
156
|
+
if (typeof obj === 'object' && 'matchedWords' in obj && 'value' in obj) {
|
157
|
+
var match = false;
|
158
|
+
for (var j = 0; j < obj.matchedWords.length; ++j) {
|
159
|
+
var word = obj.matchedWords[j];
|
160
|
+
if (!(word in foundWords)) {
|
161
|
+
foundWords[word] = 1;
|
162
|
+
match = true;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
if (match) {
|
166
|
+
res.push(obj.value);
|
167
|
+
}
|
168
|
+
} else if (Object.prototype.toString.call(obj) === '[object Array]') {
|
169
|
+
for (var i = 0; i < obj.length; ++i) {
|
170
|
+
var array = _getHitExplanationForOneAttr_recurse(obj[i], foundWords);
|
171
|
+
res = res.concat(array);
|
172
|
+
}
|
173
|
+
} else if (typeof obj === 'object') {
|
174
|
+
for (var prop in obj) {
|
175
|
+
if (obj.hasOwnProperty(prop)){
|
176
|
+
res = res.concat(_getHitExplanationForOneAttr_recurse(obj[prop], foundWords));
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
return res;
|
181
|
+
}
|
182
|
+
|
183
|
+
function _getHitExplanationForOneAttr(hit, foundWords, attr) {
|
184
|
+
var base = hit._highlightResult || hit;
|
185
|
+
if (attr.indexOf('.') === -1) {
|
186
|
+
if (attr in base) {
|
187
|
+
return _getHitExplanationForOneAttr_recurse(base[attr], foundWords);
|
188
|
+
}
|
189
|
+
return [];
|
190
|
+
}
|
191
|
+
var array = attr.split('.');
|
192
|
+
var obj = base;
|
193
|
+
for (var i = 0; i < array.length; ++i) {
|
194
|
+
if (Object.prototype.toString.call(obj) === '[object Array]') {
|
151
195
|
var res = [];
|
152
|
-
|
153
|
-
|
154
|
-
for (var j = 0; j < obj.matchedWords.length; ++j) {
|
155
|
-
var word = obj.matchedWords[j];
|
156
|
-
if (!(word in foundWords)) {
|
157
|
-
foundWords[word] = 1;
|
158
|
-
match = true;
|
159
|
-
}
|
160
|
-
}
|
161
|
-
if (match) {
|
162
|
-
res.push(obj.value);
|
163
|
-
}
|
164
|
-
} else if (Object.prototype.toString.call(obj) === '[object Array]') {
|
165
|
-
for (var i = 0; i < obj.length; ++i) {
|
166
|
-
var array = _getHitExplanationForOneAttr_recurse(obj[i], foundWords);
|
167
|
-
res = res.concat(array);
|
168
|
-
}
|
169
|
-
} else if (typeof obj === 'object') {
|
170
|
-
for (var prop in obj) {
|
171
|
-
if (obj.hasOwnProperty(prop)){
|
172
|
-
res = res.concat(_getHitExplanationForOneAttr_recurse(obj[prop], foundWords));
|
173
|
-
}
|
174
|
-
}
|
196
|
+
for (var j = 0; j < obj.length; ++j) {
|
197
|
+
res = res.concat(_getHitExplanationForOneAttr(obj[j], foundWords, array.slice(i).join('.')));
|
175
198
|
}
|
176
199
|
return res;
|
200
|
+
}
|
201
|
+
if (array[i] in obj) {
|
202
|
+
obj = obj[array[i]];
|
203
|
+
} else {
|
204
|
+
return [];
|
205
|
+
}
|
206
|
+
}
|
207
|
+
return _getHitExplanationForOneAttr_recurse(obj, foundWords);
|
208
|
+
}
|
209
|
+
|
210
|
+
var res = {};
|
211
|
+
var foundWords = {};
|
212
|
+
var title = _getHitExplanationForOneAttr(hit, foundWords, titleAttribute);
|
213
|
+
res.title = (title.length > 0) ? title[0] : '';
|
214
|
+
res.subtitles = [];
|
215
|
+
|
216
|
+
if (typeof otherAttributes !== 'undefined') {
|
217
|
+
for (var i = 0; i < otherAttributes.length; ++i) {
|
218
|
+
var attr = _getHitExplanationForOneAttr(hit, foundWords, otherAttributes[i]);
|
219
|
+
for (var j = 0; j < attr.length; ++j) {
|
220
|
+
res.subtitles.push({ attr: otherAttributes[i], value: attr[j] });
|
221
|
+
}
|
177
222
|
}
|
223
|
+
}
|
224
|
+
return res;
|
225
|
+
}
|
178
226
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
227
|
+
|
228
|
+
AlgoliaSearch.prototype = {
|
229
|
+
/*
|
230
|
+
* Delete an index
|
231
|
+
*
|
232
|
+
* @param indexName the name of index to delete
|
233
|
+
* @param callback the result callback with two arguments
|
234
|
+
* success: boolean set to true if the request was successfull
|
235
|
+
* content: the server answer that contains the task ID
|
236
|
+
*/
|
237
|
+
deleteIndex: function(indexName, callback) {
|
238
|
+
return this._jsonRequest({ method: 'DELETE',
|
239
|
+
url: '/1/indexes/' + encodeURIComponent(indexName),
|
240
|
+
callback: callback });
|
241
|
+
},
|
242
|
+
/**
|
243
|
+
* Move an existing index.
|
244
|
+
* @param srcIndexName the name of index to copy.
|
245
|
+
* @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
|
246
|
+
* @param callback the result callback with two arguments
|
247
|
+
* success: boolean set to true if the request was successfull
|
248
|
+
* content: the server answer that contains the task ID
|
249
|
+
*/
|
250
|
+
moveIndex: function(srcIndexName, dstIndexName, callback) {
|
251
|
+
var postObj = {operation: 'move', destination: dstIndexName};
|
252
|
+
return this._jsonRequest({ method: 'POST',
|
253
|
+
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
|
254
|
+
body: postObj,
|
255
|
+
callback: callback });
|
256
|
+
|
257
|
+
},
|
258
|
+
/**
|
259
|
+
* Copy an existing index.
|
260
|
+
* @param srcIndexName the name of index to copy.
|
261
|
+
* @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
|
262
|
+
* @param callback the result callback with two arguments
|
263
|
+
* success: boolean set to true if the request was successfull
|
264
|
+
* content: the server answer that contains the task ID
|
265
|
+
*/
|
266
|
+
copyIndex: function(srcIndexName, dstIndexName, callback) {
|
267
|
+
var postObj = {operation: 'copy', destination: dstIndexName};
|
268
|
+
return this._jsonRequest({ method: 'POST',
|
269
|
+
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
|
270
|
+
body: postObj,
|
271
|
+
callback: callback });
|
272
|
+
},
|
273
|
+
/**
|
274
|
+
* Return last log entries.
|
275
|
+
* @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
|
276
|
+
* @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
|
277
|
+
* @param callback the result callback with two arguments
|
278
|
+
* success: boolean set to true if the request was successfull
|
279
|
+
* content: the server answer that contains the task ID
|
280
|
+
*/
|
281
|
+
getLogs: function(callback, offset, length) {
|
282
|
+
if (this._isUndefined(offset)) {
|
283
|
+
offset = 0;
|
284
|
+
}
|
285
|
+
if (this._isUndefined(length)) {
|
286
|
+
length = 10;
|
204
287
|
}
|
205
288
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
289
|
+
return this._jsonRequest({ method: 'GET',
|
290
|
+
url: '/1/logs?offset=' + offset + '&length=' + length,
|
291
|
+
callback: callback });
|
292
|
+
},
|
293
|
+
/*
|
294
|
+
* List all existing indexes (paginated)
|
295
|
+
*
|
296
|
+
* @param callback the result callback with two arguments
|
297
|
+
* success: boolean set to true if the request was successfull
|
298
|
+
* content: the server answer with index list or error description if success is false.
|
299
|
+
* @param page The page to retrieve, starting at 0.
|
300
|
+
*/
|
301
|
+
listIndexes: function(callback, page) {
|
302
|
+
var params = typeof page !== 'undefined' ? '?page=' + page : '';
|
303
|
+
return this._jsonRequest({ method: 'GET',
|
304
|
+
url: '/1/indexes' + params,
|
305
|
+
callback: callback });
|
306
|
+
},
|
307
|
+
|
308
|
+
/*
|
309
|
+
* Get the index object initialized
|
310
|
+
*
|
311
|
+
* @param indexName the name of index
|
312
|
+
* @param callback the result callback with one argument (the Index instance)
|
313
|
+
*/
|
314
|
+
initIndex: function(indexName) {
|
315
|
+
return new this.Index(this, indexName);
|
316
|
+
},
|
317
|
+
/*
|
318
|
+
* List all existing user keys with their associated ACLs
|
319
|
+
*
|
320
|
+
* @param callback the result callback with two arguments
|
321
|
+
* success: boolean set to true if the request was successfull
|
322
|
+
* content: the server answer with user keys list or error description if success is false.
|
323
|
+
*/
|
324
|
+
listUserKeys: function(callback) {
|
325
|
+
return this._jsonRequest({ method: 'GET',
|
326
|
+
url: '/1/keys',
|
327
|
+
callback: callback });
|
328
|
+
},
|
329
|
+
/*
|
330
|
+
* Get ACL of a user key
|
331
|
+
*
|
332
|
+
* @param callback the result callback with two arguments
|
333
|
+
* success: boolean set to true if the request was successfull
|
334
|
+
* content: the server answer with user keys list or error description if success is false.
|
335
|
+
*/
|
336
|
+
getUserKeyACL: function(key, callback) {
|
337
|
+
return this._jsonRequest({ method: 'GET',
|
338
|
+
url: '/1/keys/' + key,
|
339
|
+
callback: callback });
|
340
|
+
},
|
341
|
+
/*
|
342
|
+
* Delete an existing user key
|
343
|
+
*
|
344
|
+
* @param callback the result callback with two arguments
|
345
|
+
* success: boolean set to true if the request was successfull
|
346
|
+
* content: the server answer with user keys list or error description if success is false.
|
347
|
+
*/
|
348
|
+
deleteUserKey: function(key, callback) {
|
349
|
+
return this._jsonRequest({ method: 'DELETE',
|
350
|
+
url: '/1/keys/' + key,
|
351
|
+
callback: callback });
|
352
|
+
},
|
353
|
+
/*
|
354
|
+
* Add an existing user key
|
355
|
+
*
|
356
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
357
|
+
* can contains the following values:
|
358
|
+
* - search: allow to search (https and http)
|
359
|
+
* - addObject: allows to add/update an object in the index (https only)
|
360
|
+
* - deleteObject : allows to delete an existing object (https only)
|
361
|
+
* - deleteIndex : allows to delete index content (https only)
|
362
|
+
* - settings : allows to get index settings (https only)
|
363
|
+
* - editSettings : allows to change index settings (https only)
|
364
|
+
* @param callback the result callback with two arguments
|
365
|
+
* success: boolean set to true if the request was successfull
|
366
|
+
* content: the server answer with user keys list or error description if success is false.
|
367
|
+
*/
|
368
|
+
addUserKey: function(acls, callback) {
|
369
|
+
return this.addUserKeyWithValidity(acls, 0, 0, 0, callback);
|
370
|
+
},
|
371
|
+
/*
|
372
|
+
* Add an existing user key
|
373
|
+
*
|
374
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
375
|
+
* can contains the following values:
|
376
|
+
* - search: allow to search (https and http)
|
377
|
+
* - addObject: allows to add/update an object in the index (https only)
|
378
|
+
* - deleteObject : allows to delete an existing object (https only)
|
379
|
+
* - deleteIndex : allows to delete index content (https only)
|
380
|
+
* - settings : allows to get index settings (https only)
|
381
|
+
* - editSettings : allows to change index settings (https only)
|
382
|
+
* @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
|
383
|
+
* @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
|
384
|
+
* @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
|
385
|
+
* @param callback the result callback with two arguments
|
386
|
+
* success: boolean set to true if the request was successfull
|
387
|
+
* content: the server answer with user keys list or error description if success is false.
|
388
|
+
*/
|
389
|
+
addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
|
390
|
+
var aclsObject = {};
|
391
|
+
aclsObject.acl = acls;
|
392
|
+
aclsObject.validity = validity;
|
393
|
+
aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
|
394
|
+
aclsObject.maxHitsPerQuery = maxHitsPerQuery;
|
395
|
+
return this._jsonRequest({ method: 'POST',
|
396
|
+
url: '/1/keys',
|
397
|
+
body: aclsObject,
|
398
|
+
callback: callback });
|
399
|
+
},
|
211
400
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
401
|
+
/**
|
402
|
+
* Set the extra security tagFilters header
|
403
|
+
* @param {string|array} tags The list of tags defining the current security filters
|
404
|
+
*/
|
405
|
+
setSecurityTags: function(tags) {
|
406
|
+
if (Object.prototype.toString.call(tags) === '[object Array]') {
|
407
|
+
var strTags = [];
|
408
|
+
for (var i = 0; i < tags.length; ++i) {
|
409
|
+
if (Object.prototype.toString.call(tags[i]) === '[object Array]') {
|
410
|
+
var oredTags = [];
|
411
|
+
for (var j = 0; j < tags[i].length; ++j) {
|
412
|
+
oredTags.push(tags[i][j]);
|
413
|
+
}
|
414
|
+
strTags.push('(' + oredTags.join(',') + ')');
|
415
|
+
} else {
|
416
|
+
strTags.push(tags[i]);
|
218
417
|
}
|
418
|
+
}
|
419
|
+
tags = strTags.join(',');
|
219
420
|
}
|
220
|
-
|
221
|
-
}
|
421
|
+
this.tagFilters = tags;
|
422
|
+
},
|
222
423
|
|
424
|
+
/**
|
425
|
+
* Set the extra user token header
|
426
|
+
* @param {string} userToken The token identifying a uniq user (used to apply rate limits)
|
427
|
+
*/
|
428
|
+
setUserToken: function(userToken) {
|
429
|
+
this.userToken = userToken;
|
430
|
+
},
|
223
431
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
432
|
+
/*
|
433
|
+
* Initialize a new batch of search queries
|
434
|
+
*/
|
435
|
+
startQueriesBatch: function() {
|
436
|
+
this.batch = [];
|
437
|
+
},
|
438
|
+
/*
|
439
|
+
* Add a search query in the batch
|
440
|
+
*
|
441
|
+
* @param query the full text query
|
442
|
+
* @param args (optional) if set, contains an object with query parameters:
|
443
|
+
* - attributes: an array of object attribute names to retrieve
|
444
|
+
* (if not set all attributes are retrieve)
|
445
|
+
* - attributesToHighlight: an array of object attribute names to highlight
|
446
|
+
* (if not set indexed attributes are highlighted)
|
447
|
+
* - minWordSizefor1Typo: the minimum number of characters to accept one typo.
|
448
|
+
* Defaults to 3.
|
449
|
+
* - minWordSizefor2Typos: the minimum number of characters to accept two typos.
|
450
|
+
* Defaults to 7.
|
451
|
+
* - getRankingInfo: if set, the result hits will contain ranking information in
|
452
|
+
* _rankingInfo attribute
|
453
|
+
* - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
|
454
|
+
* - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
|
455
|
+
*/
|
456
|
+
addQueryInBatch: function(indexName, query, args) {
|
457
|
+
var params = 'query=' + encodeURIComponent(query);
|
458
|
+
if (!this._isUndefined(args) && args !== null) {
|
459
|
+
params = this._getSearchParams(args, params);
|
460
|
+
}
|
461
|
+
this.batch.push({ indexName: indexName, params: params });
|
462
|
+
},
|
463
|
+
/*
|
464
|
+
* Clear all queries in cache
|
465
|
+
*/
|
466
|
+
clearCache: function() {
|
467
|
+
this.cache = {};
|
468
|
+
},
|
469
|
+
/*
|
470
|
+
* Launch the batch of queries using XMLHttpRequest.
|
471
|
+
* (Optimized for browser using a POST query to minimize number of OPTIONS queries)
|
472
|
+
*
|
473
|
+
* @param callback the function that will receive results
|
474
|
+
* @param delay (optional) if set, wait for this delay (in ms) and only send the batch if there was no other in the meantime.
|
475
|
+
*/
|
476
|
+
sendQueriesBatch: function(callback, delay) {
|
477
|
+
var as = this;
|
478
|
+
var params = {requests: []};
|
479
|
+
for (var i = 0; i < as.batch.length; ++i) {
|
480
|
+
params.requests.push(as.batch[i]);
|
481
|
+
}
|
482
|
+
window.clearTimeout(as.onDelayTrigger);
|
483
|
+
if (!this._isUndefined(delay) && delay !== null && delay > 0) {
|
484
|
+
var onDelayTrigger = window.setTimeout( function() {
|
485
|
+
as._sendQueriesBatch(params, callback);
|
486
|
+
}, delay);
|
487
|
+
as.onDelayTrigger = onDelayTrigger;
|
488
|
+
} else {
|
489
|
+
return this._sendQueriesBatch(params, callback);
|
490
|
+
}
|
491
|
+
},
|
252
492
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
return this._jsonRequest({ method: 'POST',
|
265
|
-
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
|
266
|
-
body: postObj,
|
267
|
-
callback: callback });
|
268
|
-
},
|
269
|
-
/**
|
270
|
-
* Return last log entries.
|
271
|
-
* @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
|
272
|
-
* @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
|
273
|
-
* @param callback the result callback with two arguments
|
274
|
-
* success: boolean set to true if the request was successfull
|
275
|
-
* content: the server answer that contains the task ID
|
276
|
-
*/
|
277
|
-
getLogs: function(callback, offset, length) {
|
278
|
-
if (this._isUndefined(offset)) {
|
279
|
-
offset = 0;
|
280
|
-
}
|
281
|
-
if (this._isUndefined(length)) {
|
282
|
-
length = 10;
|
283
|
-
}
|
493
|
+
/**
|
494
|
+
* Set the number of milliseconds a request can take before automatically being terminated.
|
495
|
+
*
|
496
|
+
* @param {Number} milliseconds
|
497
|
+
*/
|
498
|
+
setRequestTimeout: function(milliseconds)
|
499
|
+
{
|
500
|
+
if (milliseconds) {
|
501
|
+
this.requestTimeoutInMs = parseInt(milliseconds, 10);
|
502
|
+
}
|
503
|
+
},
|
284
504
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
* content: the server answer with index list or error description if success is false.
|
295
|
-
* @param page The page to retrieve, starting at 0.
|
296
|
-
*/
|
297
|
-
listIndexes: function(callback, page) {
|
298
|
-
var params = typeof page !== 'undefined' ? '?page=' + page : '';
|
299
|
-
return this._jsonRequest({ method: 'GET',
|
300
|
-
url: '/1/indexes' + params,
|
301
|
-
callback: callback });
|
302
|
-
},
|
505
|
+
/*
|
506
|
+
* Index class constructor.
|
507
|
+
* You should not use this method directly but use initIndex() function
|
508
|
+
*/
|
509
|
+
Index: function(algoliasearch, indexName) {
|
510
|
+
this.indexName = indexName;
|
511
|
+
this.as = algoliasearch;
|
512
|
+
this.typeAheadArgs = null;
|
513
|
+
this.typeAheadValueOption = null;
|
303
514
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
* @param callback the result callback with two arguments
|
317
|
-
* success: boolean set to true if the request was successfull
|
318
|
-
* content: the server answer with user keys list or error description if success is false.
|
319
|
-
*/
|
320
|
-
listUserKeys: function(callback) {
|
321
|
-
return this._jsonRequest({ method: 'GET',
|
322
|
-
url: '/1/keys',
|
323
|
-
callback: callback });
|
324
|
-
},
|
325
|
-
/*
|
326
|
-
* Get ACL of a user key
|
327
|
-
*
|
328
|
-
* @param callback the result callback with two arguments
|
329
|
-
* success: boolean set to true if the request was successfull
|
330
|
-
* content: the server answer with user keys list or error description if success is false.
|
331
|
-
*/
|
332
|
-
getUserKeyACL: function(key, callback) {
|
333
|
-
return this._jsonRequest({ method: 'GET',
|
334
|
-
url: '/1/keys/' + key,
|
335
|
-
callback: callback });
|
336
|
-
},
|
337
|
-
/*
|
338
|
-
* Delete an existing user key
|
339
|
-
*
|
340
|
-
* @param callback the result callback with two arguments
|
341
|
-
* success: boolean set to true if the request was successfull
|
342
|
-
* content: the server answer with user keys list or error description if success is false.
|
343
|
-
*/
|
344
|
-
deleteUserKey: function(key, callback) {
|
345
|
-
return this._jsonRequest({ method: 'DELETE',
|
346
|
-
url: '/1/keys/' + key,
|
347
|
-
callback: callback });
|
348
|
-
},
|
349
|
-
/*
|
350
|
-
* Add an existing user key
|
351
|
-
*
|
352
|
-
* @param acls the list of ACL for this key. Defined by an array of strings that
|
353
|
-
* can contains the following values:
|
354
|
-
* - search: allow to search (https and http)
|
355
|
-
* - addObject: allows to add/update an object in the index (https only)
|
356
|
-
* - deleteObject : allows to delete an existing object (https only)
|
357
|
-
* - deleteIndex : allows to delete index content (https only)
|
358
|
-
* - settings : allows to get index settings (https only)
|
359
|
-
* - editSettings : allows to change index settings (https only)
|
360
|
-
* @param callback the result callback with two arguments
|
361
|
-
* success: boolean set to true if the request was successfull
|
362
|
-
* content: the server answer with user keys list or error description if success is false.
|
363
|
-
*/
|
364
|
-
addUserKey: function(acls, callback) {
|
365
|
-
var aclsObject = {};
|
366
|
-
aclsObject.acl = acls;
|
367
|
-
return this._jsonRequest({ method: 'POST',
|
368
|
-
url: '/1/keys',
|
369
|
-
body: aclsObject,
|
370
|
-
callback: callback });
|
371
|
-
},
|
372
|
-
/*
|
373
|
-
* Add an existing user key
|
374
|
-
*
|
375
|
-
* @param acls the list of ACL for this key. Defined by an array of strings that
|
376
|
-
* can contains the following values:
|
377
|
-
* - search: allow to search (https and http)
|
378
|
-
* - addObject: allows to add/update an object in the index (https only)
|
379
|
-
* - deleteObject : allows to delete an existing object (https only)
|
380
|
-
* - deleteIndex : allows to delete index content (https only)
|
381
|
-
* - settings : allows to get index settings (https only)
|
382
|
-
* - editSettings : allows to change index settings (https only)
|
383
|
-
* @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
|
384
|
-
* @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
|
385
|
-
* @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
|
386
|
-
* @param callback the result callback with two arguments
|
387
|
-
* success: boolean set to true if the request was successfull
|
388
|
-
* content: the server answer with user keys list or error description if success is false.
|
389
|
-
*/
|
390
|
-
addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
|
391
|
-
var indexObj = this;
|
392
|
-
var aclsObject = {};
|
393
|
-
aclsObject.acl = acls;
|
394
|
-
aclsObject.validity = validity;
|
395
|
-
aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
|
396
|
-
aclsObject.maxHitsPerQuery = maxHitsPerQuery;
|
397
|
-
return this._jsonRequest({ method: 'POST',
|
398
|
-
url: '/1/indexes/' + indexObj.indexName + '/keys',
|
399
|
-
body: aclsObject,
|
400
|
-
callback: callback });
|
401
|
-
},
|
515
|
+
// make sure every index instance has it's own cache
|
516
|
+
this.cache = {};
|
517
|
+
},
|
518
|
+
/**
|
519
|
+
* Add an extra field to the HTTP request
|
520
|
+
*
|
521
|
+
* @param key the header field name
|
522
|
+
* @param value the header field value
|
523
|
+
*/
|
524
|
+
setExtraHeader: function(key, value) {
|
525
|
+
this.extraHeaders.push({ key: key, value: value});
|
526
|
+
},
|
402
527
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
}
|
420
|
-
}
|
421
|
-
tags = strTags.join(',');
|
528
|
+
_sendQueriesBatch: function(params, callback) {
|
529
|
+
if (this.jsonp === null) {
|
530
|
+
var self = this;
|
531
|
+
return this._jsonRequest({ cache: this.cache,
|
532
|
+
method: 'POST',
|
533
|
+
url: '/1/indexes/*/queries',
|
534
|
+
body: params,
|
535
|
+
callback: function(success, content) {
|
536
|
+
if (!success) {
|
537
|
+
// retry first with JSONP
|
538
|
+
self.jsonp = true;
|
539
|
+
self._sendQueriesBatch(params, callback);
|
540
|
+
} else {
|
541
|
+
self.jsonp = false;
|
542
|
+
callback && callback(success, content);
|
543
|
+
}
|
422
544
|
}
|
423
|
-
|
424
|
-
}
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
545
|
+
});
|
546
|
+
} else if (this.jsonp) {
|
547
|
+
var jsonpParams = '';
|
548
|
+
for (var i = 0; i < params.requests.length; ++i) {
|
549
|
+
var q = '/1/indexes/' + encodeURIComponent(params.requests[i].indexName) + '?' + params.requests[i].params;
|
550
|
+
jsonpParams += i + '=' + encodeURIComponent(q) + '&';
|
551
|
+
}
|
552
|
+
var pObj = {params: jsonpParams};
|
553
|
+
return this._jsonRequest({ cache: this.cache,
|
554
|
+
method: 'GET',
|
555
|
+
url: '/1/indexes/*',
|
556
|
+
body: pObj,
|
557
|
+
callback: callback });
|
558
|
+
} else {
|
559
|
+
return this._jsonRequest({ cache: this.cache,
|
560
|
+
method: 'POST',
|
561
|
+
url: '/1/indexes/*/queries',
|
562
|
+
body: params,
|
563
|
+
callback: callback});
|
564
|
+
}
|
565
|
+
},
|
566
|
+
/*
|
567
|
+
* Wrapper that try all hosts to maximize the quality of service
|
568
|
+
*/
|
569
|
+
_jsonRequest: function(opts) {
|
570
|
+
var self = this;
|
571
|
+
var callback = opts.callback;
|
572
|
+
var cache = null;
|
573
|
+
var cacheID = opts.url;
|
574
|
+
var deferred = null;
|
575
|
+
if (this.options.jQuery) {
|
576
|
+
deferred = this.options.jQuery.$.Deferred();
|
577
|
+
deferred.promise = deferred.promise(); // promise is a property in angular
|
578
|
+
} else if (this.options.angular) {
|
579
|
+
deferred = this.options.angular.$q.defer();
|
580
|
+
}
|
433
581
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
*
|
443
|
-
* @param query the full text query
|
444
|
-
* @param args (optional) if set, contains an object with query parameters:
|
445
|
-
* - attributes: an array of object attribute names to retrieve
|
446
|
-
* (if not set all attributes are retrieve)
|
447
|
-
* - attributesToHighlight: an array of object attribute names to highlight
|
448
|
-
* (if not set indexed attributes are highlighted)
|
449
|
-
* - minWordSizefor1Typo: the minimum number of characters to accept one typo.
|
450
|
-
* Defaults to 3.
|
451
|
-
* - minWordSizefor2Typos: the minimum number of characters to accept two typos.
|
452
|
-
* Defaults to 7.
|
453
|
-
* - getRankingInfo: if set, the result hits will contain ranking information in
|
454
|
-
* _rankingInfo attribute
|
455
|
-
* - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
|
456
|
-
* - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
|
457
|
-
*/
|
458
|
-
addQueryInBatch: function(indexName, query, args) {
|
459
|
-
var params = 'query=' + encodeURIComponent(query);
|
460
|
-
if (!this._isUndefined(args) && args !== null) {
|
461
|
-
params = this._getSearchParams(args, params);
|
582
|
+
if (!this._isUndefined(opts.body)) {
|
583
|
+
cacheID = opts.url + '_body_' + JSON.stringify(opts.body);
|
584
|
+
}
|
585
|
+
if (!this._isUndefined(opts.cache)) {
|
586
|
+
cache = opts.cache;
|
587
|
+
if (!this._isUndefined(cache[cacheID])) {
|
588
|
+
if (!this._isUndefined(callback) && callback) {
|
589
|
+
setTimeout(function () { callback(true, cache[cacheID]); }, 1);
|
462
590
|
}
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
* @param callback the function that will receive results
|
476
|
-
* @param delay (optional) if set, wait for this delay (in ms) and only send the batch if there was no other in the meantime.
|
477
|
-
*/
|
478
|
-
sendQueriesBatch: function(callback, delay) {
|
479
|
-
var as = this;
|
480
|
-
var params = {requests: []};
|
481
|
-
for (var i = 0; i < as.batch.length; ++i) {
|
482
|
-
params.requests.push(as.batch[i]);
|
591
|
+
deferred && deferred.resolve(cache[cacheID]);
|
592
|
+
return deferred && deferred.promise;
|
593
|
+
}
|
594
|
+
}
|
595
|
+
|
596
|
+
opts.successiveRetryCount = 0;
|
597
|
+
var impl = function() {
|
598
|
+
if (opts.successiveRetryCount >= self.hosts.length) {
|
599
|
+
var error = { message: 'Cannot connect the Algolia\'s Search API. Please send an email to support@algolia.com to report the issue.' };
|
600
|
+
if (!self._isUndefined(callback) && callback) {
|
601
|
+
opts.successiveRetryCount = 0;
|
602
|
+
callback(false, error);
|
483
603
|
}
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
604
|
+
deferred && deferred.reject(error);
|
605
|
+
return;
|
606
|
+
}
|
607
|
+
opts.callback = function(retry, success, body) {
|
608
|
+
if (success && !self._isUndefined(opts.cache)) {
|
609
|
+
cache[cacheID] = body;
|
610
|
+
}
|
611
|
+
if (!success && retry) {
|
612
|
+
self.currentHostIndex = ++self.currentHostIndex % self.hosts.length;
|
613
|
+
opts.successiveRetryCount += 1;
|
614
|
+
impl();
|
490
615
|
} else {
|
491
|
-
|
616
|
+
opts.successiveRetryCount = 0;
|
617
|
+
deferred && (success ? deferred.resolve(body) : deferred.reject(body));
|
618
|
+
if (!self._isUndefined(callback) && callback) {
|
619
|
+
callback(success, body);
|
620
|
+
}
|
492
621
|
}
|
493
|
-
|
622
|
+
};
|
623
|
+
opts.hostname = self.hosts[self.currentHostIndex];
|
624
|
+
self._jsonRequestByHost(opts);
|
625
|
+
};
|
626
|
+
impl();
|
494
627
|
|
495
|
-
|
496
|
-
|
497
|
-
*
|
498
|
-
* @param {Number} milliseconds
|
499
|
-
*/
|
500
|
-
setRequestTimeout: function(milliseconds)
|
501
|
-
{
|
502
|
-
if (milliseconds) {
|
503
|
-
this.requestTimeoutInMs = parseInt(milliseconds, 10);
|
504
|
-
}
|
505
|
-
},
|
628
|
+
return deferred && deferred.promise;
|
629
|
+
},
|
506
630
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
}
|
520
|
-
|
521
|
-
* Add an extra field to the HTTP request
|
522
|
-
*
|
523
|
-
* @param key the header field name
|
524
|
-
* @param value the header field value
|
525
|
-
*/
|
526
|
-
setExtraHeader: function(key, value) {
|
527
|
-
this.extraHeaders.push({ key: key, value: value});
|
528
|
-
},
|
631
|
+
_jsonRequestByHost: function(opts) {
|
632
|
+
var self = this;
|
633
|
+
var url = opts.hostname + opts.url;
|
634
|
+
|
635
|
+
if (this.jsonp) {
|
636
|
+
this._makeJsonpRequestByHost(url, opts);
|
637
|
+
} else if (this.options.jQuery) {
|
638
|
+
this._makejQueryRequestByHost(url, opts);
|
639
|
+
} else if (this.options.angular) {
|
640
|
+
this._makeAngularRequestByHost(url, opts);
|
641
|
+
} else {
|
642
|
+
this._makeXmlHttpRequestByHost(url, opts);
|
643
|
+
}
|
644
|
+
},
|
529
645
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
if (!success) {
|
540
|
-
// retry first with JSONP
|
541
|
-
self.jsonp = true;
|
542
|
-
self._sendQueriesBatch(params, callback);
|
543
|
-
} else {
|
544
|
-
self.jsonp = false;
|
545
|
-
callback && callback(success, content);
|
546
|
-
}
|
547
|
-
}
|
548
|
-
});
|
549
|
-
} else if (this.jsonp) {
|
550
|
-
var jsonpParams = '';
|
551
|
-
for (var i = 0; i < params.requests.length; ++i) {
|
552
|
-
var q = '/1/indexes/' + encodeURIComponent(params.requests[i].indexName) + '?' + params.requests[i].params;
|
553
|
-
jsonpParams += i + '=' + encodeURIComponent(q) + '&';
|
554
|
-
}
|
555
|
-
var pObj = {params: jsonpParams};
|
556
|
-
return this._jsonRequest({ cache: this.cache,
|
557
|
-
method: 'GET',
|
558
|
-
url: '/1/indexes/*',
|
559
|
-
body: pObj,
|
560
|
-
callback: callback });
|
561
|
-
} else {
|
562
|
-
return this._jsonRequest({ cache: this.cache,
|
563
|
-
method: 'POST',
|
564
|
-
url: '/1/indexes/*/queries',
|
565
|
-
body: params,
|
566
|
-
callback: callback});
|
567
|
-
}
|
568
|
-
},
|
569
|
-
/*
|
570
|
-
* Wrapper that try all hosts to maximize the quality of service
|
571
|
-
*/
|
572
|
-
_jsonRequest: function(opts) {
|
573
|
-
var self = this;
|
574
|
-
var callback = opts.callback;
|
575
|
-
var cache = null;
|
576
|
-
var cacheID = opts.url;
|
577
|
-
var deferred = null;
|
578
|
-
if (this.options.jQuery) {
|
579
|
-
deferred = this.options.jQuery.$.Deferred();
|
580
|
-
deferred.promise = deferred.promise(); // promise is a property in angular
|
581
|
-
} else if (this.options.angular) {
|
582
|
-
deferred = this.options.angular.$q.defer();
|
583
|
-
}
|
646
|
+
/**
|
647
|
+
* Make a $http
|
648
|
+
*
|
649
|
+
* @param url request url (includes endpoint and path)
|
650
|
+
* @param opts all request opts
|
651
|
+
*/
|
652
|
+
_makeAngularRequestByHost: function(url, opts) {
|
653
|
+
var self = this;
|
654
|
+
var body = null;
|
584
655
|
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
if (!this._isUndefined(opts.cache)) {
|
589
|
-
cache = opts.cache;
|
590
|
-
if (!this._isUndefined(cache[cacheID])) {
|
591
|
-
if (!this._isUndefined(callback) && callback) {
|
592
|
-
setTimeout(function () { callback(true, cache[cacheID]); }, 1);
|
593
|
-
}
|
594
|
-
deferred && deferred.resolve(cache[cacheID]);
|
595
|
-
return deferred && deferred.promise;
|
596
|
-
}
|
597
|
-
}
|
656
|
+
if (!this._isUndefined(opts.body)) {
|
657
|
+
body = JSON.stringify(opts.body);
|
658
|
+
}
|
598
659
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
660
|
+
url += ((url.indexOf('?') === -1) ? '?' : '&') + 'X-Algolia-API-Key=' + this.apiKey;
|
661
|
+
url += '&X-Algolia-Application-Id=' + this.applicationID;
|
662
|
+
if (this.userToken) {
|
663
|
+
url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
|
664
|
+
}
|
665
|
+
if (this.tagFilters) {
|
666
|
+
url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
|
667
|
+
}
|
668
|
+
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
669
|
+
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
670
|
+
}
|
671
|
+
this.options.angular.$http({
|
672
|
+
url: url,
|
673
|
+
method: opts.method,
|
674
|
+
data: body,
|
675
|
+
cache: false,
|
676
|
+
timeout: (this.requestTimeoutInMs * (opts.successiveRetryCount + 1))
|
677
|
+
}).then(function(response) {
|
678
|
+
opts.callback(false, true, response.data);
|
679
|
+
}, function(response) {
|
680
|
+
if (response.status === 0) {
|
681
|
+
// xhr.timeout is not handled by Angular.js right now
|
682
|
+
// let's retry
|
683
|
+
opts.callback(true, false, response.data);
|
684
|
+
} else if (response.status == 400 || response.status === 403 || response.status === 404) {
|
685
|
+
opts.callback(false, false, response.data);
|
686
|
+
} else {
|
687
|
+
opts.callback(true, false, response.data);
|
688
|
+
}
|
689
|
+
});
|
690
|
+
},
|
630
691
|
|
631
|
-
|
632
|
-
|
692
|
+
/**
|
693
|
+
* Make a $.ajax
|
694
|
+
*
|
695
|
+
* @param url request url (includes endpoint and path)
|
696
|
+
* @param opts all request opts
|
697
|
+
*/
|
698
|
+
_makejQueryRequestByHost: function(url, opts) {
|
699
|
+
var self = this;
|
700
|
+
var body = null;
|
633
701
|
|
634
|
-
|
635
|
-
|
636
|
-
|
702
|
+
if (!this._isUndefined(opts.body)) {
|
703
|
+
body = JSON.stringify(opts.body);
|
704
|
+
}
|
637
705
|
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
706
|
+
url += ((url.indexOf('?') === -1) ? '?' : '&') + 'X-Algolia-API-Key=' + this.apiKey;
|
707
|
+
url += '&X-Algolia-Application-Id=' + this.applicationID;
|
708
|
+
if (this.userToken) {
|
709
|
+
url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
|
710
|
+
}
|
711
|
+
if (this.tagFilters) {
|
712
|
+
url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
|
713
|
+
}
|
714
|
+
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
715
|
+
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
716
|
+
}
|
717
|
+
this.options.jQuery.$.ajax(url, {
|
718
|
+
type: opts.method,
|
719
|
+
timeout: (this.requestTimeoutInMs * (opts.successiveRetryCount + 1)),
|
720
|
+
dataType: 'json',
|
721
|
+
data: body,
|
722
|
+
error: function(xhr, textStatus, error) {
|
723
|
+
if (textStatus === 'timeout') {
|
724
|
+
opts.callback(true, false, { 'message': 'Timeout - Could not connect to endpoint ' + url } );
|
725
|
+
} else if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404) {
|
726
|
+
opts.callback(false, false, xhr.responseJSON );
|
644
727
|
} else {
|
645
|
-
|
728
|
+
opts.callback(true, false, { 'message': error } );
|
646
729
|
}
|
647
|
-
|
730
|
+
},
|
731
|
+
success: function(data, textStatus, xhr) {
|
732
|
+
opts.callback(false, true, data);
|
733
|
+
}
|
734
|
+
});
|
735
|
+
},
|
648
736
|
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
737
|
+
/**
|
738
|
+
* Make a JSONP request
|
739
|
+
*
|
740
|
+
* @param url request url (includes endpoint and path)
|
741
|
+
* @param opts all request options
|
742
|
+
*/
|
743
|
+
_makeJsonpRequestByHost: function(url, opts) {
|
744
|
+
if (opts.method !== 'GET') {
|
745
|
+
opts.callback(true, false, { 'message': 'Method ' + opts.method + ' ' + url + ' is not supported by JSONP.' });
|
746
|
+
return;
|
747
|
+
}
|
658
748
|
|
659
|
-
|
660
|
-
|
661
|
-
}
|
749
|
+
var cbCalled = false;
|
750
|
+
var timedOut = false;
|
662
751
|
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
672
|
-
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
673
|
-
}
|
674
|
-
this.options.angular.$http({
|
675
|
-
url: url,
|
676
|
-
method: opts.method,
|
677
|
-
data: body,
|
678
|
-
cache: false,
|
679
|
-
timeout: (this.requestTimeoutInMs * (opts.successiveRetryCount + 1))
|
680
|
-
}).then(function(response) {
|
681
|
-
opts.callback(false, true, null, response.data);
|
682
|
-
}, function(response) {
|
683
|
-
if (response.status === 0) {
|
684
|
-
// xhr.timeout is not handled by Angular.js right now
|
685
|
-
// let's retry
|
686
|
-
opts.callback(true, false, null, response.data);
|
687
|
-
} else if (response.status == 400 || response.status === 403 || response.status === 404) {
|
688
|
-
opts.callback(false, false, null, response.data);
|
689
|
-
} else {
|
690
|
-
opts.callback(true, false, null, response.data);
|
691
|
-
}
|
692
|
-
});
|
693
|
-
},
|
752
|
+
AlgoliaSearch.JSONPCounter += 1;
|
753
|
+
var head = document.getElementsByTagName('head')[0];
|
754
|
+
var script = document.createElement('script');
|
755
|
+
var cb = 'algoliaJSONP_' + AlgoliaSearch.JSONPCounter;
|
756
|
+
var done = false;
|
757
|
+
var ontimeout;
|
758
|
+
var success;
|
759
|
+
var clean;
|
694
760
|
|
695
|
-
|
696
|
-
|
697
|
-
*
|
698
|
-
* @param url request url (includes endpoint and path)
|
699
|
-
* @param opts all request opts
|
700
|
-
*/
|
701
|
-
_makejQueryRequestByHost: function(url, opts) {
|
702
|
-
var self = this;
|
703
|
-
var body = null;
|
761
|
+
window[cb] = function(data) {
|
762
|
+
try { delete window[cb]; } catch (e) { window[cb] = undefined; }
|
704
763
|
|
705
|
-
|
706
|
-
|
707
|
-
|
764
|
+
if (timedOut) {
|
765
|
+
return;
|
766
|
+
}
|
708
767
|
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
|
713
|
-
}
|
714
|
-
if (this.tagFilters) {
|
715
|
-
url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
|
716
|
-
}
|
717
|
-
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
718
|
-
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
719
|
-
}
|
720
|
-
this.options.jQuery.$.ajax(url, {
|
721
|
-
type: opts.method,
|
722
|
-
timeout: (this.requestTimeoutInMs * (opts.successiveRetryCount + 1)),
|
723
|
-
dataType: 'json',
|
724
|
-
data: body,
|
725
|
-
error: function(xhr, textStatus, error) {
|
726
|
-
if (textStatus === 'timeout') {
|
727
|
-
opts.callback(true, false, null, { 'message': 'Timeout - Could not connect to endpoint ' + url } );
|
728
|
-
} else if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404) {
|
729
|
-
opts.callback(false, false, null, xhr.responseJSON );
|
730
|
-
} else {
|
731
|
-
opts.callback(true, false, null, { 'message': error } );
|
732
|
-
}
|
733
|
-
},
|
734
|
-
success: function(data, textStatus, xhr) {
|
735
|
-
opts.callback(false, true, null, data);
|
736
|
-
}
|
737
|
-
});
|
738
|
-
},
|
768
|
+
var status =
|
769
|
+
data && data.message && data.status ||
|
770
|
+
data && 200;
|
739
771
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
*/
|
746
|
-
_makeJsonpRequestByHost: function(url, opts) {
|
747
|
-
if (opts.method !== 'GET') {
|
748
|
-
opts.callback(true, false, null, { 'message': 'Method ' + opts.method + ' ' + url + ' is not supported by JSONP.' });
|
749
|
-
return;
|
750
|
-
}
|
772
|
+
var ok = status === 200;
|
773
|
+
var retry = !ok && status !== 400 && status !== 403 && status !== 404;
|
774
|
+
cbCalled = true;
|
775
|
+
opts.callback(retry, ok, data);
|
776
|
+
};
|
751
777
|
|
752
|
-
|
753
|
-
|
754
|
-
var head = document.getElementsByTagName('head')[0];
|
755
|
-
var script = document.createElement('script');
|
756
|
-
var cb = 'algoliaJSONP_' + this.jsonpCounter;
|
757
|
-
var done = false;
|
758
|
-
var ontimeout = null;
|
759
|
-
|
760
|
-
window[cb] = function(data) {
|
761
|
-
opts.callback(false, true, null, data);
|
762
|
-
try { delete window[cb]; } catch (e) { window[cb] = undefined; }
|
763
|
-
};
|
778
|
+
script.type = 'text/javascript';
|
779
|
+
url += '?callback=' + cb + '&X-Algolia-Application-Id=' + this.applicationID + '&X-Algolia-API-Key=' + this.apiKey;
|
764
780
|
|
765
|
-
|
766
|
-
|
781
|
+
if (this.tagFilters) {
|
782
|
+
url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
|
783
|
+
}
|
767
784
|
|
768
|
-
|
769
|
-
|
770
|
-
|
785
|
+
if (this.userToken) {
|
786
|
+
url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
|
787
|
+
}
|
771
788
|
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
776
|
-
script.src += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
777
|
-
}
|
789
|
+
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
790
|
+
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
791
|
+
}
|
778
792
|
|
793
|
+
if (opts.body && opts.body.params) {
|
794
|
+
url += '&' + opts.body.params;
|
795
|
+
}
|
779
796
|
|
780
|
-
|
781
|
-
|
782
|
-
|
797
|
+
ontimeout = setTimeout(function() {
|
798
|
+
timedOut = true;
|
799
|
+
clean();
|
783
800
|
|
784
|
-
|
785
|
-
|
786
|
-
window[cb] = function(data) {
|
787
|
-
try { delete window[cb]; } catch (e) { window[cb] = undefined; }
|
788
|
-
};
|
801
|
+
opts.callback(true, false, { 'message': 'Timeout - Failed to load JSONP script.' });
|
802
|
+
}, this.requestTimeoutInMs);
|
789
803
|
|
790
|
-
|
791
|
-
|
804
|
+
success = function() {
|
805
|
+
if (done || timedOut) {
|
806
|
+
return;
|
807
|
+
}
|
792
808
|
|
793
|
-
|
794
|
-
|
809
|
+
done = true;
|
810
|
+
clean();
|
795
811
|
|
796
|
-
|
812
|
+
// script loaded but did not call the fn => script loading error
|
813
|
+
if (!cbCalled) {
|
814
|
+
opts.callback(true, false, { 'message': 'Failed to load JSONP script.' });
|
815
|
+
}
|
816
|
+
};
|
797
817
|
|
798
|
-
|
799
|
-
|
800
|
-
|
818
|
+
clean = function() {
|
819
|
+
clearTimeout(ontimeout);
|
820
|
+
script.onload = null;
|
821
|
+
script.onreadystatechange = null;
|
822
|
+
script.onerror = null;
|
823
|
+
head.removeChild(script);
|
801
824
|
|
802
|
-
|
803
|
-
|
825
|
+
try {
|
826
|
+
delete window[cb];
|
827
|
+
delete window[cb + '_loaded'];
|
828
|
+
} catch (e) {
|
829
|
+
window[cb] = null;
|
830
|
+
window[cb + '_loaded'] = null;
|
831
|
+
}
|
832
|
+
};
|
804
833
|
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
}
|
814
|
-
};
|
834
|
+
// script onreadystatechange needed only for
|
835
|
+
// <= IE8
|
836
|
+
// https://github.com/angular/angular.js/issues/4523
|
837
|
+
script.onreadystatechange = function() {
|
838
|
+
if (this.readyState === 'loaded' || this.readyState === 'complete') {
|
839
|
+
success();
|
840
|
+
}
|
841
|
+
};
|
815
842
|
|
816
|
-
|
817
|
-
|
818
|
-
|
843
|
+
script.onload = function() {
|
844
|
+
success();
|
845
|
+
};
|
819
846
|
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
847
|
+
script.onerror = function() {
|
848
|
+
if (done || timedOut) {
|
849
|
+
return;
|
850
|
+
}
|
824
851
|
|
825
|
-
|
826
|
-
|
852
|
+
clean();
|
853
|
+
opts.callback(true, false, { 'message': 'Failed to load JSONP script.' });
|
854
|
+
};
|
827
855
|
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
* @param url request url (includes endpoint and path)
|
832
|
-
* @param opts all request opts
|
833
|
-
*/
|
834
|
-
_makeXmlHttpRequestByHost: function(url, opts) {
|
835
|
-
var self = this;
|
836
|
-
var xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : {};
|
837
|
-
var body = null;
|
838
|
-
var ontimeout = null;
|
839
|
-
|
840
|
-
if (!this._isUndefined(opts.body)) {
|
841
|
-
body = JSON.stringify(opts.body);
|
842
|
-
}
|
856
|
+
script.async = true;
|
857
|
+
script.defer = true;
|
858
|
+
script.src = url;
|
843
859
|
|
844
|
-
|
845
|
-
|
846
|
-
if (this.userToken) {
|
847
|
-
url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
|
848
|
-
}
|
849
|
-
if (this.tagFilters) {
|
850
|
-
url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
|
851
|
-
}
|
852
|
-
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
853
|
-
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
854
|
-
}
|
855
|
-
if ('withCredentials' in xmlHttp) {
|
856
|
-
xmlHttp.open(opts.method, url, true);
|
857
|
-
xmlHttp.timeout = this.requestTimeoutInMs * (opts.successiveRetryCount + 1);
|
858
|
-
if (body !== null) {
|
859
|
-
/* This content type is specified to follow CORS 'simple header' directive */
|
860
|
-
xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
861
|
-
}
|
862
|
-
} else if (typeof XDomainRequest !== 'undefined') {
|
863
|
-
// Handle IE8/IE9
|
864
|
-
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
|
865
|
-
xmlHttp = new XDomainRequest();
|
866
|
-
xmlHttp.open(opts.method, url);
|
867
|
-
} else {
|
868
|
-
// very old browser, not supported
|
869
|
-
opts.callback(false, false, null, { 'message': 'CORS not supported' });
|
870
|
-
return;
|
871
|
-
}
|
860
|
+
head.appendChild(script);
|
861
|
+
},
|
872
862
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
863
|
+
/**
|
864
|
+
* Make a XmlHttpRequest
|
865
|
+
*
|
866
|
+
* @param url request url (includes endpoint and path)
|
867
|
+
* @param opts all request opts
|
868
|
+
*/
|
869
|
+
_makeXmlHttpRequestByHost: function(url, opts) {
|
870
|
+
// no cors or XDomainRequest, no request
|
871
|
+
if (!this._support.cors && !this._support.hasXDomainRequest) {
|
872
|
+
// very old browser, not supported
|
873
|
+
opts.callback(false, false, { 'message': 'CORS not supported' });
|
874
|
+
return;
|
875
|
+
}
|
881
876
|
|
882
|
-
|
883
|
-
|
877
|
+
var body = null;
|
878
|
+
var request = this._support.cors ? new XMLHttpRequest() : new XDomainRequest();
|
879
|
+
var ontimeout;
|
880
|
+
var self = this;
|
881
|
+
var timedOut;
|
882
|
+
var timeoutListener;
|
884
883
|
|
885
|
-
|
884
|
+
if (!this._isUndefined(opts.body)) {
|
885
|
+
body = JSON.stringify(opts.body);
|
886
|
+
}
|
886
887
|
|
887
|
-
|
888
|
-
|
889
|
-
ontimeout = null;
|
888
|
+
url += (url.indexOf('?') === -1 ? '?' : '&') + 'X-Algolia-API-Key=' + this.apiKey;
|
889
|
+
url += '&X-Algolia-Application-Id=' + this.applicationID;
|
890
890
|
|
891
|
-
|
892
|
-
|
893
|
-
|
891
|
+
if (this.userToken) {
|
892
|
+
url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
|
893
|
+
}
|
894
894
|
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
success = (response && response.length > 0);
|
899
|
-
} else {
|
900
|
-
response = event.target.response;
|
901
|
-
success = (event.target.status === 200 || event.target.status === 201);
|
902
|
-
}
|
895
|
+
if (this.tagFilters) {
|
896
|
+
url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
|
897
|
+
}
|
903
898
|
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
opts.callback(false, true, event, JSON.parse(xmlHttp.responseText));
|
908
|
-
}
|
909
|
-
};
|
910
|
-
xmlHttp.ontimeout = function(event) { // stop the network call but rely on ontimeout to call opt.callback
|
911
|
-
};
|
912
|
-
xmlHttp.onerror = function(event) {
|
913
|
-
clearTimeout(ontimeout);
|
914
|
-
ontimeout = null;
|
915
|
-
opts.callback(true, false, null, { 'message': 'Could not connect to host', 'error': event } );
|
916
|
-
};
|
899
|
+
for (var i = 0; i < this.extraHeaders.length; ++i) {
|
900
|
+
url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
|
901
|
+
}
|
917
902
|
|
918
|
-
|
919
|
-
|
903
|
+
timeoutListener = function() {
|
904
|
+
if (!self._support.timeout) {
|
905
|
+
timedOut = true;
|
906
|
+
request.abort();
|
907
|
+
}
|
920
908
|
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
909
|
+
opts.callback(true, false, { 'message': 'Timeout - Could not connect to endpoint ' + url } );
|
910
|
+
};
|
911
|
+
|
912
|
+
// do not rely on default XHR async flag, as some analytics code like hotjar
|
913
|
+
// breaks it and set it to false by default
|
914
|
+
if (request instanceof XMLHttpRequest) {
|
915
|
+
request.open(opts.method, url, true);
|
916
|
+
} else {
|
917
|
+
request.open(opts.method, url);
|
918
|
+
}
|
919
|
+
|
920
|
+
if (this._support.cors && body !== null && opts.method !== 'GET') {
|
921
|
+
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
922
|
+
}
|
923
|
+
|
924
|
+
// event object not received in IE8, at least
|
925
|
+
// but we do not use it, still important to note
|
926
|
+
request.onload = function(/*event*/) {
|
927
|
+
// When browser does not supports request.timeout, we can
|
928
|
+
// have both a load and timeout event
|
929
|
+
if (timedOut) {
|
930
|
+
return;
|
931
|
+
}
|
932
|
+
|
933
|
+
if (!self._support.timeout) {
|
934
|
+
clearTimeout(ontimeout);
|
935
|
+
}
|
936
|
+
|
937
|
+
var response = null;
|
938
|
+
|
939
|
+
try {
|
940
|
+
response = JSON.parse(request.responseText);
|
941
|
+
} catch(e) {}
|
939
942
|
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
943
|
+
var status =
|
944
|
+
// XHR provides a `status` property
|
945
|
+
request.status ||
|
946
|
+
|
947
|
+
// XDR does not have a `status` property,
|
948
|
+
// we rely on our own API response `status`, only
|
949
|
+
// provided when an error occurs, so we expect a .message
|
950
|
+
response && response.message && response.status ||
|
951
|
+
|
952
|
+
// XDR default to success when no response.status
|
953
|
+
response && 200;
|
954
|
+
|
955
|
+
var success = status === 200 || status === 201;
|
956
|
+
var retry = !success && status !== 400 && status !== 403 && status !== 404;
|
957
|
+
|
958
|
+
opts.callback(retry, success, response);
|
959
|
+
};
|
960
|
+
|
961
|
+
if (this._support.timeout) {
|
962
|
+
// .timeout supported by both XHR and XDR,
|
963
|
+
// we do receive timeout event, tested
|
964
|
+
request.timeout = this.requestTimeoutInMs * (opts.successiveRetryCount + 1);
|
965
|
+
|
966
|
+
request.ontimeout = timeoutListener;
|
967
|
+
} else {
|
968
|
+
ontimeout = setTimeout(timeoutListener, this.requestTimeoutInMs * (opts.successiveRetryCount + 1));
|
969
|
+
}
|
970
|
+
|
971
|
+
request.onerror = function(event) {
|
972
|
+
if (timedOut) {
|
973
|
+
return;
|
974
|
+
}
|
975
|
+
|
976
|
+
if (!self._support.timeout) {
|
977
|
+
clearTimeout(ontimeout);
|
978
|
+
}
|
979
|
+
|
980
|
+
// error event is trigerred both with XDR/XHR on:
|
981
|
+
// - DNS error
|
982
|
+
// - unallowed cross domain request
|
983
|
+
opts.callback(true, false, { 'message': 'Could not connect to host', 'error': event } );
|
984
|
+
};
|
985
|
+
|
986
|
+
request.send(body);
|
987
|
+
},
|
988
|
+
|
989
|
+
/*
|
990
|
+
* Transform search param object in query string
|
991
|
+
*/
|
992
|
+
_getSearchParams: function(args, params) {
|
993
|
+
if (this._isUndefined(args) || args === null) {
|
994
|
+
return params;
|
995
|
+
}
|
996
|
+
for (var key in args) {
|
997
|
+
if (key !== null && args.hasOwnProperty(key)) {
|
998
|
+
params += (params.length === 0) ? '?' : '&';
|
999
|
+
params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? JSON.stringify(args[key]) : args[key]);
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
return params;
|
1003
|
+
},
|
1004
|
+
_isUndefined: function(obj) {
|
1005
|
+
return obj === void 0;
|
1006
|
+
},
|
1007
|
+
|
1008
|
+
_support: {
|
1009
|
+
hasXMLHttpRequest: 'XMLHttpRequest' in window,
|
1010
|
+
hasXDomainRequest: 'XDomainRequest' in window,
|
1011
|
+
cors: 'withCredentials' in new XMLHttpRequest(),
|
1012
|
+
timeout: 'timeout' in new XMLHttpRequest()
|
1013
|
+
}
|
947
1014
|
};
|
948
1015
|
|
949
1016
|
/*
|
@@ -951,575 +1018,571 @@ AlgoliaSearch.prototype = {
|
|
951
1018
|
* You should use AlgoliaSearch.initIndex(indexName) to retrieve this object
|
952
1019
|
*/
|
953
1020
|
AlgoliaSearch.prototype.Index.prototype = {
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
1021
|
+
/*
|
1022
|
+
* Clear all queries in cache
|
1023
|
+
*/
|
1024
|
+
clearCache: function() {
|
1025
|
+
this.cache = {};
|
1026
|
+
},
|
1027
|
+
/*
|
1028
|
+
* Add an object in this index
|
1029
|
+
*
|
1030
|
+
* @param content contains the javascript object to add inside the index
|
1031
|
+
* @param callback (optional) the result callback with two arguments:
|
1032
|
+
* success: boolean set to true if the request was successfull
|
1033
|
+
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
1034
|
+
* @param objectID (optional) an objectID you want to attribute to this object
|
1035
|
+
* (if the attribute already exist the old object will be overwrite)
|
1036
|
+
*/
|
1037
|
+
addObject: function(content, callback, objectID) {
|
1038
|
+
var indexObj = this;
|
1039
|
+
if (this.as._isUndefined(objectID)) {
|
1040
|
+
return this.as._jsonRequest({ method: 'POST',
|
1041
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName),
|
1042
|
+
body: content,
|
1043
|
+
callback: callback });
|
1044
|
+
} else {
|
1045
|
+
return this.as._jsonRequest({ method: 'PUT',
|
1046
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
|
1047
|
+
body: content,
|
1048
|
+
callback: callback });
|
1049
|
+
}
|
983
1050
|
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
if (this.as.jsonp === null) {
|
1032
|
-
return this.as._jsonRequest({ method: 'GET',
|
1033
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
|
1034
|
-
callback: callback });
|
1035
|
-
} else {
|
1036
|
-
var pObj = {params: params};
|
1037
|
-
return this.as._jsonRequest({ method: 'GET',
|
1038
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
|
1039
|
-
callback: callback,
|
1040
|
-
body: pObj});
|
1041
|
-
}
|
1042
|
-
},
|
1043
|
-
|
1044
|
-
/*
|
1045
|
-
* Update partially an object (only update attributes passed in argument)
|
1046
|
-
*
|
1047
|
-
* @param partialObject contains the javascript attributes to override, the
|
1048
|
-
* object must contains an objectID attribute
|
1049
|
-
* @param callback (optional) the result callback with two arguments:
|
1050
|
-
* success: boolean set to true if the request was successfull
|
1051
|
-
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
1052
|
-
*/
|
1053
|
-
partialUpdateObject: function(partialObject, callback) {
|
1054
|
-
var indexObj = this;
|
1055
|
-
return this.as._jsonRequest({ method: 'POST',
|
1056
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
|
1057
|
-
body: partialObject,
|
1058
|
-
callback: callback });
|
1059
|
-
},
|
1060
|
-
/*
|
1061
|
-
* Partially Override the content of several objects
|
1062
|
-
*
|
1063
|
-
* @param objects contains an array of objects to update (each object must contains a objectID attribute)
|
1064
|
-
* @param callback (optional) the result callback with two arguments:
|
1065
|
-
* success: boolean set to true if the request was successfull
|
1066
|
-
* content: the server answer that updateAt and taskID
|
1067
|
-
*/
|
1068
|
-
partialUpdateObjects: function(objects, callback) {
|
1069
|
-
var indexObj = this;
|
1070
|
-
var postObj = {requests:[]};
|
1071
|
-
for (var i = 0; i < objects.length; ++i) {
|
1072
|
-
var request = { action: 'partialUpdateObject',
|
1073
|
-
objectID: objects[i].objectID,
|
1074
|
-
body: objects[i] };
|
1075
|
-
postObj.requests.push(request);
|
1076
|
-
}
|
1077
|
-
return this.as._jsonRequest({ method: 'POST',
|
1078
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
1079
|
-
body: postObj,
|
1080
|
-
callback: callback });
|
1081
|
-
},
|
1082
|
-
/*
|
1083
|
-
* Override the content of object
|
1084
|
-
*
|
1085
|
-
* @param object contains the javascript object to save, the object must contains an objectID attribute
|
1086
|
-
* @param callback (optional) the result callback with two arguments:
|
1087
|
-
* success: boolean set to true if the request was successfull
|
1088
|
-
* content: the server answer that updateAt and taskID
|
1089
|
-
*/
|
1090
|
-
saveObject: function(object, callback) {
|
1091
|
-
var indexObj = this;
|
1092
|
-
return this.as._jsonRequest({ method: 'PUT',
|
1093
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
|
1094
|
-
body: object,
|
1095
|
-
callback: callback });
|
1096
|
-
},
|
1097
|
-
/*
|
1098
|
-
* Override the content of several objects
|
1099
|
-
*
|
1100
|
-
* @param objects contains an array of objects to update (each object must contains a objectID attribute)
|
1101
|
-
* @param callback (optional) the result callback with two arguments:
|
1102
|
-
* success: boolean set to true if the request was successfull
|
1103
|
-
* content: the server answer that updateAt and taskID
|
1104
|
-
*/
|
1105
|
-
saveObjects: function(objects, callback) {
|
1106
|
-
var indexObj = this;
|
1107
|
-
var postObj = {requests:[]};
|
1108
|
-
for (var i = 0; i < objects.length; ++i) {
|
1109
|
-
var request = { action: 'updateObject',
|
1110
|
-
objectID: objects[i].objectID,
|
1111
|
-
body: objects[i] };
|
1112
|
-
postObj.requests.push(request);
|
1113
|
-
}
|
1114
|
-
return this.as._jsonRequest({ method: 'POST',
|
1115
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
1116
|
-
body: postObj,
|
1117
|
-
callback: callback });
|
1118
|
-
},
|
1119
|
-
/*
|
1120
|
-
* Delete an object from the index
|
1121
|
-
*
|
1122
|
-
* @param objectID the unique identifier of object to delete
|
1123
|
-
* @param callback (optional) the result callback with two arguments:
|
1124
|
-
* success: boolean set to true if the request was successfull
|
1125
|
-
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
1126
|
-
*/
|
1127
|
-
deleteObject: function(objectID, callback) {
|
1128
|
-
if (objectID === null || objectID.length === 0) {
|
1129
|
-
callback(false, { message: 'empty objectID'});
|
1130
|
-
return;
|
1131
|
-
}
|
1132
|
-
var indexObj = this;
|
1133
|
-
return this.as._jsonRequest({ method: 'DELETE',
|
1134
|
-
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
|
1135
|
-
callback: callback });
|
1136
|
-
},
|
1137
|
-
/*
|
1138
|
-
* Search inside the index using XMLHttpRequest request (Using a POST query to
|
1139
|
-
* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
|
1140
|
-
*
|
1141
|
-
* @param query the full text query
|
1142
|
-
* @param callback the result callback with two arguments:
|
1143
|
-
* success: boolean set to true if the request was successfull. If false, the content contains the error.
|
1144
|
-
* content: the server answer that contains the list of results.
|
1145
|
-
* @param args (optional) if set, contains an object with query parameters:
|
1146
|
-
* - page: (integer) Pagination parameter used to select the page to retrieve.
|
1147
|
-
* Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
|
1148
|
-
* - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
|
1149
|
-
* - attributesToRetrieve: a string that contains the list of object attributes you want to retrieve (let you minimize the answer size).
|
1150
|
-
* Attributes are separated with a comma (for example "name,address").
|
1151
|
-
* You can also use a string array encoding (for example ["name","address"]).
|
1152
|
-
* By default, all attributes are retrieved. You can also use '*' to retrieve all values when an attributesToRetrieve setting is specified for your index.
|
1153
|
-
* - attributesToHighlight: a string that contains the list of attributes you want to highlight according to the query.
|
1154
|
-
* Attributes are separated by a comma. You can also use a string array encoding (for example ["name","address"]).
|
1155
|
-
* If an attribute has no match for the query, the raw value is returned. By default all indexed text attributes are highlighted.
|
1156
|
-
* You can use `*` if you want to highlight all textual attributes. Numerical attributes are not highlighted.
|
1157
|
-
* A matchLevel is returned for each highlighted attribute and can contain:
|
1158
|
-
* - full: if all the query terms were found in the attribute,
|
1159
|
-
* - partial: if only some of the query terms were found,
|
1160
|
-
* - none: if none of the query terms were found.
|
1161
|
-
* - attributesToSnippet: a string that contains the list of attributes to snippet alongside the number of words to return (syntax is `attributeName:nbWords`).
|
1162
|
-
* Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
|
1163
|
-
* You can also use a string array encoding (Example: attributesToSnippet: ["name:10","content:10"]). By default no snippet is computed.
|
1164
|
-
* - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word. Defaults to 3.
|
1165
|
-
* - minWordSizefor2Typos: the minimum number of characters in a query word to accept two typos in this word. Defaults to 7.
|
1166
|
-
* - getRankingInfo: if set to 1, the result hits will contain ranking information in _rankingInfo attribute.
|
1167
|
-
* - aroundLatLng: search for entries around a given latitude/longitude (specified as two floats separated by a comma).
|
1168
|
-
* For example aroundLatLng=47.316669,5.016670).
|
1169
|
-
* You can specify the maximum distance in meters with the aroundRadius parameter (in meters) and the precision for ranking with aroundPrecision
|
1170
|
-
* (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).
|
1171
|
-
* At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
|
1172
|
-
* - insideBoundingBox: search entries inside a given area defined by the two extreme points of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
|
1173
|
-
* For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
|
1174
|
-
* At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
|
1175
|
-
* - numericFilters: a string that contains the list of numeric filters you want to apply separated by a comma.
|
1176
|
-
* The syntax of one filter is `attributeName` followed by `operand` followed by `value`. Supported operands are `<`, `<=`, `=`, `>` and `>=`.
|
1177
|
-
* You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
|
1178
|
-
* You can also use a string array encoding (for example numericFilters: ["price>100","price<1000"]).
|
1179
|
-
* - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
|
1180
|
-
* To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
|
1181
|
-
* You can also use a string array encoding, for example tagFilters: ["tag1",["tag2","tag3"]] means tag1 AND (tag2 OR tag3).
|
1182
|
-
* At indexing, tags should be added in the _tags** attribute of objects (for example {"_tags":["tag1","tag2"]}).
|
1183
|
-
* - facetFilters: filter the query by a list of facets.
|
1184
|
-
* Facets are separated by commas and each facet is encoded as `attributeName:value`.
|
1185
|
-
* For example: `facetFilters=category:Book,author:John%20Doe`.
|
1186
|
-
* You can also use a string array encoding (for example `["category:Book","author:John%20Doe"]`).
|
1187
|
-
* - facets: List of object attributes that you want to use for faceting.
|
1188
|
-
* Attributes are separated with a comma (for example `"category,author"` ).
|
1189
|
-
* You can also use a JSON string array encoding (for example ["category","author"]).
|
1190
|
-
* Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter.
|
1191
|
-
* You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
|
1192
|
-
* - queryType: select how the query words are interpreted, it can be one of the following value:
|
1193
|
-
* - prefixAll: all query words are interpreted as prefixes,
|
1194
|
-
* - prefixLast: only the last word is interpreted as a prefix (default behavior),
|
1195
|
-
* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
|
1196
|
-
* - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
|
1197
|
-
* The list of words is comma separated.
|
1198
|
-
* - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
|
1199
|
-
* This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
|
1200
|
-
* all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
|
1201
|
-
* For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
|
1202
|
-
* one is kept and others are removed.
|
1203
|
-
* @param delay (optional) if set, wait for this delay (in ms) and only send the query if there was no other in the meantime.
|
1204
|
-
*/
|
1205
|
-
search: function(query, callback, args, delay) {
|
1206
|
-
if (query === undefined || query === null) {
|
1207
|
-
query = '';
|
1208
|
-
}
|
1051
|
+
},
|
1052
|
+
/*
|
1053
|
+
* Add several objects
|
1054
|
+
*
|
1055
|
+
* @param objects contains an array of objects to add
|
1056
|
+
* @param callback (optional) the result callback with two arguments:
|
1057
|
+
* success: boolean set to true if the request was successfull
|
1058
|
+
* content: the server answer that updateAt and taskID
|
1059
|
+
*/
|
1060
|
+
addObjects: function(objects, callback) {
|
1061
|
+
var indexObj = this;
|
1062
|
+
var postObj = {requests:[]};
|
1063
|
+
for (var i = 0; i < objects.length; ++i) {
|
1064
|
+
var request = { action: 'addObject',
|
1065
|
+
body: objects[i] };
|
1066
|
+
postObj.requests.push(request);
|
1067
|
+
}
|
1068
|
+
return this.as._jsonRequest({ method: 'POST',
|
1069
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
1070
|
+
body: postObj,
|
1071
|
+
callback: callback });
|
1072
|
+
},
|
1073
|
+
/*
|
1074
|
+
* Get an object from this index
|
1075
|
+
*
|
1076
|
+
* @param objectID the unique identifier of the object to retrieve
|
1077
|
+
* @param callback (optional) the result callback with two arguments
|
1078
|
+
* success: boolean set to true if the request was successfull
|
1079
|
+
* content: the object to retrieve or the error message if a failure occured
|
1080
|
+
* @param attributes (optional) if set, contains the array of attribute names to retrieve
|
1081
|
+
*/
|
1082
|
+
getObject: function(objectID, callback, attributes) {
|
1083
|
+
if (Object.prototype.toString.call(callback) === '[object Array]' && !attributes) {
|
1084
|
+
attributes = callback;
|
1085
|
+
callback = null;
|
1086
|
+
}
|
1087
|
+
var indexObj = this;
|
1088
|
+
var params = '';
|
1089
|
+
if (!this.as._isUndefined(attributes)) {
|
1090
|
+
params = '?attributes=';
|
1091
|
+
for (var i = 0; i < attributes.length; ++i) {
|
1092
|
+
if (i !== 0) {
|
1093
|
+
params += ',';
|
1094
|
+
}
|
1095
|
+
params += attributes[i];
|
1096
|
+
}
|
1097
|
+
}
|
1209
1098
|
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1099
|
+
return this.as._jsonRequest({ method: 'GET',
|
1100
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
|
1101
|
+
callback: callback });
|
1102
|
+
},
|
1103
|
+
|
1104
|
+
/*
|
1105
|
+
* Update partially an object (only update attributes passed in argument)
|
1106
|
+
*
|
1107
|
+
* @param partialObject contains the javascript attributes to override, the
|
1108
|
+
* object must contains an objectID attribute
|
1109
|
+
* @param callback (optional) the result callback with two arguments:
|
1110
|
+
* success: boolean set to true if the request was successfull
|
1111
|
+
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
1112
|
+
*/
|
1113
|
+
partialUpdateObject: function(partialObject, callback) {
|
1114
|
+
var indexObj = this;
|
1115
|
+
return this.as._jsonRequest({ method: 'POST',
|
1116
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
|
1117
|
+
body: partialObject,
|
1118
|
+
callback: callback });
|
1119
|
+
},
|
1120
|
+
/*
|
1121
|
+
* Partially Override the content of several objects
|
1122
|
+
*
|
1123
|
+
* @param objects contains an array of objects to update (each object must contains a objectID attribute)
|
1124
|
+
* @param callback (optional) the result callback with two arguments:
|
1125
|
+
* success: boolean set to true if the request was successfull
|
1126
|
+
* content: the server answer that updateAt and taskID
|
1127
|
+
*/
|
1128
|
+
partialUpdateObjects: function(objects, callback) {
|
1129
|
+
var indexObj = this;
|
1130
|
+
var postObj = {requests:[]};
|
1131
|
+
for (var i = 0; i < objects.length; ++i) {
|
1132
|
+
var request = { action: 'partialUpdateObject',
|
1133
|
+
objectID: objects[i].objectID,
|
1134
|
+
body: objects[i] };
|
1135
|
+
postObj.requests.push(request);
|
1136
|
+
}
|
1137
|
+
return this.as._jsonRequest({ method: 'POST',
|
1138
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
1139
|
+
body: postObj,
|
1140
|
+
callback: callback });
|
1141
|
+
},
|
1142
|
+
/*
|
1143
|
+
* Override the content of object
|
1144
|
+
*
|
1145
|
+
* @param object contains the javascript object to save, the object must contains an objectID attribute
|
1146
|
+
* @param callback (optional) the result callback with two arguments:
|
1147
|
+
* success: boolean set to true if the request was successfull
|
1148
|
+
* content: the server answer that updateAt and taskID
|
1149
|
+
*/
|
1150
|
+
saveObject: function(object, callback) {
|
1151
|
+
var indexObj = this;
|
1152
|
+
return this.as._jsonRequest({ method: 'PUT',
|
1153
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
|
1154
|
+
body: object,
|
1155
|
+
callback: callback });
|
1156
|
+
},
|
1157
|
+
/*
|
1158
|
+
* Override the content of several objects
|
1159
|
+
*
|
1160
|
+
* @param objects contains an array of objects to update (each object must contains a objectID attribute)
|
1161
|
+
* @param callback (optional) the result callback with two arguments:
|
1162
|
+
* success: boolean set to true if the request was successfull
|
1163
|
+
* content: the server answer that updateAt and taskID
|
1164
|
+
*/
|
1165
|
+
saveObjects: function(objects, callback) {
|
1166
|
+
var indexObj = this;
|
1167
|
+
var postObj = {requests:[]};
|
1168
|
+
for (var i = 0; i < objects.length; ++i) {
|
1169
|
+
var request = { action: 'updateObject',
|
1170
|
+
objectID: objects[i].objectID,
|
1171
|
+
body: objects[i] };
|
1172
|
+
postObj.requests.push(request);
|
1173
|
+
}
|
1174
|
+
return this.as._jsonRequest({ method: 'POST',
|
1175
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
|
1176
|
+
body: postObj,
|
1177
|
+
callback: callback });
|
1178
|
+
},
|
1179
|
+
/*
|
1180
|
+
* Delete an object from the index
|
1181
|
+
*
|
1182
|
+
* @param objectID the unique identifier of object to delete
|
1183
|
+
* @param callback (optional) the result callback with two arguments:
|
1184
|
+
* success: boolean set to true if the request was successfull
|
1185
|
+
* content: the server answer that contains 3 elements: createAt, taskId and objectID
|
1186
|
+
*/
|
1187
|
+
deleteObject: function(objectID, callback) {
|
1188
|
+
if (objectID === null || objectID.length === 0) {
|
1189
|
+
callback(false, { message: 'empty objectID'});
|
1190
|
+
return;
|
1191
|
+
}
|
1192
|
+
var indexObj = this;
|
1193
|
+
return this.as._jsonRequest({ method: 'DELETE',
|
1194
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
|
1195
|
+
callback: callback });
|
1196
|
+
},
|
1197
|
+
/*
|
1198
|
+
* Search inside the index using XMLHttpRequest request (Using a POST query to
|
1199
|
+
* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
|
1200
|
+
*
|
1201
|
+
* @param query the full text query
|
1202
|
+
* @param callback the result callback with two arguments:
|
1203
|
+
* success: boolean set to true if the request was successfull. If false, the content contains the error.
|
1204
|
+
* content: the server answer that contains the list of results.
|
1205
|
+
* @param args (optional) if set, contains an object with query parameters:
|
1206
|
+
* - page: (integer) Pagination parameter used to select the page to retrieve.
|
1207
|
+
* Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
|
1208
|
+
* - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
|
1209
|
+
* - attributesToRetrieve: a string that contains the list of object attributes you want to retrieve (let you minimize the answer size).
|
1210
|
+
* Attributes are separated with a comma (for example "name,address").
|
1211
|
+
* You can also use an array (for example ["name","address"]).
|
1212
|
+
* By default, all attributes are retrieved. You can also use '*' to retrieve all values when an attributesToRetrieve setting is specified for your index.
|
1213
|
+
* - attributesToHighlight: a string that contains the list of attributes you want to highlight according to the query.
|
1214
|
+
* Attributes are separated by a comma. You can also use an array (for example ["name","address"]).
|
1215
|
+
* If an attribute has no match for the query, the raw value is returned. By default all indexed text attributes are highlighted.
|
1216
|
+
* You can use `*` if you want to highlight all textual attributes. Numerical attributes are not highlighted.
|
1217
|
+
* A matchLevel is returned for each highlighted attribute and can contain:
|
1218
|
+
* - full: if all the query terms were found in the attribute,
|
1219
|
+
* - partial: if only some of the query terms were found,
|
1220
|
+
* - none: if none of the query terms were found.
|
1221
|
+
* - attributesToSnippet: a string that contains the list of attributes to snippet alongside the number of words to return (syntax is `attributeName:nbWords`).
|
1222
|
+
* Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
|
1223
|
+
* You can also use an array (Example: attributesToSnippet: ['name:10','content:10']). By default no snippet is computed.
|
1224
|
+
* - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word. Defaults to 3.
|
1225
|
+
* - minWordSizefor2Typos: the minimum number of characters in a query word to accept two typos in this word. Defaults to 7.
|
1226
|
+
* - getRankingInfo: if set to 1, the result hits will contain ranking information in _rankingInfo attribute.
|
1227
|
+
* - aroundLatLng: search for entries around a given latitude/longitude (specified as two floats separated by a comma).
|
1228
|
+
* For example aroundLatLng=47.316669,5.016670).
|
1229
|
+
* You can specify the maximum distance in meters with the aroundRadius parameter (in meters) and the precision for ranking with aroundPrecision
|
1230
|
+
* (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).
|
1231
|
+
* At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
|
1232
|
+
* - insideBoundingBox: search entries inside a given area defined by the two extreme points of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
|
1233
|
+
* For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
|
1234
|
+
* At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
|
1235
|
+
* - numericFilters: a string that contains the list of numeric filters you want to apply separated by a comma.
|
1236
|
+
* The syntax of one filter is `attributeName` followed by `operand` followed by `value`. Supported operands are `<`, `<=`, `=`, `>` and `>=`.
|
1237
|
+
* You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
|
1238
|
+
* You can also use an array (for example numericFilters: ["price>100","price<1000"]).
|
1239
|
+
* - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
|
1240
|
+
* To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
|
1241
|
+
* You can also use an array, for example tagFilters: ["tag1",["tag2","tag3"]] means tag1 AND (tag2 OR tag3).
|
1242
|
+
* At indexing, tags should be added in the _tags** attribute of objects (for example {"_tags":["tag1","tag2"]}).
|
1243
|
+
* - facetFilters: filter the query by a list of facets.
|
1244
|
+
* Facets are separated by commas and each facet is encoded as `attributeName:value`.
|
1245
|
+
* For example: `facetFilters=category:Book,author:John%20Doe`.
|
1246
|
+
* You can also use an array (for example `["category:Book","author:John%20Doe"]`).
|
1247
|
+
* - facets: List of object attributes that you want to use for faceting.
|
1248
|
+
* Comma separated list: `"category,author"` or array `['category','author']`
|
1249
|
+
* Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter.
|
1250
|
+
* You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
|
1251
|
+
* - queryType: select how the query words are interpreted, it can be one of the following value:
|
1252
|
+
* - prefixAll: all query words are interpreted as prefixes,
|
1253
|
+
* - prefixLast: only the last word is interpreted as a prefix (default behavior),
|
1254
|
+
* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
|
1255
|
+
* - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
|
1256
|
+
* Comma separated and array are accepted.
|
1257
|
+
* - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
|
1258
|
+
* This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
|
1259
|
+
* all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
|
1260
|
+
* For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
|
1261
|
+
* one is kept and others are removed.
|
1262
|
+
* - restrictSearchableAttributes: List of attributes you want to use for textual search (must be a subset of the attributesToIndex index setting)
|
1263
|
+
* either comma separated or as an array
|
1264
|
+
* @param delay (optional) if set, wait for this delay (in ms) and only send the query if there was no other in the meantime.
|
1265
|
+
*/
|
1266
|
+
search: function(query, callback, args, delay) {
|
1267
|
+
if (query === undefined || query === null) {
|
1268
|
+
query = '';
|
1269
|
+
}
|
1215
1270
|
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1271
|
+
// no query = getAllObjects
|
1272
|
+
if (typeof query === 'function') {
|
1273
|
+
callback = query;
|
1274
|
+
query = '';
|
1275
|
+
}
|
1220
1276
|
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
}
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1277
|
+
if (typeof callback === 'object' && (this.as._isUndefined(args) || !args)) {
|
1278
|
+
args = callback;
|
1279
|
+
callback = null;
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
var indexObj = this;
|
1283
|
+
var params = 'query=' + encodeURIComponent(query);
|
1284
|
+
if (!this.as._isUndefined(args) && args !== null) {
|
1285
|
+
params = this.as._getSearchParams(args, params);
|
1286
|
+
}
|
1287
|
+
window.clearTimeout(indexObj.onDelayTrigger);
|
1288
|
+
if (!this.as._isUndefined(delay) && delay !== null && delay > 0) {
|
1289
|
+
var onDelayTrigger = window.setTimeout( function() {
|
1290
|
+
indexObj._search(params, callback);
|
1291
|
+
}, delay);
|
1292
|
+
indexObj.onDelayTrigger = onDelayTrigger;
|
1293
|
+
} else {
|
1294
|
+
return this._search(params, callback);
|
1295
|
+
}
|
1296
|
+
},
|
1297
|
+
|
1298
|
+
/*
|
1299
|
+
* Browse all index content
|
1300
|
+
*
|
1301
|
+
* @param page Pagination parameter used to select the page to retrieve.
|
1302
|
+
* Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
|
1303
|
+
* @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
|
1304
|
+
*/
|
1305
|
+
browse: function(page, callback, hitsPerPage) {
|
1306
|
+
if (+callback > 0 && (this.as._isUndefined(hitsPerPage) || !hitsPerPage)) {
|
1307
|
+
hitsPerPage = callback;
|
1308
|
+
callback = null;
|
1309
|
+
}
|
1310
|
+
var indexObj = this;
|
1311
|
+
var params = '?page=' + page;
|
1312
|
+
if (!this.as._isUndefined(hitsPerPage)) {
|
1313
|
+
params += '&hitsPerPage=' + hitsPerPage;
|
1314
|
+
}
|
1315
|
+
return this.as._jsonRequest({ method: 'GET',
|
1316
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
|
1317
|
+
callback: callback });
|
1318
|
+
},
|
1319
|
+
|
1320
|
+
/*
|
1321
|
+
* Get a Typeahead.js adapter
|
1322
|
+
* @param searchParams contains an object with query parameters (see search for details)
|
1323
|
+
*/
|
1324
|
+
ttAdapter: function(params) {
|
1325
|
+
var self = this;
|
1326
|
+
return function(query, cb) {
|
1327
|
+
self.search(query, function(success, content) {
|
1328
|
+
if (success) {
|
1329
|
+
cb(content.hits);
|
1330
|
+
} else {
|
1331
|
+
cb(content && content.message);
|
1332
|
+
}
|
1333
|
+
}, params);
|
1334
|
+
};
|
1335
|
+
},
|
1336
|
+
|
1337
|
+
/*
|
1338
|
+
* Wait the publication of a task on the server.
|
1339
|
+
* All server task are asynchronous and you can check with this method that the task is published.
|
1340
|
+
*
|
1341
|
+
* @param taskID the id of the task returned by server
|
1342
|
+
* @param callback the result callback with with two arguments:
|
1343
|
+
* success: boolean set to true if the request was successfull
|
1344
|
+
* content: the server answer that contains the list of results
|
1345
|
+
*/
|
1346
|
+
waitTask: function(taskID, callback) {
|
1347
|
+
var indexObj = this;
|
1348
|
+
return this.as._jsonRequest({ method: 'GET',
|
1349
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID,
|
1350
|
+
callback: function(success, body) {
|
1351
|
+
if (success) {
|
1352
|
+
if (body.status === 'published') {
|
1353
|
+
callback(true, body);
|
1354
|
+
} else {
|
1355
|
+
setTimeout(function() { indexObj.waitTask(taskID, callback); }, 100);
|
1356
|
+
}
|
1357
|
+
} else {
|
1358
|
+
callback(false, body);
|
1359
|
+
}
|
1360
|
+
}});
|
1361
|
+
},
|
1362
|
+
|
1363
|
+
/*
|
1364
|
+
* This function deletes the index content. Settings and index specific API keys are kept untouched.
|
1365
|
+
*
|
1366
|
+
* @param callback (optional) the result callback with two arguments
|
1367
|
+
* success: boolean set to true if the request was successfull
|
1368
|
+
* content: the settings object or the error message if a failure occured
|
1369
|
+
*/
|
1370
|
+
clearIndex: function(callback) {
|
1371
|
+
var indexObj = this;
|
1372
|
+
return this.as._jsonRequest({ method: 'POST',
|
1373
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
|
1374
|
+
callback: callback });
|
1375
|
+
},
|
1376
|
+
/*
|
1377
|
+
* Get settings of this index
|
1378
|
+
*
|
1379
|
+
* @param callback (optional) the result callback with two arguments
|
1380
|
+
* success: boolean set to true if the request was successfull
|
1381
|
+
* content: the settings object or the error message if a failure occured
|
1382
|
+
*/
|
1383
|
+
getSettings: function(callback) {
|
1384
|
+
var indexObj = this;
|
1385
|
+
return this.as._jsonRequest({ method: 'GET',
|
1386
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
|
1387
|
+
callback: callback });
|
1388
|
+
},
|
1389
|
+
|
1390
|
+
/*
|
1391
|
+
* Set settings for this index
|
1392
|
+
*
|
1393
|
+
* @param settigns the settings object that can contains :
|
1394
|
+
* - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
|
1395
|
+
* - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
|
1396
|
+
* - hitsPerPage: (integer) the number of hits per page (default = 10).
|
1397
|
+
* - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
|
1398
|
+
* If set to null, all attributes are retrieved.
|
1399
|
+
* - attributesToHighlight: (array of strings) default list of attributes to highlight.
|
1400
|
+
* If set to null, all indexed attributes are highlighted.
|
1401
|
+
* - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number of words to return (syntax is attributeName:nbWords).
|
1402
|
+
* By default no snippet is computed. If set to null, no snippet is computed.
|
1403
|
+
* - attributesToIndex: (array of strings) the list of fields you want to index.
|
1404
|
+
* If set to null, all textual and numerical attributes of your objects are indexed, but you should update it to get optimal results.
|
1405
|
+
* This parameter has two important uses:
|
1406
|
+
* - Limit the attributes to index: For example if you store a binary image in base64, you want to store it and be able to
|
1407
|
+
* retrieve it but you don't want to search in the base64 string.
|
1408
|
+
* - Control part of the ranking*: (see the ranking parameter for full explanation) Matches in attributes at the beginning of
|
1409
|
+
* the list will be considered more important than matches in attributes further down the list.
|
1410
|
+
* In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable
|
1411
|
+
* this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
|
1412
|
+
* - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
|
1413
|
+
* 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.
|
1414
|
+
* - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
|
1415
|
+
* in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
|
1416
|
+
* 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.
|
1417
|
+
* - ranking: (array of strings) controls the way results are sorted.
|
1418
|
+
* We have six available criteria:
|
1419
|
+
* - typo: sort according to number of typos,
|
1420
|
+
* - geo: sort according to decreassing distance when performing a geo-location based search,
|
1421
|
+
* - proximity: sort according to the proximity of query words in hits,
|
1422
|
+
* - attribute: sort according to the order of attributes defined by attributesToIndex,
|
1423
|
+
* - exact:
|
1424
|
+
* - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
|
1425
|
+
* 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
|
1426
|
+
* show starting by the v letter before it.
|
1427
|
+
* - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
|
1428
|
+
* - custom: sort according to a user defined formula set in **customRanking** attribute.
|
1429
|
+
* The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
|
1430
|
+
* - customRanking: (array of strings) lets you specify part of the ranking.
|
1431
|
+
* The syntax of this condition is an array of strings containing attributes prefixed by asc (ascending order) or desc (descending order) operator.
|
1432
|
+
* For example `"customRanking" => ["desc(population)", "asc(name)"]`
|
1433
|
+
* - queryType: Select how the query words are interpreted, it can be one of the following value:
|
1434
|
+
* - prefixAll: all query words are interpreted as prefixes,
|
1435
|
+
* - prefixLast: only the last word is interpreted as a prefix (default behavior),
|
1436
|
+
* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
|
1437
|
+
* - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
|
1438
|
+
* - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
|
1439
|
+
* - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
|
1440
|
+
* @param callback (optional) the result callback with two arguments
|
1441
|
+
* success: boolean set to true if the request was successfull
|
1442
|
+
* content: the server answer or the error message if a failure occured
|
1443
|
+
*/
|
1444
|
+
setSettings: function(settings, callback) {
|
1445
|
+
var indexObj = this;
|
1446
|
+
return this.as._jsonRequest({ method: 'PUT',
|
1447
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
|
1448
|
+
body: settings,
|
1449
|
+
callback: callback });
|
1450
|
+
},
|
1451
|
+
/*
|
1452
|
+
* List all existing user keys associated to this index
|
1453
|
+
*
|
1454
|
+
* @param callback the result callback with two arguments
|
1455
|
+
* success: boolean set to true if the request was successfull
|
1456
|
+
* content: the server answer with user keys list or error description if success is false.
|
1457
|
+
*/
|
1458
|
+
listUserKeys: function(callback) {
|
1459
|
+
var indexObj = this;
|
1460
|
+
return this.as._jsonRequest({ method: 'GET',
|
1461
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
|
1462
|
+
callback: callback });
|
1463
|
+
},
|
1464
|
+
/*
|
1465
|
+
* Get ACL of a user key associated to this index
|
1466
|
+
*
|
1467
|
+
* @param callback the result callback with two arguments
|
1468
|
+
* success: boolean set to true if the request was successfull
|
1469
|
+
* content: the server answer with user keys list or error description if success is false.
|
1470
|
+
*/
|
1471
|
+
getUserKeyACL: function(key, callback) {
|
1472
|
+
var indexObj = this;
|
1473
|
+
return this.as._jsonRequest({ method: 'GET',
|
1474
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
|
1475
|
+
callback: callback });
|
1476
|
+
},
|
1477
|
+
/*
|
1478
|
+
* Delete an existing user key associated to this index
|
1479
|
+
*
|
1480
|
+
* @param callback the result callback with two arguments
|
1481
|
+
* success: boolean set to true if the request was successfull
|
1482
|
+
* content: the server answer with user keys list or error description if success is false.
|
1483
|
+
*/
|
1484
|
+
deleteUserKey: function(key, callback) {
|
1485
|
+
var indexObj = this;
|
1486
|
+
return this.as._jsonRequest({ method: 'DELETE',
|
1487
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
|
1488
|
+
callback: callback });
|
1489
|
+
},
|
1490
|
+
/*
|
1491
|
+
* Add an existing user key associated to this index
|
1492
|
+
*
|
1493
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
1494
|
+
* can contains the following values:
|
1495
|
+
* - search: allow to search (https and http)
|
1496
|
+
* - addObject: allows to add/update an object in the index (https only)
|
1497
|
+
* - deleteObject : allows to delete an existing object (https only)
|
1498
|
+
* - deleteIndex : allows to delete index content (https only)
|
1499
|
+
* - settings : allows to get index settings (https only)
|
1500
|
+
* - editSettings : allows to change index settings (https only)
|
1501
|
+
* @param callback the result callback with two arguments
|
1502
|
+
* success: boolean set to true if the request was successfull
|
1503
|
+
* content: the server answer with user keys list or error description if success is false.
|
1504
|
+
*/
|
1505
|
+
addUserKey: function(acls, callback) {
|
1506
|
+
var indexObj = this;
|
1507
|
+
var aclsObject = {};
|
1508
|
+
aclsObject.acl = acls;
|
1509
|
+
return this.as._jsonRequest({ method: 'POST',
|
1510
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
|
1511
|
+
body: aclsObject,
|
1512
|
+
callback: callback });
|
1513
|
+
},
|
1514
|
+
/*
|
1515
|
+
* Add an existing user key associated to this index
|
1516
|
+
*
|
1517
|
+
* @param acls the list of ACL for this key. Defined by an array of strings that
|
1518
|
+
* can contains the following values:
|
1519
|
+
* - search: allow to search (https and http)
|
1520
|
+
* - addObject: allows to add/update an object in the index (https only)
|
1521
|
+
* - deleteObject : allows to delete an existing object (https only)
|
1522
|
+
* - deleteIndex : allows to delete index content (https only)
|
1523
|
+
* - settings : allows to get index settings (https only)
|
1524
|
+
* - editSettings : allows to change index settings (https only)
|
1525
|
+
* @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
|
1526
|
+
* @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
|
1527
|
+
* @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
|
1528
|
+
* @param callback the result callback with two arguments
|
1529
|
+
* success: boolean set to true if the request was successfull
|
1530
|
+
* content: the server answer with user keys list or error description if success is false.
|
1531
|
+
*/
|
1532
|
+
addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
|
1533
|
+
var indexObj = this;
|
1534
|
+
var aclsObject = {};
|
1535
|
+
aclsObject.acl = acls;
|
1536
|
+
aclsObject.validity = validity;
|
1537
|
+
aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
|
1538
|
+
aclsObject.maxHitsPerQuery = maxHitsPerQuery;
|
1539
|
+
return this.as._jsonRequest({ method: 'POST',
|
1540
|
+
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
|
1541
|
+
body: aclsObject,
|
1542
|
+
callback: callback });
|
1543
|
+
},
|
1544
|
+
///
|
1545
|
+
/// Internal methods only after this line
|
1546
|
+
///
|
1547
|
+
_search: function(params, callback) {
|
1548
|
+
var pObj = {params: params};
|
1549
|
+
if (this.as.jsonp === null) {
|
1550
|
+
var self = this;
|
1551
|
+
return this.as._jsonRequest({ cache: this.cache,
|
1552
|
+
method: 'POST',
|
1553
|
+
url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
|
1554
|
+
body: pObj,
|
1555
|
+
callback: function(success, content) {
|
1556
|
+
var status = content && content.status;
|
1557
|
+
if (success || status && Math.floor(status / 100) === 4 || Math.floor(status / 100) === 1) {
|
1558
|
+
self.as.jsonp = false;
|
1559
|
+
callback && callback(success, content);
|
1560
|
+
} else {
|
1561
|
+
self.as.jsonp = true;
|
1562
|
+
self._search(params, callback);
|
1563
|
+
}
|
1564
|
+
}
|
1565
|
+
});
|
1566
|
+
} else if (this.as.jsonp) {
|
1567
|
+
return this.as._jsonRequest({ cache: this.cache,
|
1568
|
+
method: 'GET',
|
1569
|
+
url: '/1/indexes/' + encodeURIComponent(this.indexName),
|
1570
|
+
body: pObj,
|
1571
|
+
callback: callback });
|
1572
|
+
} else {
|
1573
|
+
return this.as._jsonRequest({ cache: this.cache,
|
1574
|
+
method: 'POST',
|
1575
|
+
url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
|
1576
|
+
body: pObj,
|
1577
|
+
callback: callback});
|
1578
|
+
}
|
1579
|
+
},
|
1517
1580
|
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1581
|
+
// internal attributes
|
1582
|
+
as: null,
|
1583
|
+
indexName: null,
|
1584
|
+
typeAheadArgs: null,
|
1585
|
+
typeAheadValueOption: null
|
1523
1586
|
};
|
1524
1587
|
|
1525
1588
|
/*
|
@@ -1876,7 +1939,7 @@ AlgoliaSearch.prototype.Index.prototype = {
|
|
1876
1939
|
}
|
1877
1940
|
var aggregatedAnswer = content.results[0];
|
1878
1941
|
aggregatedAnswer.disjunctiveFacets = aggregatedAnswer.disjunctiveFacets || {};
|
1879
|
-
aggregatedAnswer.
|
1942
|
+
aggregatedAnswer.facets_stats = aggregatedAnswer.facets_stats || {};
|
1880
1943
|
// create disjunctive facets from facets (disjunctive facets without refinements)
|
1881
1944
|
for (var facet in unusedDisjunctiveFacets) {
|
1882
1945
|
if (aggregatedAnswer.facets[facet] && !aggregatedAnswer.disjunctiveFacets[facet]) {
|
@@ -1903,9 +1966,13 @@ AlgoliaSearch.prototype.Index.prototype = {
|
|
1903
1966
|
}
|
1904
1967
|
// aggregate the disjunctive facets stats
|
1905
1968
|
for (var stats in content.results[i + 1].facets_stats) {
|
1906
|
-
aggregatedAnswer.
|
1969
|
+
aggregatedAnswer.facets_stats[stats] = content.results[i + 1].facets_stats[stats];
|
1907
1970
|
}
|
1908
1971
|
}
|
1972
|
+
|
1973
|
+
// Backward compatibility
|
1974
|
+
aggregatedAnswer.facetStats = aggregatedAnswer.facets_stats;
|
1975
|
+
|
1909
1976
|
// add the excludes
|
1910
1977
|
for (var exclude in self.excludes) {
|
1911
1978
|
if (self.excludes[exclude]) {
|
@@ -1969,7 +2036,8 @@ AlgoliaSearch.prototype.Index.prototype = {
|
|
1969
2036
|
attributesToHighlight: [],
|
1970
2037
|
attributesToSnippet: [],
|
1971
2038
|
facets: facet,
|
1972
|
-
facetFilters: this._getFacetFilters(facet)
|
2039
|
+
facetFilters: this._getFacetFilters(facet),
|
2040
|
+
analytics: false
|
1973
2041
|
});
|
1974
2042
|
},
|
1975
2043
|
|