Coming from V1 (or js client v2)? Read the migration guide to the new version of the Helper.
The JavaScript helper is an advanced library we provide to our users. If you are looking to build a complete search interface, we recommend you to use instantsearch.js. If you want to build an autocomplete menu, see autocomplete.js.
algoliasearch-helper-js
This module is the companion of the algolia/algoliasearch-client-js. It helps you keep
track of the search parameters and provides a higher level API.
See the helper in action
Features
- Search parameters management
- Facets exclusions
- Pagination
- Disjunctive faceting (search on two or more values of the same facet)
Examples
Vanilla JavaScript
A small example that uses Browserify to manage modules.
var algoliasearch = require('algoliasearch');
var algoliasearchHelper = require('algoliasearch-helper');
var client = algoliasearch('appId', 'apiKey');
var helper = algoliasearchHelper(client, 'indexName', {
facets: ['mainCharacterFirstName', 'year'],
disjunctiveFacets: ['director']
});
helper.on('result', function(event){
console.log(event.results);
});
helper.addDisjunctiveFacetRefinement('director', 'Clint Eastwood');
helper.addDisjunctiveFacetRefinement('director', 'Sofia Coppola');
helper.addNumericRefinement('year', '=', 2003);
helper.search();
See more examples in the examples folder
AngularJS module
<script src="https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.angular.js"></script>
<script src="dist/algoliasearch.helper.min.js"></script>
<script type="text/javascript">
angular.module('searchApp', ['ngSanitize', 'algoliasearch'])
.controller('searchController', ['$scope', '$sce', 'algolia', function($scope, $sce, algolia) {
var algolia = algolia.Client('applicationId', 'apiKey');
$scope.q = '';
$scope.content = null;
$scope.helper = algoliasearchHelper(algolia, 'indexName', {
facets: ['type', 'shipping'],
disjunctiveFacets: ['category', 'manufacturer'],
hitsPerPage: 5,
});
$scope.helper.on('result', function(event) {
$scope.$apply(function() {
$scope.content = event.results;
});
});
$scope.toggleRefine = function($event, facet, value) {
$event.preventDefault();
$scope.helper.toggleRefine(facet, value).search();
};
$scope.$watch('q', function(q) {
$scope.helper.setQuery(q).search();
});
$scope.helper.search();
}]);
</script>
You can see the full Angular example here
Helper cheatsheet
There is also a complete JSDoc
Add the helper in your project
Regular <script>
tag
Use our jsDelivr build:
<script src="https://cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js"></script>
With NPM
npm install algoliasearch-helper
Init the helper
var helper = algoliasearchHelper(client, 'indexName');
Helper lifecycle
-
modify the parameters of the search (usually through user interactions)
helper.setQuery('iphone').addFacetRefinement('category', 'phone')
-
trigger the search (after all the modification have been applied)
helper.search()
-
read the results (with the event "result" handler) and update the UI with the results
helper.on('result', function(event) { updateUI(event.results); });
-
go back to 1
Objects
AlgoliasearchHelper: the helper. Keeps the state of the search, makes the queries and calls the handlers when an event happen.
SearchParameters: the object representing the state of the search. The current state is stored in helperInstance.state
.
SearchResults: the object in which the Algolia answers are transformed into. This object is passed to the result event handler.
An example of SearchResults in JSON is available at the end of this readme
Search
The search is triggered by the search()
method.
It takes all the previous modifications to the search and uses them to create the queries to Algolia. The search parameters are immutable.
Example:
var helper = algoliasearchHelper(client, indexName);
helper.on('result', function(event) {
console.log(event.results);
});
helper.search();
helper.setQuery('landscape').search();
helper.addTag('photo').search();
Events
The helper is a Node.js EventEmitter instance.
result
: get notified when new results are received. The handler function will receive
two objects (SearchResults
and SearchParameters
).
error
: get notified when errors are received from the API.
change
: get notified when a property has changed in the helper
search
: get notified when a request is sent to Algolia
Listen to the result
event
helper.on('result', updateTheResults);
Listen to a result
event once
helper.once('result', updateTheResults);
Remove a result
listener
helper.removeListener('result', updateTheResults);
Remove all result
listeners
helper.removeAllListeners('result');
All the methods from Node.js EventEmitter class are available.
Query
Do a search with the query "fruit"
helper.setQuery('fruit').search();
Filtering results
Facets are filters to retrieve a subset of an index having a specific value for a given attribute. First you need to define which attribute will be used as a facet in the dashboard: https://www.algolia.com/explorer#?tab=display
Regular (conjunctive) facets
Refinements are ANDed by default (Conjunctive selection).
Facet definition
var helper = algoliasearchHelper(client, indexName, {
facets: ['ANDFacet']
});
Add a facet filter
helper.addFacetRefinement('ANDFacet', 'valueOfANDFacet').search();
Remove a facet filter
helper.removeFacetRefinement('ANDFacet', 'valueOfANDFacet').search();
Disjunctive facets
Refinements are ORed by default (Disjunctive selection).
Facet definition
var helper = algoliasearchHelper(client, indexName, {
disjunctiveFacets: ['ORFacet']
});
Add a facet filter
helper.addDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();
Remove a facet filter
helper.removeDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();
Negative facets
Filter so that we do NOT get a given facet
Facet definition (same as "AND" facet)
var helper = algoliasearchHelper(client, indexName, {
facets: ['ANDFacet']
}).search();
Exclude a value for a facet
helper.addFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');
Remove an exclude from the list of excluded values
helper.removeFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');
Numeric facets
Filter over numeric attributes with math operations like =
, >
, <
, >=
, <=
. Can be used for numbers and dates (if converted to timestamp)
Facet definition
var helper = algoliasearchHelper(client, indexName, {
disjunctiveFacets: ['numericAttribute']
});
Add numeric refinements
helper.addNumericRefinement('numericAttribute', '=', '3').search();
helper.addNumericRefinement('numericAttribute', '=', '4').search();
helper.addNumericRefinement('numericAttribute2', '=', ['42', '56', '37'] ).search();
Remove a numeric refinement
helper.removeNumericRefinement('numericAttribute', '=', '3').search();
Batch numeric filter removal
helper.removeNumericRefinement('numericAttribute', '=').search();
helper.removeNumericRefinement('numericAttribute').search();
Hierarchical facets
Hierarchical facets are useful to build such navigation menus:
| products
> fruits
> citrus
| strawberries
| peaches
| apples
Here, we refined the search this way:
- click on fruits
- click on citrus
Usage
To build such menu, you need to use hierarchical faceting:
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1']
}]
});
Requirements: All the specified attributes
must be defined in your Algolia settings
as attributes for faceting.
Given your objects looks like this:
{
"objectID": "123",
"name": "orange",
"categories": {
"lvl0": "fruits",
"lvl1": "fruits > citrus"
}
}
And you refine products
:
helper.toggleFacetRefinement('products', 'fruits > citrus');
You will get a hierarchical presentation of your facet values: a navigation menu
of your facet values.
helper.on('result', function(event){
console.log(event.results.hierarchicalFacets[0]);
});
To ease navigation, we always:
- provide the root level categories
- provide the current refinement sub categories (
fruits > citrus > *
: n + 1) - provide the parent refinement (
fruits > citrus
=> fruits
: n -1) categories - refine the search using the current hierarchical refinement
Multiple values per level
Your records can also share multiple categories between one another by using arrays inside your object:
{
"objectID": "123",
"name": "orange",
"categories": {
"lvl0": ["fruits", "color"],
"lvl1": ["fruits > citrus", "color > orange"]
}
},
{
"objectID": "456",
"name": "grapefruit",
"categories": {
"lvl0": ["fruits", "color", "new"],
"lvl1": ["fruits > citrus", "color > yellow", "new > citrus"]
}
}
Specifying another separator
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
separator: '|'
}]
});
helper.toggleFacetRefinement('products', 'fruits|citrus');
Would mean that your objects look like so:
{
"objectID": "123",
"name": "orange",
"categories": {
"lvl0": "fruits",
"lvl1": "fruits|citrus"
}
}
Specifying a different sort order for values
The default sort for the hierarchical facet view is: isRefined:desc (first show refined), name:asc (then sort by name)
.
You can specify a different sort order by using:
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
sortBy: ['count:desc', 'name:asc']
}]
});
The available sort tokens are:
Restrict results and hierarchical values to non-root level
Let's say you have a lot of levels:
- fruits
- yellow
- citrus
- spicy
But you only want to get the values starting at "citrus", you can use rootPath
You can specify an root path to filter the hierarchical values
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2', 'categories.lvl3'],
rootPath: 'fruits > yellow > citrus'
}]
});
Having a rootPath will refine the results on it automatically.
Hide parent level of current parent level
By default the hierarchical facet is going to return the child and parent facet values of the current refinement.
If you do not want to get the parent facet values you can set showParentLevel to false
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
showParentLevel: false
}]
});
Asking for the current breadcrumb
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
separator: '|'
}]
});
helper.toggleFacetRefinement('products', 'fruits|citrus');
var breadcrumb = helper.getHierarchicalFacetBreadcrumb('products');
console.log(breadcrumb);
console.log(breadcrumb.join(' | '));
Clearing filters
Clear all the refinements for all the refined attributes
helper.clearRefinements().search();
Clear all the refinements for a specific attribute
helper.clearRefinements('ANDFacet').search();
[ADVANCED] Clear only the exclusions on the "ANDFacet" attribute
helper.clearRefinements(function(value, attribute, type) {
return type === 'exclude' && attribute === 'ANDFacet';
}).search();
Facet utilities
Get the values of a facet with the default sort
helper.on('result', function(event) {
event.results.getFacetValues('age');
});
Get the values of a facet with a custom sort
helper.on('result', function(event) {
event.results.getFacetValues('age', {sortBy: ['count:asc']});
});
Get the facet stats
This only apply on numeric based facets/attributes.
helper.on('result', function(event) {
event.results.getFacetStats('age');
});
Tags
Tags are an easy way to do filtering. They are based on a special attribute in the records named _tags
, which can be a single string value or an array of strings.
Add a tag filter for the value "landscape"
helper.addTag('landscape').search();
Remove a tag filter for the value "landscape"
helper.removeTag('landscape').search();
Clear all the tags filters
helper.clearTags().search();
Get the current page
helper.getPage();
Change page
helper.setPage(3).search();
Automatic reset to page 0
During a search, changing the parameters will update the result set, which can then change
the number of pages in the result set. Therefore, the behavior has been standardized so
that any operation that may change the number of page will reset the pagination to page 0.
This may lead to some unexpected behavior. For example:
helper.setPage(4);
helper.getPage();
helper.setQuery('foo');
helper.getPage();
Non exhaustive list of operations that trigger a reset:
- refinements (conjunctive, exclude, disjunctive, hierarchical, numeric)
- tags
- index (setIndex)
- setQuery
- setHitsPerPage
- setTypoTolerance
Index
Index can be changed. The common use case is when you have several slaves with different sort order (sort by relevance, price or any other attribute).
Change the current index
helper.setIndex('index_orderByPrice').search();
Get the current index
var currentIndex = helper.getIndex();
One time query
Sometime it's convenient to reuse the current search parameters with small changes
without changing the state stored in the helper. That's why there is a function
called searchOnce
. This method does not trigger change
or error
events.
In the following, we are using searchOnce
to fetch only a single element using
all the other parameters already set in the search parameters.
Using searchOnce with a callback
var state = helper.searchOnce(
{hitsPerPage: 1},
function(error, content, state) {
});
Using searchOnce with a promise
var state1 = helper.searchOnce({hitsPerPage: 1})
.then(function(res) {
});
Query parameters
There are lots of other parameters you can set.
Set a parameter at the initialization of the helper
var helper = algoliasearchHelper(client, indexName, {
hitsPerPage: 50
});
Set a parameter later
helper.setQueryParameter('hitsPerPage', 20).search();
List of parameters that can be set
Results format
Here is an example of a result object you get with the result
event.
{
"hitsPerPage": 10,
"processingTimeMS": 2,
"facets": [
{
"name": "type",
"data": {
"HardGood": 6627,
"BlackTie": 550,
"Music": 665,
"Software": 131,
"Game": 456,
"Movie": 1571
},
"exhaustive": false
},
{
"exhaustive": false,
"data": {
"Free shipping": 5507
},
"name": "shipping"
}
],
"hits": [
{
"thumbnailImage": "http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_54x108_s.gif",
"_highlightResult": {
"shortDescription": {
"matchLevel": "none",
"value": "Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection",
"matchedWords": []
},
"category": {
"matchLevel": "none",
"value": "Computer Security Software",
"matchedWords": []
},
"manufacturer": {
"matchedWords": [],
"value": "Webroot",
"matchLevel": "none"
},
"name": {
"value": "Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows",
"matchedWords": [],
"matchLevel": "none"
}
},
"image": "http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_105x210_sc.jpg",
"shipping": "Free shipping",
"bestSellingRank": 4,
"shortDescription": "Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection",
"url": "http://www.bestbuy.com/site/webroot-secureanywhere-internet-security-3-devi…d=1219060687969&skuId=1688832&cmp=RMX&ky=2d3GfEmNIzjA0vkzveHdZEBgpPCyMnLTJ",
"name": "Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows",
"category": "Computer Security Software",
"salePrice_range": "1 - 50",
"objectID": "1688832",
"type": "Software",
"customerReviewCount": 5980,
"salePrice": 49.99,
"manufacturer": "Webroot"
},
....
],
"nbHits": 10000,
"disjunctiveFacets": [
{
"exhaustive": false,
"data": {
"5": 183,
"12": 112,
"7": 149,
...
},
"name": "customerReviewCount",
"stats": {
"max": 7461,
"avg": 157.939,
"min": 1
}
},
{
"data": {
"Printer Ink": 142,
"Wireless Speakers": 60,
"Point & Shoot Cameras": 48,
...
},
"name": "category",
"exhaustive": false
},
{
"exhaustive": false,
"data": {
"> 5000": 2,
"1 - 50": 6524,
"501 - 2000": 566,
"201 - 500": 1501,
"101 - 200": 1360,
"2001 - 5000": 47
},
"name": "salePrice_range"
},
{
"data": {
"Dynex™": 202,
"Insignia™": 230,
"PNY": 72,
...
},
"name": "manufacturer",
"exhaustive": false
}
],
"query": "",
"nbPages": 100,
"page": 0,
"index": "bestbuy"
}
Browser support
This project works on any ES5 browser, basically >= IE9+.