Socket
Socket
Sign inDemoInstall

algoliasearch-helper

Package Overview
Dependencies
Maintainers
4
Versions
147
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

algoliasearch-helper - npm Package Compare versions

Comparing version 2.1.2 to 2.2.0

.eslintignore

2

bower.json
{
"name": "algoliasearch-helper",
"version": "2.1.2",
"version": "2.2.0",
"homepage": "https://github.com/algolia/algoliasearch-helper-js",

@@ -5,0 +5,0 @@ "authors": [

@@ -1,7 +0,8 @@

"use strict";
var AlgoliaSearchHelper = require( "./src/algoliasearch.helper" );
'use strict';
var SearchParameters = require( "./src/SearchParameters" );
var SearchResults = require( "./src/SearchResults" );
var AlgoliaSearchHelper = require('./src/algoliasearch.helper');
var SearchParameters = require('./src/SearchParameters');
var SearchResults = require('./src/SearchResults');
/**

@@ -14,11 +15,11 @@ * The algoliasearchHelper module contains everything needed to use the Algoliasearch

* var client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
* var helper = algoliasearchHelper( client, "bestbuy", {
* facets : [ "shipping" ],
* disjunctiveFacets : [ "category" ]
* } );
* helper.on( "result", function( result ) {
* console.log( result );
* } );
* helper.toggleRefine( "Movies & TV Shows" )
* .toggleRefine( "Free shipping" )
* var helper = algoliasearchHelper(client, 'bestbuy', {
* facets: ['shipping'],
* disjunctiveFacets: ['category']
* });
* helper.on('result', function(result) {
* console.log(result);
* });
* helper.toggleRefine('Movies & TV Shows')
* .toggleRefine('Free shipping')
* .search();

@@ -31,4 +32,4 @@ * @module algoliasearchHelper

*/
function algoliasearchHelper( client, index, opts ) {
return new AlgoliaSearchHelper( client, index, opts );
function algoliasearchHelper(client, index, opts) {
return new AlgoliaSearchHelper(client, index, opts);
}

@@ -41,3 +42,3 @@

*/
algoliasearchHelper.version = "2.1.2";
algoliasearchHelper.version = '2.1.2';

@@ -44,0 +45,0 @@ /**

{
"name": "algoliasearch-helper",
"version": "2.1.2",
"version": "2.2.0",
"description": "Helper for implementing advanced search features with algolia",

@@ -10,6 +10,6 @@ "main": "index.js",

"minify-build": "uglifyjs dist/algoliasearch.helper.js --mangle --compress=warnings=false > dist/algoliasearch.helper.min.js",
"dev": "DEBUG=zuul* zuul --no-coverage --local 8090 --ui tape test/run.js",
"dev-coverage": "DEBUG=zuul* zuul --local 8090 --ui tape test/run.js",
"lint": "eslint src index.js; true",
"watch-lint": " onchange '.eslintrc' 'src/**/*.js' 'index.js' -- npm run lint ",
"dev": "DEBUG=zuul* zuul --no-coverage --local 8090 test/run.js",
"dev-coverage": "DEBUG=zuul* zuul --local 8090 test/run.js",
"lint": "scripts/lint.sh",
"lint:watch": "onchange 'src/**/*js' 'index.js' 'test/**/*js' '.eslintrc' -- npm run -s lint",
"open": "sleep 1 && opn http://localhost:8080/examples",

@@ -22,8 +22,12 @@ "examples": "npm run build & npm run open & http-server .",

"doc:publish": "gh-pages-deploy",
"test-ci": "echo \"Error: no test specified\" && exit 1",
"test": "echo \"Error: no test specified\" && exit 1"
"readme:toc": "doctoc --notitle README.md --maxlevel 3",
"test-ci": "scripts/test-ci.sh",
"test": "scripts/test.sh",
"test-node": "clear && node test/run",
"test:watch": "onchange '{src,test}/**/*.js' 'index.js' -- npm run test-node",
"jsfmt": "jsfmt"
},
"gh-pages-deploy": {
"prep": [
"npm run doc"
"doc"
],

@@ -41,5 +45,14 @@ "noprompt": false

},
"standard": {
"ignore": [
"dist/"
]
},
"browser": {
"lodash": "lodash-compat"
},
"devDependencies": {
"algoliasearch": ">= 3.1",
"browserify": "9.0.3",
"@algolia/jsfmt": "1.3.0",
"algoliasearch": "latest",
"browserify": "10.2.4",
"bulk-require": "0.2.1",

@@ -49,5 +62,8 @@ "bulkify": "1.1.1",

"envify": "3.4.0",
"eslint": "0.17.1",
"eslint": "0.24.0",
"eslint-config-airbnb": "0.0.6",
"eslint-config-algolia": "2.1.1",
"gh-pages-deploy": "0.3.0",
"http-server": "0.8.0",
"jscs": "1.13.1",
"jsdoc": "bobylito/jsdoc",

@@ -57,10 +73,13 @@ "minami": "algolia/minami",

"opn": "1.0.1",
"sinon": "1.14.1",
"tape": "3.5.0",
"uglify-js": "2.4.17",
"watchify": "2.6.0",
"zuul": "2.1.1"
"phantomjs": "1.9.17",
"sinon": "1.15.4",
"tape": "4.0.0",
"uglify-js": "2.4.23",
"watchify": "3.2.3",
"zuul": "3.2.0",
"zuul-ngrok": "3.0.0"
},
"dependencies": {
"lodash": "3.9.x"
"lodash": "3.10.0",
"lodash-compat": "3.10.0"
},

@@ -67,0 +86,0 @@ "peerDependencies": {

@@ -5,37 +5,77 @@ *Coming from V1 (or js client v2)?* Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V1-to-V2) to the new version of the Helper.

This module is the companion of the algoliasearch-client-js. It helps you keep
This module is the companion of the [algolia/algoliasearch-client-js](https://github.com/algolia/algoliasearch-client-js). It helps you keep
track of the search parameters and provides a higher level API.
The helper is built on top of algoliasearch-client-js and this version is
specifically made to work with the newest V3 versions of it.
This is the library you will need to easily build a good search UX like our [instant search demo](http://demos.algolia.com/instant-search-demo/).
[See the helper in action](http://algolia.github.io/algoliasearch-helper-js/)
[![Version][version-svg]][package-url] [![Build Status][travis-svg]][travis-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url]
[![Browser tests][browser-test-matrix]][browser-test-url]
[travis-svg]: https://img.shields.io/travis/algolia/algoliasearch-helper-js/master.svg?style=flat-square
[travis-url]: https://travis-ci.org/algolia/algoliasearch-helper-js
[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/algoliasearch-helper.svg?style=flat-square
[downloads-url]: http://npm-stat.com/charts.html?package=algoliasearch-helper
[browser-test-matrix]: https://saucelabs.com/browser-matrix/as-helper-js.svg
[browser-test-url]: https://saucelabs.com/u/as-helper-js
[version-svg]: https://img.shields.io/npm/v/algoliasearch-helper.svg?style=flat-square
[package-url]: https://npmjs.org/package/algoliasearch-helper
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Features](#features)
- [Example](#example)
- [Helper cheatsheet](#helper-cheatsheet)
- [Add the helper in your project](#add-the-helper-in-your-project)
- [Init the helper](#init-the-helper)
- [Helper lifecycle](#helper-lifecycle)
- [Objects](#objects)
- [Search](#search)
- [Events](#events)
- [Query](#query)
- [Filtering results](#filtering-results)
- [Tags](#tags)
- [Pagination](#pagination)
- [Index](#index)
- [Query parameters](#query-parameters)
- [Results format](#results-format)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Features
- Search parameters tracking
- Search parameters management
- Facets exclusions
- Pagination
- Disjunctive facetting (search on two or more values for a single facet)
- Disjunctive faceting (search on two or more values of the same facet)
## What does it look like?
## Example
A small example that uses Browserify to manage modules.
```javascript
var algoliasearch = require( "algoliasearch" );
var algoliasearchHelper = require( "algoliasearch-helper" );
```js
var algoliasearch = require('algoliasearch');
var algoliasearchHelper = require('algoliasearch-helper');
var client = algoliasearch( "app_id", "secret" );
var client = algoliasearch('appId', 'apiKey');
var helper = algoliasearchHelper( client, "myMainIndex", {
facets : [ "mainCharacterFirstName", "year" ],
disjunctiveFacets : [ "director" ]
var helper = algoliasearchHelper(client, 'indexName', {
facets: ['mainCharacterFirstName', 'year'],
disjunctiveFacets: ['director']
});
helper.on( "result", function( data ){
console.log( data.hits );
} );
helper.on('result', function(data){
console.log(data.hits);
});
helper.addDisjunctiveRefine( "director", "Clint Eastword" );
helper.addDisjunctiveRefine( "director", "Sofia Coppola" );
helper.addDisjunctiveRefine('director', 'Clint Eastword');
helper.addDisjunctiveRefine('director', 'Sofia Coppola');
helper.addNumericRefinement( "year", "=", 2003 );
helper.addNumericRefinement('year', '=', 2003);

@@ -46,35 +86,974 @@ // Search for any movie filmed in 2003 and directed by either C. Eastwood or S. Coppola

## How to use this module
## Helper cheatsheet
[Have a look at the JSDoc](http://algolia.github.io/algoliasearch-helper-js/docs)
[There is also a complete JSDoc](http://algolia.github.io/algoliasearch-helper-js/docs)
[See the examples in action](http://algolia.github.io/algoliasearch-helper-js/)
### Add the helper in your project
### Use with NPM
### Regular `<script>` tag
Use our [jsDelivr](http://www.jsdelivr.com/) build:
`<script src="//cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js"></script>`
### With NPM
`npm install algoliasearch-helper`
### Use with bower
### With bower
`bower install algoliasearch-helper`
### Use the CDN
### Init the helper
Include this in your page :
```js
var helper = algoliasearchHelper(client, 'indexName'/*, parameters*/);
```
`<script src="//cdn.jsdelivr.net/algoliasearch.helper/2.0.0/algoliasearch.helper.min.js"></script>`
### Helper lifecycle
## How to contribute
1. modify the parameters of the search (usually through user interactions)<br/>
```
helper.setQuery('iphone').addRefine('category', 'phone')
```
- fork this repo
- clone the repository `git clone https://github.com/[your-handle-here]/algoliasearch-helper-js.git`
- make your fix or feature
- launch the dev mode `npm run dev`
- add a test for your feature (see /test folder)
- make sure, it goes through the linter without an error `npm run lint`
- [propose your pull request](https://help.github.com/articles/creating-a-pull-request/)
- profit :)
2. trigger the search (after all the modification have been applied)<br/>
```
helper.search()
```
A quick note though, even though we'll make our best to read and integrate your PR,
we may be a bit slow. Sorry :). We might also make some comments and discussions too,
for the best interest of this library. *Thanks in advance for your contribution!*
3. read the results (with the event "result" handler) and update the UI with the results<br/>
```
helper.on('result', function(results) {
updateUI(results);
});
```
4. 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](#results-format)
### 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:
```js
var helper = algoliasearchHelper(client, indexName);
// Let's monitor the results with the console
helper.on('result', function(content) {
console.log(content);
});
// Let's make an empty search
// The results are all sorted using the dashboard configuration
helper.search();
// Let's search for "landscape"
helper.setQuery('landscape').search();
// Let's add a category "photo"
// Will make a search with "photo" tag and "landscape" as the query
helper.addTag('photo').search();
```
### Events
The helper is an [Event Emitter](https://nodejs.org/api/events.html#events_class_events_eventemitter).
`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
#### Listen to the `result` event
```js
helper.on('result', updateTheResults);
```
#### Listen to a `result` event once
```js
helper.once('result', updateTheResults);
```
#### Remove all `result` listeners
```js
helper.removeListener('result');
```
### Query
#### Do a search with the query "fruit"
```javscript
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](https://www.algolia.com/explorer#?tab=display)
#### "AND" facets
##### Facet definition
```js
var helper = algoliasearchHelper(client, indexName, {
facets: ['ANDFacet']
});
```
##### Add a facet filter
```js
helper.addRefine('ANDFacet', 'valueOfANDFacet').search();
```
##### Remove a facet filter
```js
helper.removeRefine('ANDFacet', 'valueOfANDFacet').search();
```
#### "OR" facets
##### Facet definition
```js
var helper = algoliasearchHelper(client, indexName, {
disjunctiveFacets: ['ORFacet']
});
```
##### Add a facet filter
```js
helper.addDisjunctiveRefine('ORFacet', 'valueOfORFacet').search();
```
##### Remove a facet filter
```js
helper.removeDisjunctiveRefine('ORFacet', 'valueOfORFacet').search();
```
#### Negative facets
filter so that we do NOT get a given category
##### Facet definition (same as "AND" facet)
```js
var helper = algoliasearchHelper(client, indexName, {
facets: ['ANDFacet']
}).search();
```
##### Exclude a value for a facet
```js
helper.addExclude('ANDFacet', 'valueOfANDFacetToExclude');
```
##### Remove an exclude from the list of excluded values
```js
helper.removeExclude('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
```js
var helper = algoliasearchHelper(client, indexName, {
disjunctiveFacets: ['numericFacet']
});
```
##### Add a numeric refinement
```js
helper.addNumericRefinement('numericFacet', '=', '3').search();
```
##### Remove a numeric refinement
```js
helper.removeNumericRefinemetn('numericFacet', '=', '3').search();
```
#### Hierarchical facets
Hierarchical facets are useful to build such navigation menus:
```txt
| products
> fruits
> citrus
| strawberries
| peaches
| apples
```
Here, we refined the search this way:
- click on fruits
- click on citrus
To build such menu, you need to use hierarchical faceting:
```javascript
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1']
}]
});
```
Given your objects looks like this:
```json
{
"objectID": "123",
"name": "orange",
"categories": {
"lvl0": "fruits",
"lvl1": "fruits > citrus"
}
}
```
And you refine `products`:
```js
helper.toggleRefine('products', 'fruits > citrus');
```
You will get a hierarchical presentation of your facet values: a navigation menu
of your facet values.
```js
helper.on('result', function(data){
console.log(data.hierarchicalFacets[0]);
// {
// 'name': 'products',
// 'count': null,
// 'isRefined': true,
// 'path': null,
// 'data': [{
// 'name': 'fruits',
// 'path': 'fruits',
// 'count': 1,
// 'isRefined': true,
// 'data': [{
// 'name': 'citrus',
// 'path': 'fruits > citrus',
// 'count': 1,
// 'isRefined': true,
// 'data': null
// }]
// }]
// }
});
```
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
##### Specifying another separator
```js
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
separator: '|'
}]
});
helper.toggleRefine('products', 'fruits|citrus');
```
Would mean that your objects look like so:
```json
{
"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:
```js
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
sortBy: ['count:desc', 'name:asc'] // first show the most common values, then sort by name
}]
});
```
The available sort tokens are:
- count
- isRefined
- name
- path
##### Asking for the current breadcrumb
```js
var helper = algoliasearchHelper(client, indexName, {
hierarchicalFacets: [{
name: 'products',
attributes: ['categories.lvl0', 'categories.lvl1'],
separator: '|'
}]
});
helper.toggleRefine('products', 'fruits|citrus');
var breadcrumb = helper.getHierarchicalFacetBreadcrumb('products');
console.log(breadcrumb);
// ['fruits', 'citrus']
console.log(breadcrumb.join(' | '));
// 'fruits | citrus'
```
#### Clearing filters
##### Clear all the refinements for all the refined attributes
```js
helper.clearRefinements().search();
```
##### Clear all the refinements for a specific attribute
```js
helper.clearRefinements('ANDFacet').search();
```
##### [ADVANCED] Clear only the exclusions on the "ANDFacet" attribute
```js
helper.clearRefinements(function(value, attribute, type) {
return type === 'exclude' && attribute === 'ANDFacet';
}).search();
```
### 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"
```js
helper.addTag('landscape').search();
```
#### Remove a tag filter for the value "landscape"
```js
helper.removeTag('landscape').search();
```
#### Clear all the tags filters
```js
helper.clearTags().search();
```
### Pagination
#### Get the current page
```js
helper.getCurrentPage();
```
#### Change page
```js
helper.setCurrentPage(3).search();
```
### 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
```js
helper.setIndex('index_orderByPrice').search();
```
#### Get the current index
```js
var currentIndex = helper.state.index;
```
### Query parameters
There are lots of other parameters you can set.
#### Set a parameter at the initialization of the helper
```js
var helper = algoliasearchHelper(client, indexName, {
hitsPerPage: 50
});
```
#### Set a parameter later
```js
helper.setQueryParameter('hitsPerPage', 20).search();
```
#### List of parameters that can be set
<table cellspacing="0" cellpadding="0" class="params">
<tbody>
<tr>
<td valign="top" class="td1">
<p class="p1"><span class="s1"><b>Name</b></span></p>
</td>
<td valign="top" class="td2">
<p class="p1"><span class="s1"><b>Type</b></span></p>
</td>
<td valign="top" class="td3">
<p class="p1"><span class="s1"><b>Description</b></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">advancedSyntax</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Enable the advanced syntax.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#advancedSyntax">advancedSyntax on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">allowTyposOnNumericTokens</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Should the engine allow typos on numerics.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#allowTyposOnNumericTokens">allowTyposOnNumericTokens on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">analytics</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Enable the analytics</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#analytics">analytics on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">analyticsTags</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Tag of the query in the analytics.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#analyticsTags">analyticsTags on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">aroundLatLng</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Center of the geo search.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#aroundLatLng">aroundLatLng on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">aroundLatLngViaIP</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Center of the search, retrieve from the user IP.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#aroundLatLngViaIP">aroundLatLngViaIP on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">aroundPrecision</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Precision of the geo search.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#aroundPrecision">aroundPrecision on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">aroundRadius</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Radius of the geo search.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#aroundRadius">aroundRadius on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">attributesToHighlight</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">List of attributes to highlight</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#attributesToHighlight">attributesToHighlight on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">attributesToRetrieve</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">List of attributes to retrieve</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#attributesToRetrieve">attributesToRetrieve on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">attributesToSnippet</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">List of attributes to snippet</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#attributesToSnippet">attributesToSnippet on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">disjunctiveFacets</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">Array.&lt;string&gt;</span></p>
</td>
<td valign="top" class="td6">
<p class="p1"><span class="s1">All the declared disjunctive facets</span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">distinct</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Remove duplicates based on the index setting attributeForDistinct</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#distinct">distinct on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">facets</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">Array.&lt;string&gt;</span></p>
</td>
<td valign="top" class="td6">
<p class="p1"><span class="s1">All the facets that will be requested to the server</span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">getRankingInfo</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">integer</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Enable the ranking informations in the response</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#getRankingInfo">getRankingInfo on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">hitsPerPage</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Number of hits to be returned by the search API</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#hitsPerPage">hitsPerPage on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">ignorePlurals</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Should the plurals be ignored</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#ignorePlurals">ignorePlurals on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">insideBoundingBox</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Geo search inside a box.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#insideBoundingBox">insideBoundingBox on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">maxValuesPerFacet</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Number of values for each facetted attribute</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#maxValuesPerFacet">maxValuesPerFacet on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">minWordSizefor1Typo</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Number of characters to wait before doing one character replacement.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#minWordSizefor1Typo">minWordSizefor1Typo on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">minWordSizefor2Typos</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Number of characters to wait before doing a second character replacement.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#minWordSizefor2Typos">minWordSizefor2Typos on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">optionalWords</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Add some optional words to those defined in the dashboard</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#optionalWords">optionalWords on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">page</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">number</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">The current page number</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#page">page on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">query</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Query string of the instant search. The empty string is a valid query.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#query">query on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">queryType</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">How the query should be treated by the search engine. Possible values: prefixAll, prefixLast, prefixNone</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#queryType">queryType on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">removeWordsIfNoResults</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Possible values are "lastWords" "firstWords" "allOptionnal" "none" (default)</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#removeWordsIfNoResults">removeWordsIfNoResults on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">replaceSynonymsInHighlight</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Should the engine replace the synonyms in the highlighted results.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#replaceSynonymsInHighlight">replaceSynonymsInHighlight on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">restrictSearchableAttributes</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Restrict which attribute is searched.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#restrictSearchableAttributes">restrictSearchableAttributes on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">synonyms</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">boolean</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Enable the synonyms</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#synonyms">synonyms on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">tagFilters</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">Contains the tag filters in the raw format of the Algolia API. Setting this parameter is not compatible with the of the add/remove/toggle methods of the tag api.</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#tagFilters">tagFilters on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
<tr>
<td valign="top" class="td4">
<p class="p2"><span class="s2">typoTolerance</span></p>
</td>
<td valign="top" class="td5">
<p class="p3"><span class="s1">string</span></p>
</td>
<td valign="top" class="td6">
<p class="p4"><span class="s1">How the typo tolerance behave in the search engine. Possible values: true, false, min, strict</span></p>
<p class="p5"><span class="s1"><a href="https://www.algolia.com/doc#typoTolerance">typoTolerance on Algolia.com<span class="s3"></span></a></span></p>
</td>
</tr>
</tbody>
</table>
### Results format
Here is an example of a result object you get with the `result` event.
```js
{
"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"
}
```

@@ -1,11 +0,15 @@

"use strict";
var SearchParameters = require( "./SearchParameters" );
var SearchResults = require( "./SearchResults" );
var extend = require( "./functions/extend" );
var util = require( "util" );
var events = require( "events" );
var forEach = require( "lodash/collection/forEach" );
var isEmpty = require( "lodash/lang/isEmpty" );
var bind = require( "lodash/function/bind" );
'use strict';
var SearchParameters = require('./SearchParameters');
var SearchResults = require('./SearchResults');
var util = require('util');
var events = require('events');
var forEach = require('lodash/collection/forEach');
var isEmpty = require('lodash/lang/isEmpty');
var bind = require('lodash/function/bind');
var reduce = require('lodash/collection/reduce');
var map = require('lodash/collection/map');
var trim = require('lodash/string/trim');
var merge = require('lodash/object/merge');
/**

@@ -15,8 +19,8 @@ * Initialize a new AlgoliaSearchHelper

* @classdesc The AlgoliaSearchHelper is a class that ease the management of the
* search. It provides an event based interface for search callbacks :
* - change : when the internal search state is changed.
* search. It provides an event based interface for search callbacks:
* - change: when the internal search state is changed.
* This event contains a {@link SearchParameters} object and the {@link SearchResults} of the last result if any.
* - result : when the response is retrieved from Algolia and is processed.
* - result: when the response is retrieved from Algolia and is processed.
* This event contains a {@link SearchResults} object and the {@link SearchParameters} corresponding to this answer.
* - error : when the response is an error. This event contains the error returned by the server.
* - error: when the response is an error. This event contains the error returned by the server.
* @param {AlgoliaSearch} client an AlgoliaSearch client

@@ -26,6 +30,6 @@ * @param {string} index the index name to query

*/
function AlgoliaSearchHelper( client, index, options ) {
function AlgoliaSearchHelper(client, index, options) {
this.client = client;
this.index = index;
this.state = SearchParameters.make( options );
this.state = SearchParameters.make(options);
this.lastResults = null;

@@ -36,3 +40,3 @@ this._queryId = 0;

util.inherits( AlgoliaSearchHelper, events.EventEmitter );
util.inherits(AlgoliaSearchHelper, events.EventEmitter);

@@ -53,4 +57,4 @@ /**

*/
AlgoliaSearchHelper.prototype.setQuery = function( q ) {
this.state = this.state.setQuery( q );
AlgoliaSearchHelper.prototype.setQuery = function(q) {
this.state = this.state.setQuery(q);
this._change();

@@ -65,4 +69,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.clearRefinements = function( name ) {
this.state = this.state.clearRefinements( name );
AlgoliaSearchHelper.prototype.clearRefinements = function(name) {
this.state = this.state.clearRefinements(name);
this._change();

@@ -88,4 +92,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.addDisjunctiveRefine = function( facet, value ) {
this.state = this.state.addDisjunctiveFacetRefinement( facet, value );
AlgoliaSearchHelper.prototype.addDisjunctiveRefine = function(facet, value) {
this.state = this.state.addDisjunctiveFacetRefinement(facet, value);
this._change();

@@ -102,4 +106,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.addNumericRefinement = function( attribute, operator, value ) {
this.state = this.state.addNumericRefinement( attribute, operator, value );
AlgoliaSearchHelper.prototype.addNumericRefinement = function(attribute, operator, value) {
this.state = this.state.addNumericRefinement(attribute, operator, value);
this._change();

@@ -115,4 +119,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.addRefine = function( facet, value ) {
this.state = this.state.addFacetRefinement( facet, value );
AlgoliaSearchHelper.prototype.addRefine = function(facet, value) {
this.state = this.state.addFacetRefinement(facet, value);
this._change();

@@ -128,4 +132,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.addExclude = function( facet, value ) {
this.state = this.state.addExcludeRefinement( facet, value );
AlgoliaSearchHelper.prototype.addExclude = function(facet, value) {
this.state = this.state.addExcludeRefinement(facet, value);
this._change();

@@ -140,4 +144,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.addTag = function( tag ) {
this.state = this.state.addTagRefinement( tag );
AlgoliaSearchHelper.prototype.addTag = function(tag) {
this.state = this.state.addTagRefinement(tag);
this._change();

@@ -154,4 +158,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.removeNumericRefinement = function( attribute, operator, value ) {
this.state = this.state.removeNumericRefinement( attribute, operator, value );
AlgoliaSearchHelper.prototype.removeNumericRefinement = function(attribute, operator, value) {
this.state = this.state.removeNumericRefinement(attribute, operator, value);
this._change();

@@ -167,4 +171,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.removeDisjunctiveRefine = function( facet, value ) {
this.state = this.state.removeDisjunctiveFacetRefinement( facet, value );
AlgoliaSearchHelper.prototype.removeDisjunctiveRefine = function(facet, value) {
this.state = this.state.removeDisjunctiveFacetRefinement(facet, value);
this._change();

@@ -180,4 +184,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.removeRefine = function( facet, value ) {
this.state = this.state.removeFacetRefinement( facet, value );
AlgoliaSearchHelper.prototype.removeRefine = function(facet, value) {
this.state = this.state.removeFacetRefinement(facet, value);
this._change();

@@ -193,4 +197,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.removeExclude = function( facet, value ) {
this.state = this.state.removeExcludeRefinement( facet, value );
AlgoliaSearchHelper.prototype.removeExclude = function(facet, value) {
this.state = this.state.removeExcludeRefinement(facet, value);
this._change();

@@ -205,4 +209,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.removeTag = function( tag ) {
this.state = this.state.removeTagRefinement( tag );
AlgoliaSearchHelper.prototype.removeTag = function(tag) {
this.state = this.state.removeTagRefinement(tag);
this._change();

@@ -218,4 +222,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.toggleExclude = function( facet, value ) {
this.state = this.state.toggleExcludeFacetRefinement( facet, value );
AlgoliaSearchHelper.prototype.toggleExclude = function(facet, value) {
this.state = this.state.toggleExcludeFacetRefinement(facet, value);
this._change();

@@ -230,17 +234,16 @@ return this;

* @return {AlgoliaSearchHelper}
* @throws will throw an error if the facet is not declared in the settings of the helper
*/
AlgoliaSearchHelper.prototype.toggleRefine = function( facet, value ) {
if( this.state.isConjunctiveFacet( facet ) ) {
this.state = this.state.toggleFacetRefinement( facet, value );
AlgoliaSearchHelper.prototype.toggleRefine = function(facet, value) {
if (this.state.isHierarchicalFacet(facet)) {
this.state = this.state.toggleHierarchicalFacetRefinement(facet, value);
} else if (this.state.isConjunctiveFacet(facet)) {
this.state = this.state.toggleFacetRefinement(facet, value);
} else if (this.state.isDisjunctiveFacet(facet)) {
this.state = this.state.toggleDisjunctiveFacetRefinement(facet, value);
} else {
throw new Error('Cannot refine the undeclared facet ' + facet +
'; it should be added to the helper options facets or disjunctiveFacets');
}
else if( this.state.isDisjunctiveFacet( facet ) ) {
this.state = this.state.toggleDisjunctiveFacetRefinement( facet, value );
}
else {
/* eslint-disable */
console.log( "warning : you're trying to refine the undeclared facet '" + facet +
"'; add it to the helper options 'facets' or 'disjunctiveFacets'" );
/* eslint-enable */
return this;
}
this._change();

@@ -255,4 +258,4 @@ return this;

*/
AlgoliaSearchHelper.prototype.toggleTag = function( tag ) {
this.state = this.state.toggleTagRefinement( tag );
AlgoliaSearchHelper.prototype.toggleTag = function(tag) {
this.state = this.state.toggleTagRefinement(tag);
this._change();

@@ -267,3 +270,3 @@ return this;

AlgoliaSearchHelper.prototype.nextPage = function() {
return this.setCurrentPage( this.state.page + 1 );
return this.setCurrentPage(this.state.page + 1);
};

@@ -276,3 +279,3 @@

AlgoliaSearchHelper.prototype.previousPage = function() {
return this.setCurrentPage( this.state.page - 1 );
return this.setCurrentPage(this.state.page - 1);
};

@@ -285,6 +288,6 @@

*/
AlgoliaSearchHelper.prototype.setCurrentPage = function( page ) {
if( page < 0 ) throw new Error( "Page requested below 0." );
AlgoliaSearchHelper.prototype.setCurrentPage = function(page) {
if (page < 0) throw new Error('Page requested below 0.');
this.state = this.state.setPage( page );
this.state = this.state.setPage(page);
this._change();

@@ -299,5 +302,5 @@ return this;

*/
AlgoliaSearchHelper.prototype.setIndex = function( name ) {
AlgoliaSearchHelper.prototype.setIndex = function(name) {
this.index = name;
this.setCurrentPage( 0 );
this.setCurrentPage(0);
return this;

@@ -312,6 +315,6 @@ };

*/
AlgoliaSearchHelper.prototype.setQueryParameter = function( parameter, value ) {
var newState = this.state.setQueryParameter( parameter, value );
AlgoliaSearchHelper.prototype.setQueryParameter = function(parameter, value) {
var newState = this.state.setQueryParameter(parameter, value);
if( this.state === newState ) return this;
if (this.state === newState) return this;

@@ -324,8 +327,8 @@ this.state = newState;

/**
* Set the whole state ( warning : will erase previous state )
* Set the whole state (warning: will erase previous state)
* @param {SearchParameters} newState the whole new state
* @return {AlgoliaSearchHelper}
*/
AlgoliaSearchHelper.prototype.setState = function( newState ) {
this.state = new SearchParameters( newState );
AlgoliaSearchHelper.prototype.setState = function(newState) {
this.state = new SearchParameters(newState);
this._change();

@@ -336,2 +339,10 @@ return this;

/**
* Get the current search state stored in the helper. This object is immutable.
* @return {SearchParameters}
*/
AlgoliaSearchHelper.prototype.getState = function() {
return this.state;
};
/**
* Override the current state without triggering a change event.

@@ -343,13 +354,13 @@ * Do not use this method unless you know what you are doing. (see the example

* @example
* helper.on( "change", function( state ){
* helper.on('change', function(state){
* // In this function you might want to find a way to store the state in the url/history
* updateYourURL( state );
* } );
* window.onpopstate = function( event ){
* updateYourURL(state)
* })
* window.onpopstate = function(event){
* // This is naive though as you should check if the state is really defined etc.
* helper.overrideStateWithoutTriggeringChangeEvent( event.state ).search();
* helper.overrideStateWithoutTriggeringChangeEvent(event.state).search()
* }
*/
AlgoliaSearchHelper.prototype.overrideStateWithoutTriggeringChangeEvent = function( newState ) {
this.state = new SearchParameters( newState );
AlgoliaSearchHelper.prototype.overrideStateWithoutTriggeringChangeEvent = function(newState) {
this.state = new SearchParameters(newState);
return this;

@@ -364,10 +375,12 @@ };

*/
AlgoliaSearchHelper.prototype.isRefined = function( facet, value ) {
if( this.state.isConjunctiveFacet( facet ) ) {
return this.state.isFacetRefined( facet, value );
AlgoliaSearchHelper.prototype.isRefined = function(facet, value) {
if (this.state.isConjunctiveFacet(facet)) {
return this.state.isFacetRefined(facet, value);
} else if (this.state.isDisjunctiveFacet(facet)) {
return this.state.isDisjunctiveFacetRefined(facet, value);
}
else if( this.state.isDisjunctiveFacet( facet ) ) {
return this.state.isDisjunctiveFacetRefined( facet, value );
}
return false;
throw new Error(facet +
' is not properly defined in this helper configuration' +
'(use the facets or disjunctiveFacets keys to configure it)');
};

@@ -380,5 +393,11 @@

*/
AlgoliaSearchHelper.prototype.hasRefinements = function( attribute ) {
var attributeHasNumericRefinements = !isEmpty( this.state.getNumericRefinements( attribute ) );
return attributeHasNumericRefinements || this.isRefined( attribute );
AlgoliaSearchHelper.prototype.hasRefinements = function(attribute) {
var attributeHasNumericRefinements = !isEmpty(this.state.getNumericRefinements(attribute));
var isFacetDeclared = this.state.isConjunctiveFacet(attribute) || this.state.isDisjunctiveFacet(attribute);
if (!attributeHasNumericRefinements && isFacetDeclared) {
return this.state.isFacetRefined(attribute);
}
return attributeHasNumericRefinements;
};

@@ -392,4 +411,4 @@

*/
AlgoliaSearchHelper.prototype.isExcluded = function( facet, value ) {
return this.state.isExcludeRefined( facet, value );
AlgoliaSearchHelper.prototype.isExcluded = function(facet, value) {
return this.state.isExcludeRefined(facet, value);
};

@@ -403,4 +422,4 @@

*/
AlgoliaSearchHelper.prototype.isDisjunctiveRefined = function( facet, value ) {
return this.state.isDisjunctiveFacetRefined( facet, value );
AlgoliaSearchHelper.prototype.isDisjunctiveRefined = function(facet, value) {
return this.state.isDisjunctiveFacetRefined(facet, value);
};

@@ -413,4 +432,4 @@

*/
AlgoliaSearchHelper.prototype.isTagRefined = function( tag ) {
return this.state.isTagRefined( tag );
AlgoliaSearchHelper.prototype.isTagRefined = function(tag) {
return this.state.isTagRefined(tag);
};

@@ -447,4 +466,4 @@

*/
AlgoliaSearchHelper.prototype.getQueryParameter = function( parameterName ) {
return this.state.getQueryParameter( parameterName );
AlgoliaSearchHelper.prototype.getQueryParameter = function(parameterName) {
return this.state.getQueryParameter(parameterName);
};

@@ -457,40 +476,43 @@

*/
AlgoliaSearchHelper.prototype.getRefinements = function( facetName ) {
AlgoliaSearchHelper.prototype.getRefinements = function(facetName) {
var refinements = [];
if( this.state.isConjunctiveFacet( facetName ) ) {
var conjRefinements = this.state.getConjunctiveRefinements( facetName );
forEach( conjRefinements, function( r ) {
refinements.push( {
value : r,
type : "conjunctive"
} );
} );
if (this.state.isConjunctiveFacet(facetName)) {
var conjRefinements = this.state.getConjunctiveRefinements(facetName);
forEach(conjRefinements, function(r) {
refinements.push({
value: r,
type: 'conjunctive'
});
});
var excludeRefinements = this.state.getExcludeRefinements(facetName);
forEach(excludeRefinements, function(r) {
refinements.push({
value: r,
type: 'exclude'
});
});
} else if (this.state.isDisjunctiveFacet(facetName)) {
var disjRefinements = this.state.getDisjunctiveRefinements(facetName);
forEach(disjRefinements, function(r) {
refinements.push({
value: r,
type: 'disjunctive'
});
});
}
else if( this.state.isDisjunctiveFacet( facetName ) ) {
var disjRefinements = this.state.getDisjunctiveRefinements( facetName );
forEach( disjRefinements, function( r ) {
refinements.push( {
value : r,
type : "disjunctive"
} );
} );
}
var excludeRefinements = this.state.getExcludeRefinements( facetName );
forEach( excludeRefinements, function( r ) {
refinements.push( {
value : r,
type : "exclude"
} );
} );
var numericRefinements = this.state.getNumericRefinements(facetName);
var numericRefinements = this.state.getNumericRefinements( facetName );
forEach( numericRefinements, function( value, operator ) {
refinements.push( {
value : value,
operator : operator,
type : "numeric"
} );
} );
forEach(numericRefinements, function(value, operator) {
refinements.push({
value: value,
operator: operator,
type: 'numeric'
});
});

@@ -500,4 +522,20 @@ return refinements;

///////////// PRIVATE
/**
* Get the current breadcrumb for a hierarchical facet, as an array
* @param {string} facetName Hierarchical facet name
* @return {array}
*/
AlgoliaSearchHelper.prototype.getHierarchicalFacetBreadcrumb = function(facetName) {
return map(
this
.state
.getHierarchicalRefinement(facetName)[0]
.split(this.state._getHierarchicalFacetSeparator(
this.state.getHierarchicalFacetByName(facetName)
)), function trimName(facetValue) { return trim(facetValue); }
);
};
// /////////// PRIVATE
/**

@@ -511,7 +549,7 @@ * Perform the underlying queries

this.client.search( this._getQueries(),
bind( this._handleResponse,
this,
state,
this._queryId++ ) );
this.client.search(this._getQueries(),
bind(this._handleResponse,
this,
state,
this._queryId++));
};

@@ -528,18 +566,34 @@

//One query for the hits
queries.push( {
indexName : this.index,
query : this.state.query,
params : this._getHitsSearchParams()
} );
// One query for the hits
queries.push({
indexName: this.index,
query: this.state.query,
params: this._getHitsSearchParams()
});
//One for each disjunctive facets
forEach( this.state.getRefinedDisjunctiveFacets(), function( refinedFacet ) {
queries.push( {
indexName : this.index,
query : this.state.query,
params : this._getDisjunctiveFacetSearchParams( refinedFacet )
} );
}, this );
// One for each disjunctive facets
forEach(this.state.getRefinedDisjunctiveFacets(), function(refinedFacet) {
queries.push({
indexName: this.index,
query: this.state.query,
params: this._getDisjunctiveFacetSearchParams(refinedFacet)
});
}, this);
// maybe more to get the root level of hierarchical facets when activated
forEach(this.state.getRefinedHierarchicalFacets(), function(refinedFacet) {
var hierarchicalFacet = this.state.getHierarchicalFacetByName(refinedFacet);
var currentRefinement = this.state.getHierarchicalRefinement(refinedFacet);
// if we are deeper than level 0 (starting from `beer > IPA`)
// we want to get the root values
if (currentRefinement.length > 0 && currentRefinement[0].split(this.state._getHierarchicalFacetSeparator(hierarchicalFacet)).length > 1) {
queries.push({
indexName: this.index,
query: this.state.query,
params: this._getDisjunctiveFacetSearchParams(refinedFacet, true)
});
}
}, this);
return queries;

@@ -558,4 +612,4 @@ };

*/
AlgoliaSearchHelper.prototype._handleResponse = function( state, queryId, err, content ) {
if( queryId < this._lastQueryIdReceived ) {
AlgoliaSearchHelper.prototype._handleResponse = function(state, queryId, err, content) {
if (queryId < this._lastQueryIdReceived) {
// Outdated answer

@@ -567,9 +621,10 @@ return;

if ( err ) {
this.emit( "error", err );
if (err) {
this.emit('error', err);
return;
}
var formattedResponse = this.lastResults = new SearchResults( state, content );
this.emit( "result", formattedResponse, state );
var formattedResponse = this.lastResults = new SearchResults(state, content);
this.emit('result', formattedResponse, state);
};

@@ -583,4 +638,6 @@

AlgoliaSearchHelper.prototype._getHitsSearchParams = function() {
var query = this.state.query;
var facets = this.state.facets.concat( this.state.disjunctiveFacets );
var facets = this.state.facets
.concat(this.state.disjunctiveFacets)
.concat(this._getHitsHierarchicalFacetsAttributes());
var facetFilters = this._getFacetFilters();

@@ -590,22 +647,19 @@ var numericFilters = this._getNumericFilters();

var additionalParams = {
facets : facets,
tagFilters : tagFilters
facets: facets,
tagFilters: tagFilters
};
if( this.state.distinct === true || this.state.distinct === false ) {
if (this.state.distinct === true || this.state.distinct === false) {
additionalParams.distinct = this.state.distinct;
}
if( !this.containsRefinement( query, facetFilters, numericFilters, tagFilters ) ) {
additionalParams.distinct = false;
}
if( facetFilters.length > 0 ) {
if (facetFilters.length > 0) {
additionalParams.facetFilters = facetFilters;
}
if( numericFilters.length > 0 ) {
if (numericFilters.length > 0) {
additionalParams.numericFilters = numericFilters;
}
return extend( this.state.getQueryParams(), additionalParams );
return merge(this.state.getQueryParams(), additionalParams);
};

@@ -619,40 +673,43 @@

*/
AlgoliaSearchHelper.prototype._getDisjunctiveFacetSearchParams = function( facet ) {
var query = this.state.query;
var facetFilters = this._getFacetFilters( facet );
var numericFilters = this._getNumericFilters( facet );
AlgoliaSearchHelper.prototype._getDisjunctiveFacetSearchParams = function(facet, hierarchicalRootLevel) {
var facetFilters = this._getFacetFilters(facet, hierarchicalRootLevel);
var numericFilters = this._getNumericFilters(facet);
var tagFilters = this._getTagFilters();
var additionalParams = {
hitsPerPage : 1,
page : 0,
attributesToRetrieve : [],
attributesToHighlight : [],
attributesToSnippet : [],
facets : facet,
tagFilters : tagFilters
hitsPerPage: 1,
page: 0,
attributesToRetrieve: [],
attributesToHighlight: [],
attributesToSnippet: [],
tagFilters: tagFilters
};
if( this.state.distinct === true || this.state.distinct === false ) {
var hierarchicalFacet = this.state.getHierarchicalFacetByName(facet);
if (hierarchicalFacet) {
additionalParams.facets = this._getDisjunctiveHierarchicalFacetAttribute(hierarchicalFacet, hierarchicalRootLevel);
} else {
additionalParams.facets = facet;
}
if (this.state.distinct === true || this.state.distinct === false) {
additionalParams.distinct = this.state.distinct;
}
if( !this.containsRefinement( query, facetFilters, numericFilters, tagFilters ) ) {
additionalParams.distinct = false;
}
if( numericFilters.length > 0 ) {
if (numericFilters.length > 0) {
additionalParams.numericFilters = numericFilters;
}
if( facetFilters.length > 0 ) {
if (facetFilters.length > 0) {
additionalParams.facetFilters = facetFilters;
}
return extend( this.state.getQueryParams(), additionalParams );
return merge(this.state.getQueryParams(), additionalParams);
};
AlgoliaSearchHelper.prototype.containsRefinement = function( query, facetFilters, numericFilters, tagFilters ) {
AlgoliaSearchHelper.prototype.containsRefinement = function(query, facetFilters, numericFilters, tagFilters) {
return query ||
facetFilters.length !== 0 ||
numericFilters.length !== 0 ||
tagFilters.length !== 0;
facetFilters.length !== 0 ||
numericFilters.length !== 0 ||
tagFilters.length !== 0;
};

@@ -666,11 +723,13 @@

*/
AlgoliaSearchHelper.prototype._getNumericFilters = function( facetName ) {
AlgoliaSearchHelper.prototype._getNumericFilters = function(facetName) {
var numericFilters = [];
forEach( this.state.numericRefinements, function( operators, attribute ) {
forEach( operators, function( value, operator ) {
if( facetName !== attribute ) {
numericFilters.push( attribute + operator + value );
forEach(this.state.numericRefinements, function(operators, attribute) {
forEach(operators, function(value, operator) {
if (facetName !== attribute) {
numericFilters.push(attribute + operator + value);
}
} );
} );
});
});
return numericFilters;

@@ -685,7 +744,7 @@ };

AlgoliaSearchHelper.prototype._getTagFilters = function() {
if( this.state.tagFilters ) {
if (this.state.tagFilters) {
return this.state.tagFilters;
}
return this.state.tagRefinements.join( "," );
return this.state.tagRefinements.join(',');
};

@@ -699,5 +758,5 @@

*/
AlgoliaSearchHelper.prototype._hasDisjunctiveRefinements = function( facet ) {
return this.state.disjunctiveRefinements[ facet ] &&
this.state.disjunctiveRefinements[ facet ].length > 0;
AlgoliaSearchHelper.prototype._hasDisjunctiveRefinements = function(facet) {
return this.state.disjunctiveRefinements[facet] &&
this.state.disjunctiveRefinements[facet].length > 0;
};

@@ -712,26 +771,56 @@

*/
AlgoliaSearchHelper.prototype._getFacetFilters = function( facet ) {
AlgoliaSearchHelper.prototype._getFacetFilters = function(facet, hierarchicalRootLevel) {
var facetFilters = [];
forEach( this.state.facetsRefinements, function( facetValues, facetName ) {
forEach( facetValues, function( facetValue ) {
facetFilters.push( facetName + ":" + facetValue );
} );
} );
forEach(this.state.facetsRefinements, function(facetValues, facetName) {
forEach(facetValues, function(facetValue) {
facetFilters.push(facetName + ':' + facetValue);
});
});
forEach( this.state.facetsExcludes, function( facetValues, facetName ) {
forEach( facetValues, function( facetValue ) {
facetFilters.push( facetName + ":-" + facetValue );
} );
} );
forEach(this.state.facetsExcludes, function(facetValues, facetName) {
forEach(facetValues, function(facetValue) {
facetFilters.push(facetName + ':-' + facetValue);
});
});
forEach( this.state.disjunctiveFacetsRefinements, function( facetValues, facetName ) {
if( facetName === facet || !facetValues || facetValues.length === 0 ) return;
forEach(this.state.disjunctiveFacetsRefinements, function(facetValues, facetName) {
if (facetName === facet || !facetValues || facetValues.length === 0) return;
var orFilters = [];
forEach( facetValues, function( facetValue ) {
orFilters.push( facetName + ":" + facetValue );
} );
facetFilters.push( orFilters );
} );
forEach(facetValues, function(facetValue) {
orFilters.push(facetName + ':' + facetValue);
});
facetFilters.push(orFilters);
});
forEach(this.state.hierarchicalFacetsRefinements, function(facetValues, facetName) {
var facetValue = facetValues[0];
if (facetValue === undefined) {
return;
}
var hierarchicalFacet = this.state.getHierarchicalFacetByName(facetName);
var separator = this.state._getHierarchicalFacetSeparator(hierarchicalFacet);
var attributeToRefine;
// we ask for parent facet values only when the `facet` is the current hierarchical facet
if (facet === facetName) {
// if we are at the root level already, no need to ask for facet values, we get them from
// the hits query
if (facetValue.indexOf(separator) === -1 || hierarchicalRootLevel === true) {
return;
}
attributeToRefine = hierarchicalFacet.attributes[facetValue.split(separator).length - 2];
facetValue = facetValue.slice(0, facetValue.lastIndexOf(separator));
} else {
attributeToRefine = hierarchicalFacet.attributes[facetValue.split(separator).length - 1];
}
facetFilters.push([attributeToRefine + ':' + facetValue]);
}, this);
return facetFilters;

@@ -741,5 +830,40 @@ };

AlgoliaSearchHelper.prototype._change = function() {
this.emit( "change", this.state, this.lastResults );
this.emit('change', this.state, this.lastResults);
};
AlgoliaSearchHelper.prototype._getHitsHierarchicalFacetsAttributes = function() {
var out = [];
return reduce(
this.state.hierarchicalFacets,
// ask for as much levels as there's hierarchical refinements
function getHitsAttributesForHierarchicalFacet(allAttributes, hierarchicalFacet) {
var hierarchicalRefinement = this.state.getHierarchicalRefinement(hierarchicalFacet.name)[0];
// if no refinement, ask for root level
if (!hierarchicalRefinement) {
allAttributes.push(hierarchicalFacet.attributes[0]);
return allAttributes;
}
var level = hierarchicalRefinement.split(this.state._getHierarchicalFacetSeparator(hierarchicalFacet)).length;
var newAttributes = hierarchicalFacet.attributes.slice(0, level + 1);
return allAttributes.concat(newAttributes);
}, out, this);
};
AlgoliaSearchHelper.prototype._getDisjunctiveHierarchicalFacetAttribute = function(hierarchicalFacet, rootLevel) {
if (rootLevel === true) {
return [hierarchicalFacet.attributes[0]];
}
var hierarchicalRefinement = this.state.getHierarchicalRefinement(hierarchicalFacet.name)[0] || '';
// if refinement is 'beers > IPA > Flying dog',
// then we want `facets: ['beers > IPA']` as disjunctive facet (parent level values)
var parentLevel = hierarchicalRefinement.split(this.state._getHierarchicalFacetSeparator(hierarchicalFacet)).length - 1;
return hierarchicalFacet.attributes.slice(0, parentLevel + 1);
};
module.exports = AlgoliaSearchHelper;

@@ -1,5 +0,7 @@

"use strict";
var isObject = require( "lodash/lang/isObject" );
var forEach = require( "lodash/collection/forEach" );
'use strict';
var forEach = require('lodash/collection/forEach');
var identity = require('lodash/utility/identity');
var isObject = require('lodash/lang/isObject');
/**

@@ -11,8 +13,8 @@ * Recursively freeze the parts of an object that are not frozen.

*/
var deepFreeze = function( obj ) {
if( !isObject( obj ) ) return obj;
var deepFreeze = function(obj) {
if (!isObject(obj)) return obj;
forEach( obj, deepFreeze );
if( !Object.isFrozen( obj ) ) {
Object.freeze( obj );
forEach(obj, deepFreeze);
if (!Object.isFrozen(obj)) {
Object.freeze(obj);
}

@@ -23,2 +25,2 @@

module.exports = deepFreeze;
module.exports = Object.freeze ? deepFreeze : identity;

@@ -1,18 +0,23 @@

"use strict";
var keys = require( "lodash/object/keys" );
var intersection = require( "lodash/array/intersection" );
var forEach = require( "lodash/collection/forEach" );
var reduce = require( "lodash/collection/reduce" );
var filter = require( "lodash/collection/filter" );
var omit = require( "lodash/object/omit" );
var isEmpty = require( "lodash/lang/isEmpty" );
var isUndefined = require( "lodash/lang/isUndefined" );
var isString = require( "lodash/lang/isString" );
var isFunction = require( "lodash/lang/isFunction" );
'use strict';
var extend = require( "../functions/extend" );
var deepFreeze = require( "../functions/deepFreeze" );
var keys = require('lodash/object/keys');
var intersection = require('lodash/array/intersection');
var forEach = require('lodash/collection/forEach');
var reduce = require('lodash/collection/reduce');
var filter = require('lodash/collection/filter');
var omit = require('lodash/object/omit');
var indexOf = require('lodash/array/indexOf');
var isEmpty = require('lodash/lang/isEmpty');
var isUndefined = require('lodash/lang/isUndefined');
var isString = require('lodash/lang/isString');
var isFunction = require('lodash/lang/isFunction');
var find = require('lodash/collection/find');
var pluck = require('lodash/collection/pluck');
var RefinementList = require( "./RefinementList" );
var defaults = require('lodash/object/defaults');
var merge = require('lodash/object/merge');
var deepFreeze = require('../functions/deepFreeze');
var RefinementList = require('./RefinementList');
/**

@@ -43,4 +48,4 @@ * @typedef {string[]} SearchParameters.FacetList

{
"query" : "",
"disjunctiveFacets" : [
"query": "",
"disjunctiveFacets": [
"customerReviewCount",

@@ -50,17 +55,16 @@ "category",

"manufacturer"
],
"maxValuesPerFacet" : 30,
"page" : 0,
"hitsPerPage" : 10,
"facets" : [
],
"maxValuesPerFacet": 30,
"page": 0,
"hitsPerPage": 10,
"facets": [
"type",
"shipping"
]
]
}
*/
var SearchParameters = function( newParameters ) {
function SearchParameters(newParameters) {
var params = newParameters || {};
//Query
// Query
/**

@@ -71,5 +75,5 @@ * Query string of the instant search. The empty string is a valid query.

*/
this.query = params.query || "";
this.query = params.query || '';
//Facets
// Facets
/**

@@ -85,4 +89,11 @@ * All the facets that will be requested to the server

this.disjunctiveFacets = params.disjunctiveFacets || [];
//Refinements
/**
* All the declared hierarchical facets,
* a hierarchical facet is a disjunctive facet with some specific behavior
* @member {string[]|object[]}
*/
this.hierarchicalFacets = params.hierarchicalFacets || [];
// Refinements
/**
* @private

@@ -109,3 +120,3 @@ * @member {Object.<string, SearchParameters.FacetList>}

* Contains the tags used to refine the query
* Associated property in the query : tagFilters
* Associated property in the query: tagFilters
* @private

@@ -115,2 +126,7 @@ * @member {string[]}

this.tagRefinements = params.tagRefinements || [];
/**
* @private
* @member {Object.<string, SearchParameters.FacetList>}
*/
this.hierarchicalFacetsRefinements = params.hierarchicalFacetsRefinements || {};

@@ -126,3 +142,3 @@ /**

//Misc. parameters
// Misc. parameters
/**

@@ -148,3 +164,3 @@ * Number of hits to be returned by the search API

* How the query should be treated by the search engine.
* Possible values : prefixAll, prefixLast, prefixNone
* Possible values: prefixAll, prefixLast, prefixNone
* @see https://www.algolia.com/doc#queryType

@@ -156,3 +172,3 @@ * @member {string}

* How the typo tolerance behave in the search engine.
* Possible values : true, false, min, strict
* Possible values: true, false, min, strict
* @see https://www.algolia.com/doc#typoTolerance

@@ -307,3 +323,3 @@ * @member {string}

this.insideBoundingBox = params.insideBoundingBox;
};
}

@@ -315,5 +331,6 @@ /**

*/
SearchParameters.make = function makeSearchParameters( newParameters ) {
var instance = new SearchParameters( newParameters );
return deepFreeze( instance );
SearchParameters.make = function makeSearchParameters(newParameters) {
var instance = new SearchParameters(newParameters);
return deepFreeze(instance);
};

@@ -327,18 +344,19 @@

*/
SearchParameters.validate = function( currentState, parameters ) {
SearchParameters.validate = function(currentState, parameters) {
var params = parameters || {};
var ks = keys( params );
var unknownKeys = filter( ks, function( k ) {
return !currentState.hasOwnProperty( k );
} );
if( unknownKeys.length === 1 ) return new Error( "Property " + unknownKeys[0] + " is not defined on SearchParameters (see http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html )" );
if( unknownKeys.length > 1 ) return new Error( "Properties " + unknownKeys.join( " " ) + " are not defined on SearchParameters (see http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html )" );
var ks = keys(params);
var unknownKeys = filter(ks, function(k) {
return !currentState.hasOwnProperty(k);
});
if( currentState.tagFilters && params.tagRefinements && params.tagRefinements.length > 0 ) {
return new Error( "[Tags] Can't switch from the managed tag API to the advanced API. It is probably an error, if it's really what you want, you should first clear the tags with clearTags method." );
if (unknownKeys.length === 1) return new Error('Property ' + unknownKeys[0] + ' is not defined on SearchParameters (see http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html)');
if (unknownKeys.length > 1) return new Error('Properties ' + unknownKeys.join(' ') + ' are not defined on SearchParameters (see http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html)');
if (currentState.tagFilters && params.tagRefinements && params.tagRefinements.length > 0) {
return new Error("[Tags] Can't switch from the managed tag API to the advanced API. It is probably an error, if it's really what you want, you should first clear the tags with clearTags method.");
}
if( currentState.tagRefinements.length > 0 && params.tagFilters ) {
return new Error( "[Tags] Can't switch from the advanced tag API to the managed API. It is probably an error, if it's not, you should first clear the tags with clearTags method." );
if (currentState.tagRefinements.length > 0 && params.tagFilters) {
return new Error("[Tags] Can't switch from the advanced tag API to the managed API. It is probably an error, if it's not, you should first clear the tags with clearTags method.");
}

@@ -350,3 +368,3 @@

SearchParameters.prototype = {
constructor : SearchParameters,
constructor: SearchParameters,

@@ -362,10 +380,11 @@ /**

*/
clearRefinements : function clearRefinements( attribute ) {
return this.setQueryParameters( {
page : 0,
numericRefinements : this._clearNumericRefinements( attribute ),
facetsRefinements : RefinementList.clearRefinement( this.facetsRefinements, attribute, "conjunctiveFacet" ),
facetsExcludes : RefinementList.clearRefinement( this.facetsExcludes, attribute, "exclude" ),
disjunctiveFacetsRefinements : RefinementList.clearRefinement( this.disjunctiveFacetsRefinements, attribute, "disjunctiveFacet" )
} );
clearRefinements: function clearRefinements(attribute) {
return this.setQueryParameters({
page: 0,
numericRefinements: this._clearNumericRefinements(attribute),
facetsRefinements: RefinementList.clearRefinement(this.facetsRefinements, attribute, 'conjunctiveFacet'),
facetsExcludes: RefinementList.clearRefinement(this.facetsExcludes, attribute, 'exclude'),
disjunctiveFacetsRefinements: RefinementList.clearRefinement(this.disjunctiveFacetsRefinements, attribute, 'disjunctiveFacet'),
hierarchicalFacetsRefinements: RefinementList.clearRefinement(this.hierarchicalFacetsRefinements, attribute, 'hierarchicalFacet')
});
},

@@ -377,10 +396,10 @@ /**

*/
clearTags : function clearTags() {
if( this.tagFilters === undefined && this.tagRefinements.length === 0 ) return this;
clearTags: function clearTags() {
if (this.tagFilters === undefined && this.tagRefinements.length === 0) return this;
return this.setQueryParameters( {
page : 0,
tagFilters : undefined,
tagRefinements : []
} );
return this.setQueryParameters({
page: 0,
tagFilters: undefined,
tagRefinements: []
});
},

@@ -393,9 +412,9 @@ /**

*/
setQuery : function setQuery( newQuery ) {
if( newQuery === this.query ) return this;
setQuery: function setQuery(newQuery) {
if (newQuery === this.query) return this;
return this.setQueryParameters( {
query : newQuery,
page : 0
} );
return this.setQueryParameters({
query: newQuery,
page: 0
});
},

@@ -408,8 +427,8 @@ /**

*/
setPage : function setPage( newPage ) {
if( newPage === this.page ) return this;
setPage: function setPage(newPage) {
if (newPage === this.page) return this;
return this.setQueryParameters( {
page : newPage
} );
return this.setQueryParameters({
page: newPage
});
},

@@ -423,6 +442,6 @@ /**

*/
setFacets : function setFacets( facets ) {
return this.setQueryParameters( {
facets : facets
} );
setFacets: function setFacets(facets) {
return this.setQueryParameters({
facets: facets
});
},

@@ -436,6 +455,6 @@ /**

*/
setDisjunctiveFacets : function setDisjunctiveFacets( facets ) {
return this.setQueryParameters( {
disjunctiveFacets : facets
} );
setDisjunctiveFacets: function setDisjunctiveFacets(facets) {
return this.setQueryParameters({
disjunctiveFacets: facets
});
},

@@ -449,9 +468,9 @@ /**

*/
setHitsPerPage : function setHitsPerPage( n ) {
if( this.hitsPerPage === n ) return this;
setHitsPerPage: function setHitsPerPage(n) {
if (this.hitsPerPage === n) return this;
return this.setQueryParameters( {
hitsPerPage : n,
page : 0
} );
return this.setQueryParameters({
hitsPerPage: n,
page: 0
});
},

@@ -465,32 +484,33 @@ /**

*/
setTypoTolerance : function setTypoTolerance( typoTolerance ) {
if( this.typoTolerance === typoTolerance ) return this;
setTypoTolerance: function setTypoTolerance(typoTolerance) {
if (this.typoTolerance === typoTolerance) return this;
return this.setQueryParameters( {
typoTolerance : typoTolerance,
page : 0
} );
return this.setQueryParameters({
typoTolerance: typoTolerance,
page: 0
});
},
/**
* Add or update a numeric filter for a given attribute
* Current limitation of the numeric filters : you can't have more than one value
* Current limitation of the numeric filters: you can't have more than one value
* filtered for each (attribute, oprator). It means that you can't have a filter
* for ( "attribute", "=", 3 ) and ( "attribute", "=", 8 )
* for ("attribute", "=", 3 ) and ("attribute", "=", 8)
* @method
* @param {string} attribute attribute to set the filter on
* @param {string} operator operator of the filter ( possible values : =, >, >=, <, <=, != )
* @param {string} operator operator of the filter (possible values: =, >, >=, <, <=, !=)
* @param {number} value value of the filter
* @return {SearchParameters}
*/
addNumericRefinement : function( attribute, operator, value ) {
if( this.isNumericRefined( attribute, operator, value ) ) return this;
addNumericRefinement: function(attribute, operator, value) {
if (this.isNumericRefined(attribute, operator, value)) return this;
var mod = extend( {}, this.numericRefinements );
mod[ attribute ] = extend( {}, mod[ attribute ] );
mod[ attribute ][ operator ] = value;
var mod = merge({}, this.numericRefinements);
return this.setQueryParameters( {
page : 0,
numericRefinements : mod
} );
mod[attribute] = merge({}, mod[attribute]);
mod[attribute][operator] = value;
return this.setQueryParameters({
page: 0,
numericRefinements: mod
});
},

@@ -502,4 +522,7 @@ /**

*/
getConjunctiveRefinements : function( facetName ) {
return this.facetsRefinements[ facetName ] || [];
getConjunctiveRefinements: function(facetName) {
if (!this.isConjunctiveFacet(facetName)) {
throw new Error(facetName + ' is not defined in the facets attribute of the helper configuration');
}
return this.facetsRefinements[facetName] || [];
},

@@ -511,6 +534,18 @@ /**

*/
getDisjunctiveRefinements : function( facetName ) {
return this.disjunctiveFacetsRefinements[ facetName ] || [];
getDisjunctiveRefinements: function(facetName) {
if (!this.isDisjunctiveFacet(facetName)) {
throw new Error(facetName + ' is not defined in the disjunctiveFacets attribute of the helper configuration');
}
return this.disjunctiveFacetsRefinements[facetName] || [];
},
/**
* Get the list of hierarchical refinements for a single facet
* @param {string} facetName name of the attribute used for facetting
* @return {string[]} list of refinements
*/
getHierarchicalRefinement: function(facetName) {
// we send an array but we currently do not support multiple hierarchicalRefinements for a hierarchicalFacet
return this.hierarchicalFacetsRefinements[facetName] || [];
},
/**
* Get the list of exclude refinements for a single facet

@@ -520,4 +555,7 @@ * @param {string} facetName name of the attribute used for facetting

*/
getExcludeRefinements : function( facetName ) {
return this.facetsExcludes[ facetName ] || [];
getExcludeRefinements: function(facetName) {
if (!this.isConjunctiveFacet(facetName)) {
throw new Error(facetName + ' is not defined in the facets attribute of the helper configuration');
}
return this.facetsExcludes[facetName] || [];
},

@@ -528,14 +566,14 @@ /**

* @param {string} attribute attribute to set the filter on
* @param {string} operator operator of the filter ( possible values : =, >, >=, <, <=, != )
* @param {string} operator operator of the filter (possible values: =, >, >=, <, <=, !=)
* @return {SearchParameters}
*/
removeNumericRefinement : function( attribute, operator ) {
if( !this.isNumericRefined( attribute, operator ) ) return this;
removeNumericRefinement: function(attribute, operator) {
if (!this.isNumericRefined(attribute, operator)) return this;
return this.setQueryParameters( {
page : 0,
numericRefinements : this._clearNumericRefinements( function( value, key ) {
return this.setQueryParameters({
page: 0,
numericRefinements: this._clearNumericRefinements(function(value, key) {
return key === attribute && value.op === operator;
} )
} );
})
});
},

@@ -547,7 +585,7 @@ /**

*/
getNumericRefinements : function( facetName ) {
return this.numericRefinements[ facetName ] || [];
getNumericRefinements: function(facetName) {
return this.numericRefinements[facetName] || [];
},
/**
* Return the current refinement for the ( attribute, operator )
* Return the current refinement for the (attribute, operator)
* @param {string} attribute of the record

@@ -557,4 +595,4 @@ * @param {string} operator applied

*/
getNumericRefinement : function( attribute, operator ) {
return this.numericRefinements[ attribute ] && this.numericRefinements[ attribute ][ operator ];
getNumericRefinement: function(attribute, operator) {
return this.numericRefinements[attribute] && this.numericRefinements[attribute][operator];
},

@@ -571,18 +609,17 @@ /**

*/
_clearNumericRefinements : function _clearNumericRefinements( attribute ) {
if ( isUndefined( attribute ) ) {
_clearNumericRefinements: function _clearNumericRefinements(attribute) {
if (isUndefined(attribute)) {
return {};
}
else if ( isString( attribute ) ) {
return omit( this.numericRefinements, attribute );
}
else if ( isFunction( attribute ) ) {
return reduce( this.numericRefinements, function( memo, operators, key ) {
var operatorList = omit( operators, function( value, operator ) {
return attribute( { val : value, op : operator }, key, "numeric" );
} );
} else if (isString(attribute)) {
return omit(this.numericRefinements, attribute);
} else if (isFunction(attribute)) {
return reduce(this.numericRefinements, function(memo, operators, key) {
var operatorList = omit(operators, function(value, operator) {
return attribute({val: value, op: operator}, key, 'numeric');
});
if( !isEmpty( operatorList ) ) memo[ key ] = operatorList;
if (!isEmpty(operatorList)) memo[key] = operatorList;
return memo;
}, {} );
}, {});
}

@@ -597,8 +634,12 @@ },

*/
addFacetRefinement : function addFacetRefinement( facet, value ) {
if( RefinementList.isRefined( this.facetsRefinements, facet, value ) ) return this;
return this.setQueryParameters( {
page : 0,
facetsRefinements : RefinementList.addRefinement( this.facetsRefinements, facet, value )
} );
addFacetRefinement: function addFacetRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
if (RefinementList.isRefined(this.facetsRefinements, facet, value)) return this;
return this.setQueryParameters({
page: 0,
facetsRefinements: RefinementList.addRefinement(this.facetsRefinements, facet, value)
});
},

@@ -612,8 +653,12 @@ /**

*/
addExcludeRefinement : function addExcludeRefinement( facet, value ) {
if( RefinementList.isRefined( this.facetsExcludes, facet, value ) ) return this;
return this.setQueryParameters( {
page : 0,
facetsExcludes : RefinementList.addRefinement( this.facetsExcludes, facet, value )
} );
addExcludeRefinement: function addExcludeRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
if (RefinementList.isRefined(this.facetsExcludes, facet, value)) return this;
return this.setQueryParameters({
page: 0,
facetsExcludes: RefinementList.addRefinement(this.facetsExcludes, facet, value)
});
},

@@ -627,8 +672,13 @@ /**

*/
addDisjunctiveFacetRefinement : function addDisjunctiveFacetRefinement( facet, value ) {
if( RefinementList.isRefined( this.disjunctiveFacetsRefinements, facet, value ) ) return this;
return this.setQueryParameters( {
page : 0,
disjunctiveFacetsRefinements : RefinementList.addRefinement( this.disjunctiveFacetsRefinements, facet, value )
} );
addDisjunctiveFacetRefinement: function addDisjunctiveFacetRefinement(facet, value) {
if (!this.isDisjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the disjunctiveFacets attribute of the helper configuration');
}
if (RefinementList.isRefined(this.disjunctiveFacetsRefinements, facet, value)) return this;
return this.setQueryParameters({
page: 0,
disjunctiveFacetsRefinements: RefinementList.addRefinement(this.disjunctiveFacetsRefinements, facet, value)
});
},

@@ -640,11 +690,11 @@ /**

*/
addTagRefinement : function addTagRefinement( tag ) {
if( this.isTagRefined( tag ) ) return this;
addTagRefinement: function addTagRefinement(tag) {
if (this.isTagRefined(tag)) return this;
var modification = {
page : 0,
tagRefinements : this.tagRefinements.concat( tag )
page: 0,
tagRefinements: this.tagRefinements.concat(tag)
};
return this.setQueryParameters( modification );
return this.setQueryParameters(modification);
},

@@ -660,9 +710,12 @@ /**

*/
removeFacetRefinement : function removeFacetRefinement( facet, value ) {
if( !RefinementList.isRefined( this.facetsRefinements, facet, value ) ) return this;
removeFacetRefinement: function removeFacetRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
if (!RefinementList.isRefined(this.facetsRefinements, facet, value)) return this;
return this.setQueryParameters( {
page : 0,
facetsRefinements : RefinementList.removeRefinement( this.facetsRefinements, facet, value )
} );
return this.setQueryParameters({
page: 0,
facetsRefinements: RefinementList.removeRefinement(this.facetsRefinements, facet, value)
});
},

@@ -676,9 +729,12 @@ /**

*/
removeExcludeRefinement : function removeExcludeRefinement( facet, value ) {
if( !RefinementList.isRefined( this.facetsExcludes, facet, value ) ) return this;
removeExcludeRefinement: function removeExcludeRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
if (!RefinementList.isRefined(this.facetsExcludes, facet, value)) return this;
return this.setQueryParameters( {
page : 0,
facetsExcludes : RefinementList.removeRefinement( this.facetsExcludes, facet, value )
} );
return this.setQueryParameters({
page: 0,
facetsExcludes: RefinementList.removeRefinement(this.facetsExcludes, facet, value)
});
},

@@ -692,9 +748,12 @@ /**

*/
removeDisjunctiveFacetRefinement : function removeDisjunctiveFacetRefinement( facet, value ) {
if( !RefinementList.isRefined( this.disjunctiveFacetsRefinements, facet, value ) ) return this;
removeDisjunctiveFacetRefinement: function removeDisjunctiveFacetRefinement(facet, value) {
if (!this.isDisjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the disjunctiveFacets attribute of the helper configuration');
}
if (!RefinementList.isRefined(this.disjunctiveFacetsRefinements, facet, value)) return this;
return this.setQueryParameters( {
page : 0,
disjunctiveFacetsRefinements : RefinementList.removeRefinement( this.disjunctiveFacetsRefinements, facet, value )
} );
return this.setQueryParameters({
page: 0,
disjunctiveFacetsRefinements: RefinementList.removeRefinement(this.disjunctiveFacetsRefinements, facet, value)
});
},

@@ -707,11 +766,11 @@ /**

*/
removeTagRefinement : function removeTagRefinement( tag ) {
if( !this.isTagRefined( tag ) ) return this;
removeTagRefinement: function removeTagRefinement(tag) {
if (!this.isTagRefined(tag)) return this;
var modification = {
page : 0,
tagRefinements : filter( this.tagRefinements, function( t ) { return t !== tag; } )
page: 0,
tagRefinements: filter(this.tagRefinements, function(t) { return t !== tag; })
};
return this.setQueryParameters( modification );
return this.setQueryParameters(modification);
},

@@ -725,7 +784,11 @@ /**

*/
toggleFacetRefinement : function toggleFacetRefinement( facet, value ) {
return this.setQueryParameters( {
page : 0,
facetsRefinements : RefinementList.toggleRefinement( this.facetsRefinements, facet, value )
} );
toggleFacetRefinement: function toggleFacetRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
return this.setQueryParameters({
page: 0,
facetsRefinements: RefinementList.toggleRefinement(this.facetsRefinements, facet, value)
});
},

@@ -739,7 +802,11 @@ /**

*/
toggleExcludeFacetRefinement : function toggleExcludeFacetRefinement( facet, value ) {
return this.setQueryParameters( {
page : 0,
facetsExcludes : RefinementList.toggleRefinement( this.facetsExcludes, facet, value )
} );
toggleExcludeFacetRefinement: function toggleExcludeFacetRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
return this.setQueryParameters({
page: 0,
facetsExcludes: RefinementList.toggleRefinement(this.facetsExcludes, facet, value)
});
},

@@ -753,9 +820,56 @@ /**

*/
toggleDisjunctiveFacetRefinement : function toggleDisjunctiveFacetRefinement( facet, value ) {
return this.setQueryParameters( {
page : 0,
disjunctiveFacetsRefinements : RefinementList.toggleRefinement( this.disjunctiveFacetsRefinements, facet, value )
} );
toggleDisjunctiveFacetRefinement: function toggleDisjunctiveFacetRefinement(facet, value) {
if (!this.isDisjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the disjunctiveFacets attribute of the helper configuration');
}
return this.setQueryParameters({
page: 0,
disjunctiveFacetsRefinements: RefinementList.toggleRefinement(this.disjunctiveFacetsRefinements, facet, value)
});
},
/**
* Switch the refinement applied over a facet/value
* @method
* @param {string} facet name of the attribute used for facetting
* @param {value} value value used for filtering
* @return {SearchParameters}
*/
toggleHierarchicalFacetRefinement: function toggleHierarchicalFacetRefinement(facet, value) {
if (!this.isHierarchicalFacet(facet)) {
throw new Error(facet + ' is not defined in the hierarchicalFacets attribute of the helper configuration');
}
var separator = this._getHierarchicalFacetSeparator(this.getHierarchicalFacetByName(facet));
var mod = {};
var upOneOrMultipleLevel = this.hierarchicalFacetsRefinements[facet] !== undefined &&
this.hierarchicalFacetsRefinements[facet].length > 0 && (
// remove current refinement:
// refinement was 'beer > IPA', call is toggleRefine('beer > IPA'), refinement should be `beer`
this.hierarchicalFacetsRefinements[facet][0] === value ||
// remove a parent refinement of the current refinement:
// - refinement was 'beer > IPA > Flying dog'
// - call is toggleRefine('beer > IPA')
// - refinement should be `beer`
this.hierarchicalFacetsRefinements[facet][0].indexOf(value + separator) === 0
);
if (upOneOrMultipleLevel) {
if (value.indexOf(separator) === -1) {
// go back to root level
mod[facet] = [];
} else {
mod[facet] = [value.slice(0, value.lastIndexOf(separator))];
}
} else {
mod[facet] = [value];
}
return this.setQueryParameters({
hierarchicalFacetsRefinements: defaults({}, mod, this.hierarchicalFacetsRefinements)
});
},
/**
* Switch the tag refinement

@@ -766,9 +880,8 @@ * @method

*/
toggleTagRefinement : function toggleTagRefinement( tag ) {
if( this.isTagRefined( tag ) ) {
return this.removeTagRefinement( tag );
toggleTagRefinement: function toggleTagRefinement(tag) {
if (this.isTagRefined(tag)) {
return this.removeTagRefinement(tag);
}
else {
return this.addTagRefinement( tag );
}
return this.addTagRefinement(tag);
},

@@ -781,6 +894,15 @@ /**

*/
isDisjunctiveFacet : function( facet ) {
return this.disjunctiveFacets.indexOf( facet ) > -1;
isDisjunctiveFacet: function(facet) {
return indexOf(this.disjunctiveFacets, facet) > -1;
},
/**
* Test if the facet name is from one of the hierarchical facets
* @method
* @param {string} facetName facet name to test
* @return {boolean}
*/
isHierarchicalFacet: function(facetName) {
return this.getHierarchicalFacetByName(facetName) !== undefined;
},
/**
* Test if the facet name is from one of the conjunctive/normal facets

@@ -791,4 +913,4 @@ * @method

*/
isConjunctiveFacet : function( facet ) {
return this.facets.indexOf( facet ) > -1;
isConjunctiveFacet: function(facet) {
return indexOf(this.facets, facet) > -1;
},

@@ -804,4 +926,7 @@ /**

*/
isFacetRefined : function isFacetRefined( facet, value ) {
return RefinementList.isRefined( this.facetsRefinements, facet, value );
isFacetRefined: function isFacetRefined(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
return RefinementList.isRefined(this.facetsRefinements, facet, value);
},

@@ -817,4 +942,7 @@ /**

*/
isExcludeRefined : function isExcludeRefined( facet, value ) {
return RefinementList.isRefined( this.facetsExcludes, facet, value );
isExcludeRefined: function isExcludeRefined(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
return RefinementList.isRefined(this.facetsExcludes, facet, value);
},

@@ -830,4 +958,7 @@ /**

*/
isDisjunctiveFacetRefined : function isDisjunctiveFacetRefined( facet, value ) {
return RefinementList.isRefined( this.disjunctiveFacetsRefinements, facet, value );
isDisjunctiveFacetRefined: function isDisjunctiveFacetRefined(facet, value) {
if (!this.isDisjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the disjunctiveFacets attribute of the helper configuration');
}
return RefinementList.isRefined(this.disjunctiveFacetsRefinements, facet, value);
},

@@ -844,11 +975,11 @@ /**

*/
isNumericRefined : function isNumericRefined( attribute, operator, value ) {
if( isUndefined( value ) ) {
return this.numericRefinements[ attribute ] &&
!isUndefined( this.numericRefinements[ attribute ][ operator ] );
isNumericRefined: function isNumericRefined(attribute, operator, value) {
if (isUndefined(value)) {
return this.numericRefinements[attribute] &&
!isUndefined(this.numericRefinements[attribute][operator]);
}
return this.numericRefinements[ attribute ] &&
!( isUndefined( this.numericRefinements[ attribute ][ operator ] ) ) &&
this.numericRefinements[ attribute ][ operator ] === value;
return this.numericRefinements[attribute] &&
!isUndefined(this.numericRefinements[attribute][operator]) &&
this.numericRefinements[attribute][operator] === value;
},

@@ -861,4 +992,4 @@ /**

*/
isTagRefined : function isTagRefined( tag ) {
return this.tagRefinements.indexOf( tag ) !== -1;
isTagRefined: function isTagRefined(tag) {
return indexOf(this.tagRefinements, tag) !== -1;
},

@@ -872,11 +1003,29 @@ /**

*/
getRefinedDisjunctiveFacets : function getRefinedDisjunctiveFacets() {
getRefinedDisjunctiveFacets: function getRefinedDisjunctiveFacets() {
// attributes used for numeric filter can also be disjunctive
var disjunctiveNumericRefinedFacets = intersection(
keys( this.numericRefinements ),
keys(this.numericRefinements),
this.disjunctiveFacets
);
return keys( this.disjunctiveFacetsRefinements ).concat( disjunctiveNumericRefinedFacets );
return keys(this.disjunctiveFacetsRefinements)
.concat(disjunctiveNumericRefinedFacets)
.concat(this.getRefinedHierarchicalFacets());
},
/**
* Returns the list of all disjunctive facets refined
* @method
* @param {string} facet name of the attribute used for facetting
* @param {value} value value used for filtering
* @return {string[]}
*/
getRefinedHierarchicalFacets: function getRefinedHierarchicalFacets() {
return intersection(
// enforce the order between the two arrays,
// so that refinement name index === hierarchical facet index
pluck(this.hierarchicalFacets, 'name'),
keys(this.hierarchicalFacetsRefinements)
);
},
/**
* Returned the list of all disjunctive facets not refined

@@ -886,22 +1035,27 @@ * @method

*/
getUnrefinedDisjunctiveFacets : function() {
getUnrefinedDisjunctiveFacets: function() {
var refinedFacets = this.getRefinedDisjunctiveFacets();
return filter( this.disjunctiveFacets, function( f ) {
return refinedFacets.indexOf( f ) === -1;
} );
return filter(this.disjunctiveFacets, function(f) {
return indexOf(refinedFacets, f) === -1;
});
},
managedParameters : [
"facets", "disjunctiveFacets", "facetsRefinements",
"facetsExcludes", "disjunctiveFacetsRefinements",
"numericRefinements", "tagRefinements"
managedParameters: [
'facets', 'disjunctiveFacets', 'facetsRefinements',
'facetsExcludes', 'disjunctiveFacetsRefinements',
'numericRefinements', 'tagRefinements', 'hierarchicalFacets', 'hierarchicalFacetsRefinements'
],
getQueryParams : function getQueryParams() {
getQueryParams: function getQueryParams() {
var managedParameters = this.managedParameters;
return reduce( this, function( memo, value, parameter, parameters ) {
if( managedParameters.indexOf( parameter ) === -1 &&
parameters[ parameter ] !== undefined ) {
memo[ parameter ] = value;
// FIXME with lodash
return reduce(this, function(memo, value, parameter, parameters) {
if (indexOf(managedParameters, parameter) === -1 &&
parameters[parameter] !== undefined) {
memo[parameter] = value;
}
return memo;
}, {} );
}, {});
},

@@ -913,6 +1067,6 @@ /**

*/
getQueryParameter : function getQueryParameter( paramName ) {
if( !this.hasOwnProperty( paramName ) ) throw new Error( "Parameter '" + paramName + "' is not an attribute of SearchParameters (http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html)" );
getQueryParameter: function getQueryParameter(paramName) {
if (!this.hasOwnProperty(paramName)) throw new Error("Parameter '" + paramName + "' is not an attribute of SearchParameters (http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html)");
return this[ paramName ];
return this[paramName];
},

@@ -928,8 +1082,10 @@ /**

*/
setQueryParameter : function setParameter( parameter, value ) {
if( this[ parameter ] === value ) return this;
setQueryParameter: function setParameter(parameter, value) {
if (this[parameter] === value) return this;
var modification = {};
modification[ parameter ] = value;
return this.setQueryParameters( modification );
modification[parameter] = value;
return this.setQueryParameters(modification);
},

@@ -943,15 +1099,18 @@ /**

*/
setQueryParameters : function setQueryParameters( params ) {
var error = SearchParameters.validate( this, params );
if( error ) {
setQueryParameters: function setQueryParameters(params) {
var error = SearchParameters.validate(this, params);
if (error) {
throw error;
}
return this.mutateMe( function merge( newInstance ) {
var ks = keys( params );
forEach( ks, function( k ) {
newInstance[ k ] = params[ k ];
} );
return this.mutateMe(function mergeWith(newInstance) {
var ks = keys(params);
forEach(ks, function(k) {
newInstance[k] = params[k];
});
return newInstance;
} );
});
},

@@ -966,6 +1125,37 @@ /**

*/
mutateMe : function mutateMe( fn ) {
var newState = new ( this.constructor )( this );
fn( newState, this );
return deepFreeze( newState );
mutateMe: function mutateMe(fn) {
var newState = new this.constructor(this);
fn(newState, this);
return deepFreeze(newState);
},
/**
* Helper function to get the hierarchicalFacet separator or the default one (`>`)
* @param {object} hierarchicalFacet
* @return {string} returns the hierarchicalFacet.separator or `>` as default
*/
_getHierarchicalFacetSortBy: function(hierarchicalFacet) {
return hierarchicalFacet.sortBy || ['isRefined:desc', 'name:asc'];
},
/**
* Helper function to get the hierarchicalFacet separator or the default one (`>`)
* @param {object} hierarchicalFacet
* @return {string} returns the hierarchicalFacet.separator or `>` as default
*/
_getHierarchicalFacetSeparator: function(hierarchicalFacet) {
return hierarchicalFacet.separator || ' > ';
},
/**
* Helper function to get the hierarchicalFacet by it's name
* @param {string} hierarchicalFacetName
* @return {object} a hierarchicalFacet
*/
getHierarchicalFacetByName: function(hierarchicalFacetName) {
return find(
this.hierarchicalFacets,
{name: hierarchicalFacetName}
);
}

@@ -972,0 +1162,0 @@ };

@@ -0,1 +1,3 @@

'use strict';
/**

@@ -13,14 +15,12 @@ * Functions to manipulate refinement lists

"use strict";
var extend = require( "../functions/extend" );
var isUndefined = require('lodash/lang/isUndefined');
var isString = require('lodash/lang/isString');
var isFunction = require('lodash/lang/isFunction');
var isEmpty = require('lodash/lang/isEmpty');
var defaults = require('lodash/object/defaults');
var isUndefined = require( "lodash/lang/isUndefined" );
var isString = require( "lodash/lang/isString" );
var isFunction = require( "lodash/lang/isFunction" );
var isEmpty = require( "lodash/lang/isEmpty" );
var reduce = require('lodash/collection/reduce');
var filter = require('lodash/collection/filter');
var omit = require('lodash/object/omit');
var reduce = require( "lodash/collection/reduce" );
var filter = require( "lodash/collection/filter" );
var omit = require( "lodash/object/omit" );
var lib = {

@@ -34,18 +34,21 @@ /**

*/
addRefinement : function addRefinement( refinementList, attribute, value ) {
if( lib.isRefined( refinementList, attribute, value ) ) {
addRefinement: function addRefinement(refinementList, attribute, value) {
if (lib.isRefined(refinementList, attribute, value)) {
return refinementList;
}
var valueAsString = "" + value;
var valueAsString = '' + value;
var facetRefinement = !refinementList[ attribute ] ? [ valueAsString ] : refinementList[ attribute ].concat( valueAsString );
var facetRefinement = !refinementList[attribute] ?
[valueAsString] :
refinementList[attribute].concat(valueAsString);
var mod = {};
mod[ attribute ] = facetRefinement;
return extend( {}, refinementList, mod );
mod[attribute] = facetRefinement;
return defaults({}, mod, refinementList);
},
/**
* Removes refinement(s) for an attribute :
* Removes refinement(s) for an attribute:
* - if the value is specified removes the refinement for the value on the attribute

@@ -58,12 +61,12 @@ * - if no value is specified removes all the refinements for this attribute

*/
removeRefinement : function removeRefinement( refinementList, attribute, value ) {
if( isUndefined( value ) ) {
return lib.clearRefinement( refinementList, attribute );
removeRefinement: function removeRefinement(refinementList, attribute, value) {
if (isUndefined(value)) {
return lib.clearRefinement(refinementList, attribute);
}
var valueAsString = "" + value;
var valueAsString = '' + value;
return lib.clearRefinement( refinementList, function( v, f ) {
return lib.clearRefinement(refinementList, function(v, f) {
return attribute === f && valueAsString === v;
} );
});
},

@@ -77,17 +80,17 @@ /**

*/
toggleRefinement : function toggleRefinement( refinementList, attribute, value ) {
if( isUndefined( value ) ) throw new Error( "toggleRefinement should be used with a value" );
toggleRefinement: function toggleRefinement(refinementList, attribute, value) {
if (isUndefined(value)) throw new Error('toggleRefinement should be used with a value');
if( lib.isRefined( refinementList, attribute, value ) ) {
return lib.removeRefinement( refinementList, attribute, value );
if (lib.isRefined(refinementList, attribute, value)) {
return lib.removeRefinement(refinementList, attribute, value);
}
return lib.addRefinement( refinementList, attribute, value );
return lib.addRefinement(refinementList, attribute, value);
},
/**
* Clear all or parts of a RefinementList. Depending on the arguments, three
* behaviors can happen :
* - if no attribute is provided : clears the whole list
* - if an attribute is provided as a string : clears the list for the specific attribute
* - if an attribute is provided as a function : discards the elements for which the function returns true
* behaviors can happen:
* - if no attribute is provided: clears the whole list
* - if an attribute is provided as a string: clears the list for the specific attribute
* - if an attribute is provided as a function: discards the elements for which the function returns true
* @param {RefinementList} refinementList the initial list

@@ -98,18 +101,17 @@ * @param {string} [attribute] the attribute or function to discard

*/
clearRefinement : function clearRefinement( refinementList, attribute, refinementType ) {
if ( isUndefined( attribute ) ) {
clearRefinement: function clearRefinement(refinementList, attribute, refinementType) {
if (isUndefined(attribute)) {
return {};
}
else if ( isString( attribute ) ) {
return omit( refinementList, attribute );
}
else if ( isFunction( attribute ) ) {
return reduce( refinementList, function( memo, values, key ) {
var facetList = filter( values, function( value ) {
return !attribute( value, key, refinementType );
} );
} else if (isString(attribute)) {
return omit(refinementList, attribute);
} else if (isFunction(attribute)) {
return reduce(refinementList, function(memo, values, key) {
var facetList = filter(values, function(value) {
return !attribute(value, key, refinementType);
});
if( !isEmpty( facetList ) ) memo[ key ] = facetList;
if (!isEmpty(facetList)) memo[key] = facetList;
return memo;
}, {} );
}, {});
}

@@ -126,14 +128,15 @@ },

*/
isRefined : function isRefined( refinementList, attribute, refinementValue ) {
var containsRefinements = refinementList[ attribute ] &&
refinementList[ attribute ].length > 0;
isRefined: function isRefined(refinementList, attribute, refinementValue) {
var indexOf = require('lodash/array/indexOf');
if( isUndefined( refinementValue ) ) {
var containsRefinements = !!refinementList[attribute] &&
refinementList[attribute].length > 0;
if (isUndefined(refinementValue) || !containsRefinements) {
return containsRefinements;
}
var refinementValueAsString = "" + refinementValue;
var refinementValueAsString = '' + refinementValue;
return containsRefinements &&
refinementList[ attribute ].indexOf( refinementValueAsString ) !== -1;
return indexOf(refinementList[attribute], refinementValueAsString) !== -1;
}

@@ -140,0 +143,0 @@ };

@@ -1,9 +0,16 @@

"use strict";
var forEach = require( "lodash/collection/forEach" );
var compact = require( "lodash/array/compact" );
var sum = require( "lodash/collection/sum" );
var find = require( "lodash/collection/find" );
'use strict';
var extend = require( "../functions/extend" );
var forEach = require('lodash/collection/forEach');
var compact = require('lodash/array/compact');
var indexOf = require('lodash/array/indexOf');
var sum = require('lodash/collection/sum');
var find = require('lodash/collection/find');
var includes = require('lodash/collection/includes');
var map = require('lodash/collection/map');
var findIndex = require('lodash/array/findIndex');
var defaults = require('lodash/object/defaults');
var merge = require('lodash/object/merge');
var generateHierarchicalTree = require('./generate-hierarchical-tree');
/**

@@ -13,14 +20,16 @@ * @typedef SearchResults.Facet

* @property {string} name name of the attribute in the record
* @property {object.<string, number>} data the facetting data : value, number of entries
* @property {object.<string, number>} data the facetting data: value, number of entries
* @property {object} stats undefined unless facet_stats is retrieved from algolia
*/
function getIndices( obj ) {
function getIndices(obj) {
var indices = {};
forEach( obj, function( val, idx ) { indices[ val ] = idx; } );
forEach(obj, function(val, idx) { indices[val] = idx; });
return indices;
}
function assignFacetStats( dest, facetStats, key ) {
if ( facetStats && facetStats[key] ) {
function assignFacetStats(dest, facetStats, key) {
if (facetStats && facetStats[key]) {
dest.stats = facetStats[key];

@@ -30,2 +39,11 @@ }

function findMatchingHierarchicalFacetFromAttributeName(hierarchicalFacets, hierarchicalAttributeName) {
return find(
hierarchicalFacets,
function facetKeyMatchesAttribute(hierarchicalFacet) {
return includes(hierarchicalFacet.attributes, hierarchicalAttributeName);
}
);
}
/**

@@ -40,124 +58,124 @@ * Constructor for SearchResults

{
"hitsPerPage" : 10,
"processingTimeMS" : 2,
"facets" : [
"hitsPerPage": 10,
"processingTimeMS": 2,
"facets": [
{
"name" : "type",
"data" : {
"HardGood" : 6627,
"BlackTie" : 550,
"Music" : 665,
"Software" : 131,
"Game" : 456,
"Movie" : 1571
"name": "type",
"data": {
"HardGood": 6627,
"BlackTie": 550,
"Music": 665,
"Software": 131,
"Game": 456,
"Movie": 1571
},
"exhaustive" : false
"exhaustive": false
},
{
"exhaustive" : false,
"data" : {
"Free shipping" : 5507
"exhaustive": false,
"data": {
"Free shipping": 5507
},
"name" : "shipping"
"name": "shipping"
}
],
"hits" : [
],
"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" : []
"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" : []
"category": {
"matchLevel": "none",
"value": "Computer Security Software",
"matchedWords": []
},
"manufacturer" : {
"matchedWords" : [],
"value" : "Webroot",
"matchLevel" : "none"
"manufacturer": {
"matchedWords": [],
"value": "Webroot",
"matchLevel": "none"
},
"name" : {
"value" : "Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows",
"matchedWords" : [],
"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"
"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" : [
],
"nbHits": 10000,
"disjunctiveFacets": [
{
"exhaustive" : false,
"data" : {
"5" : 183,
"12" : 112,
"7" : 149,
"exhaustive": false,
"data": {
"5": 183,
"12": 112,
"7": 149,
...
},
"name" : "customerReviewCount",
"stats" : {
"max" : 7461,
"avg" : 157.939,
"min" : 1
"name": "customerReviewCount",
"stats": {
"max": 7461,
"avg": 157.939,
"min": 1
}
},
{
"data" : {
"Printer Ink" : 142,
"Wireless Speakers" : 60,
"Point & Shoot Cameras" : 48,
"data": {
"Printer Ink": 142,
"Wireless Speakers": 60,
"Point & Shoot Cameras": 48,
...
},
"name" : "category",
"exhaustive" : false
"name": "category",
"exhaustive": false
},
{
"exhaustive" : false,
"data" : {
"> 5000" : 2,
"1 - 50" : 6524,
"501 - 2000" : 566,
"201 - 500" : 1501,
"101 - 200" : 1360,
"2001 - 5000" : 47
"exhaustive": false,
"data": {
"> 5000": 2,
"1 - 50": 6524,
"501 - 2000": 566,
"201 - 500": 1501,
"101 - 200": 1360,
"2001 - 5000": 47
},
"name" : "salePrice_range"
"name": "salePrice_range"
},
{
"data" : {
"Dynex™" : 202,
"Insignia™" : 230,
"PNY" : 72,
"data": {
"Dynex™": 202,
"Insignia™": 230,
"PNY": 72,
...
},
"name" : "manufacturer",
"exhaustive" : false
"name": "manufacturer",
"exhaustive": false
}
],
"query" : "",
"nbPages" : 100,
"page" : 0,
"index" : "bestbuy"
],
"query": "",
"nbPages": 100,
"page": 0,
"index": "bestbuy"
}
**/
var SearchResults = function( state, algoliaResponse ) {
var mainSubResponse = algoliaResponse.results[ 0 ];
function SearchResults(state, algoliaResponse) {
var mainSubResponse = algoliaResponse.results[0];

@@ -204,3 +222,3 @@ /**

*/
this.processingTimeMS = sum( algoliaResponse.results, "processingTimeMS" );
this.processingTimeMS = sum(algoliaResponse.results, 'processingTimeMS');
/**

@@ -212,2 +230,9 @@ * disjunctive facets results

/**
* disjunctive facets results
* @member {SearchResults.Facet[]}
*/
this.hierarchicalFacets = map(state.hierarchicalFacets, function initFutureTree() {
return [];
});
/**
* other facets results

@@ -220,77 +245,140 @@ * @member {SearchResults.Facet[]}

var facetsIndices = getIndices( state.facets );
var disjunctiveFacetsIndices = getIndices( state.disjunctiveFacets );
var facetsIndices = getIndices(state.facets);
var disjunctiveFacetsIndices = getIndices(state.disjunctiveFacets);
var nextDisjunctiveResult = 1;
//Since we send request only for disjunctive facets that have been refined,
//we get the facets informations from the first, general, response.
forEach( mainSubResponse.facets, function( facetValueObject, facetKey ) {
var isFacetDisjunctive = state.disjunctiveFacets.indexOf( facetKey ) !== -1;
var position = isFacetDisjunctive ? disjunctiveFacetsIndices[ facetKey ] :
facetsIndices[ facetKey ];
if( isFacetDisjunctive ) {
this.disjunctiveFacets[ position ] = {
name : facetKey,
data : facetValueObject,
exhaustive : mainSubResponse.exhaustiveFacetsCount
};
assignFacetStats( this.disjunctiveFacets[ position ], mainSubResponse.facets_stats, facetKey );
// Since we send request only for disjunctive facets that have been refined,
// we get the facets informations from the first, general, response.
forEach(mainSubResponse.facets, function(facetValueObject, facetKey) {
var hierarchicalFacet;
if (hierarchicalFacet = findMatchingHierarchicalFacetFromAttributeName(state.hierarchicalFacets, facetKey)) {
this.hierarchicalFacets[findIndex(state.hierarchicalFacets, {name: hierarchicalFacet.name})].push({
attribute: facetKey,
data: facetValueObject,
exhaustive: mainSubResponse.exhaustiveFacetsCount
});
} else {
var isFacetDisjunctive = indexOf(state.disjunctiveFacets, facetKey) !== -1;
var position = isFacetDisjunctive ? disjunctiveFacetsIndices[facetKey] :
facetsIndices[facetKey];
if (isFacetDisjunctive) {
this.disjunctiveFacets[position] = {
name: facetKey,
data: facetValueObject,
exhaustive: mainSubResponse.exhaustiveFacetsCount
};
assignFacetStats(this.disjunctiveFacets[position], mainSubResponse.facets_stats, facetKey);
} else {
this.facets[position] = {
name: facetKey,
data: facetValueObject,
exhaustive: mainSubResponse.exhaustiveFacetsCount
};
assignFacetStats(this.facets[position], mainSubResponse.facets_stats, facetKey);
}
}
else {
this.facets[ position ] = {
name : facetKey,
data : facetValueObject,
exhaustive : mainSubResponse.exhaustiveFacetsCount
};
assignFacetStats( this.facets[ position ], mainSubResponse.facets_stats, facetKey );
}
}, this );
}, this);
// aggregate the refined disjunctive facets
forEach( disjunctiveFacets, function( disjunctiveFacet, idx ) {
var result = algoliaResponse.results[ idx + 1 ];
forEach(disjunctiveFacets, function(disjunctiveFacet) {
var result = algoliaResponse.results[nextDisjunctiveResult];
var hierarchicalFacet = state.getHierarchicalFacetByName(disjunctiveFacet);
// There should be only item in facets.
forEach( result.facets, function( facetResults, dfacet ) {
var position = disjunctiveFacetsIndices[ dfacet ];
forEach(result.facets, function(facetResults, dfacet) {
var position;
var dataFromMainRequest = ( mainSubResponse.facets && mainSubResponse.facets[ dfacet ] ) || {};
this.disjunctiveFacets[ position ] = {
name : dfacet,
data : extend( {}, dataFromMainRequest, facetResults ),
exhaustive : result.exhaustiveFacetsCount
};
assignFacetStats( this.disjunctiveFacets[ position ], result.facets_stats, dfacet );
if (hierarchicalFacet) {
position = findIndex(state.hierarchicalFacets, {name: hierarchicalFacet.name});
var attributeIndex = findIndex(this.hierarchicalFacets[position], {attribute: dfacet});
this.hierarchicalFacets[position][attributeIndex].data = merge({}, this.hierarchicalFacets[position][attributeIndex].data, facetResults);
} else {
position = disjunctiveFacetsIndices[dfacet];
if ( state.disjunctiveFacetsRefinements[dfacet] ) {
forEach( state.disjunctiveFacetsRefinements[ dfacet ], function( refinementValue ) {
// add the disjunctive refinements if it is no more retrieved
if ( !this.disjunctiveFacets[position].data[refinementValue] &&
state.disjunctiveFacetsRefinements[dfacet].indexOf( refinementValue ) > -1 ) {
this.disjunctiveFacets[position].data[refinementValue] = 0;
}
}, this );
var dataFromMainRequest = mainSubResponse.facets && mainSubResponse.facets[dfacet] || {};
this.disjunctiveFacets[position] = {
name: dfacet,
data: defaults({}, facetResults, dataFromMainRequest),
exhaustive: result.exhaustiveFacetsCount
};
assignFacetStats(this.disjunctiveFacets[position], result.facets_stats, dfacet);
if (state.disjunctiveFacetsRefinements[dfacet]) {
forEach(state.disjunctiveFacetsRefinements[dfacet], function(refinementValue) {
// add the disjunctive refinements if it is no more retrieved
if (!this.disjunctiveFacets[position].data[refinementValue] &&
indexOf(state.disjunctiveFacetsRefinements[dfacet], refinementValue) > -1) {
this.disjunctiveFacets[position].data[refinementValue] = 0;
}
}, this);
}
}
}, this );
}, this );
}, this);
nextDisjunctiveResult++;
}, this);
// if we have some root level values for hierarchical facets, merge them
forEach(state.getRefinedHierarchicalFacets(), function(refinedFacet) {
var hierarchicalFacet = state.getHierarchicalFacetByName(refinedFacet);
var currentRefinement = state.getHierarchicalRefinement(refinedFacet);
// if we are already at a root refinement (or no refinement at all), there is no
// root level values request
if (currentRefinement.length === 0 || currentRefinement[0].split(state._getHierarchicalFacetSeparator(hierarchicalFacet)).length < 2) {
return;
}
var result = algoliaResponse.results[nextDisjunctiveResult];
forEach(result.facets, function(facetResults, dfacet) {
var position = findIndex(state.hierarchicalFacets, {name: hierarchicalFacet.name});
var attributeIndex = findIndex(this.hierarchicalFacets[position], {attribute: dfacet});
// when we always get root levels, if the hits refinement is `beers > IPA` (count: 5),
// then the disjunctive values will be `beers` (count: 100),
// but we do not want to display
// | beers (100)
// > IPA (5)
// We want
// | beers (5)
// > IPA (5)
var defaultData = {};
if (currentRefinement.length > 0) {
var root = currentRefinement[0].split(state._getHierarchicalFacetSeparator(hierarchicalFacet))[0];
defaultData[root] = this.hierarchicalFacets[position][attributeIndex].data[root];
}
this.hierarchicalFacets[position][attributeIndex].data = defaults(defaultData, facetResults, this.hierarchicalFacets[position][attributeIndex].data);
}, this);
nextDisjunctiveResult++;
}, this);
// add the excludes
forEach( state.facetsExcludes, function( excludes, facetName ) {
var position = facetsIndices[ facetName ];
this.facets[ position ] = {
name : facetName,
data : mainSubResponse.facets[ facetName ],
exhaustive : mainSubResponse.exhaustiveFacetsCount
forEach(state.facetsExcludes, function(excludes, facetName) {
var position = facetsIndices[facetName];
this.facets[position] = {
name: facetName,
data: mainSubResponse.facets[facetName],
exhaustive: mainSubResponse.exhaustiveFacetsCount
};
forEach( excludes, function( facetValue ) {
this.facets[ position ] = this.facets[ position ] || { name : facetName };
this.facets[ position ].data = this.facets[ position ].data || {};
this.facets[ position ].data[ facetValue ] = 0;
}, this );
}, this );
forEach(excludes, function(facetValue) {
this.facets[position] = this.facets[position] || {name: facetName};
this.facets[position].data = this.facets[position].data || {};
this.facets[position].data[facetValue] = 0;
}, this);
}, this);
this.facets = compact( this.facets );
this.disjunctiveFacets = compact( this.disjunctiveFacets );
this.hierarchicalFacets = map(this.hierarchicalFacets, generateHierarchicalTree(state));
this.facets = compact(this.facets);
this.disjunctiveFacets = compact(this.disjunctiveFacets);
this._state = state;
};
}

@@ -302,8 +390,10 @@ /**

*/
SearchResults.prototype.getFacetByName = function( name ) {
var isName = function( facet ) { return facet.name === name; };
var indexInFacets = find( this.facets, isName );
return indexInFacets || find( this.disjunctiveFacets, isName );
SearchResults.prototype.getFacetByName = function(name) {
var predicate = {name: name};
return find(this.facets, predicate) ||
find(this.disjunctiveFacets, predicate) ||
find(this.hierarchicalFacets, predicate);
};
module.exports = SearchResults;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc