{
"query": "",
"disjunctiveFacets": [
"customerReviewCount",
"category",
"salePrice_range",
"manufacturer"
],
"maxValuesPerFacet": 30,
"page": 0,
"hitsPerPage": 10,
"facets": [
"type",
"shipping"
]
}
*/
function SearchParameters(newParameters) {
var params = newParameters ? SearchParameters._parseNumbers(newParameters) : {};
/**
* This attribute contains the list of all the conjunctive facets
* used. This list will be added to requested facets in the
* [facets attribute](https://www.algolia.com/doc/rest-api/search#param-facets) sent to algolia.
* @member {string[]}
*/
this.facets = params.facets || [];
/**
* This attribute contains the list of all the disjunctive facets
* used. This list will be added to requested facets in the
* [facets attribute](https://www.algolia.com/doc/rest-api/search#param-facets) sent to algolia.
* @member {string[]}
*/
this.disjunctiveFacets = params.disjunctiveFacets || [];
/**
* This attribute contains the list of all the hierarchical facets
* used. This list will be added to requested facets in the
* [facets attribute](https://www.algolia.com/doc/rest-api/search#param-facets) sent to algolia.
* Hierarchical facets are a sub type of disjunctive facets that
* let you filter faceted attributes hierarchically.
* @member {string[]|object[]}
*/
this.hierarchicalFacets = params.hierarchicalFacets || []; // Refinements
/**
* This attribute contains all the filters that need to be
* applied on the conjunctive facets. Each facet must be properly
* defined in the `facets` attribute.
*
* The key is the name of the facet, and the `FacetList` contains all
* filters selected for the associated facet name.
*
* When querying algolia, the values stored in this attribute will
* be translated into the `facetFilters` attribute.
* @member {Object.}
*/
this.facetsRefinements = params.facetsRefinements || {};
/**
* This attribute contains all the filters that need to be
* excluded from the conjunctive facets. Each facet must be properly
* defined in the `facets` attribute.
*
* The key is the name of the facet, and the `FacetList` contains all
* filters excluded for the associated facet name.
*
* When querying algolia, the values stored in this attribute will
* be translated into the `facetFilters` attribute.
* @member {Object.}
*/
this.facetsExcludes = params.facetsExcludes || {};
/**
* This attribute contains all the filters that need to be
* applied on the disjunctive facets. Each facet must be properly
* defined in the `disjunctiveFacets` attribute.
*
* The key is the name of the facet, and the `FacetList` contains all
* filters selected for the associated facet name.
*
* When querying algolia, the values stored in this attribute will
* be translated into the `facetFilters` attribute.
* @member {Object.}
*/
this.disjunctiveFacetsRefinements = params.disjunctiveFacetsRefinements || {};
/**
* This attribute contains all the filters that need to be
* applied on the numeric attributes.
*
* The key is the name of the attribute, and the value is the
* filters to apply to this attribute.
*
* When querying algolia, the values stored in this attribute will
* be translated into the `numericFilters` attribute.
* @member {Object.}
*/
this.numericRefinements = params.numericRefinements || {};
/**
* This attribute contains all the tags used to refine the query.
*
* When querying algolia, the values stored in this attribute will
* be translated into the `tagFilters` attribute.
* @member {string[]}
*/
this.tagRefinements = params.tagRefinements || [];
/**
* This attribute contains all the filters that need to be
* applied on the hierarchical facets. Each facet must be properly
* defined in the `hierarchicalFacets` attribute.
*
* The key is the name of the facet, and the `FacetList` contains all
* filters selected for the associated facet name. The FacetList values
* are structured as a string that contain the values for each level
* separated by the configured separator.
*
* When querying algolia, the values stored in this attribute will
* be translated into the `facetFilters` attribute.
* @member {Object.}
*/
this.hierarchicalFacetsRefinements = params.hierarchicalFacetsRefinements || {};
var self = this;
Object.keys(params).forEach(function (paramName) {
var isKeyKnown = SearchParameters.PARAMETERS.indexOf(paramName) !== -1;
var isValueDefined = params[paramName] !== undefined;
if (!isKeyKnown && isValueDefined) {
self[paramName] = params[paramName];
}
});
}
/**
* List all the properties in SearchParameters and therefore all the known Algolia properties
* This doesn't contain any beta/hidden features.
* @private
*/
SearchParameters.PARAMETERS = Object.keys(new SearchParameters());
/**
* @private
* @param {object} partialState full or part of a state
* @return {object} a new object with the number keys as number
*/
SearchParameters._parseNumbers = function (partialState) {
// Do not reparse numbers in SearchParameters, they ought to be parsed already
if (partialState instanceof SearchParameters) return partialState;
var numbers = {};
var numberKeys = ['aroundPrecision', 'aroundRadius', 'getRankingInfo', 'minWordSizefor2Typos', 'minWordSizefor1Typo', 'page', 'maxValuesPerFacet', 'distinct', 'minimumAroundRadius', 'hitsPerPage', 'minProximity'];
numberKeys.forEach(function (k) {
var value = partialState[k];
if (typeof value === 'string') {
var parsedValue = parseFloat(value); // global isNaN is ok to use here, value is only number or NaN
numbers[k] = isNaN(parsedValue) ? value : parsedValue;
}
}); // there's two formats of insideBoundingBox, we need to parse
// the one which is an array of float geo rectangles
if (Array.isArray(partialState.insideBoundingBox)) {
numbers.insideBoundingBox = partialState.insideBoundingBox.map(function (geoRect) {
return geoRect.map(function (value) {
return parseFloat(value);
});
});
}
if (partialState.numericRefinements) {
var numericRefinements = {};
Object.keys(partialState.numericRefinements).forEach(function (attribute) {
var operators = partialState.numericRefinements[attribute] || {};
numericRefinements[attribute] = {};
Object.keys(operators).forEach(function (operator) {
var values = operators[operator];
var parsedValues = values.map(function (v) {
if (Array.isArray(v)) {
return v.map(function (vPrime) {
if (typeof vPrime === 'string') {
return parseFloat(vPrime);
}
return vPrime;
});
} else if (typeof v === 'string') {
return parseFloat(v);
}
return v;
});
numericRefinements[attribute][operator] = parsedValues;
});
});
numbers.numericRefinements = numericRefinements;
}
return merge$1({}, partialState, numbers);
};
/**
* Factory for SearchParameters
* @param {object|SearchParameters} newParameters existing parameters or partial
* object for the properties of a new SearchParameters
* @return {SearchParameters} frozen instance of SearchParameters
*/
SearchParameters.make = function makeSearchParameters(newParameters) {
var instance = new SearchParameters(newParameters);
var hierarchicalFacets = newParameters.hierarchicalFacets || [];
hierarchicalFacets.forEach(function (facet) {
if (facet.rootPath) {
var currentRefinement = instance.getHierarchicalRefinement(facet.name);
if (currentRefinement.length > 0 && currentRefinement[0].indexOf(facet.rootPath) !== 0) {
instance = instance.clearRefinements(facet.name);
} // get it again in case it has been cleared
currentRefinement = instance.getHierarchicalRefinement(facet.name);
if (currentRefinement.length === 0) {
instance = instance.toggleHierarchicalFacetRefinement(facet.name, facet.rootPath);
}
}
});
return instance;
};
/**
* Validates the new parameters based on the previous state
* @param {SearchParameters} currentState the current state
* @param {object|SearchParameters} parameters the new parameters to set
* @return {Error|null} Error if the modification is invalid, null otherwise
*/
SearchParameters.validate = function (currentState, parameters) {
var params = parameters || {};
if (currentState.tagFilters && params.tagRefinements && params.tagRefinements.length > 0) {
return new Error('[Tags] Cannot switch from the managed tag API to the advanced API. It is probably ' + 'an error, if it is really what you want, you should first clear the tags with clearTags method.');
}
if (currentState.tagRefinements.length > 0 && params.tagFilters) {
return new Error('[Tags] Cannot switch from the advanced tag API to the managed API. It is probably ' + 'an error, if it is not, you should first clear the tags with clearTags method.');
}
if (currentState.numericFilters && params.numericRefinements && objectHasKeys_1(params.numericRefinements)) {
return new Error("[Numeric filters] Can't switch from the advanced to the managed API. It" + ' is probably an error, if this is really what you want, you have to first' + ' clear the numeric filters.');
}
if (objectHasKeys_1(currentState.numericRefinements) && params.numericFilters) {
return new Error("[Numeric filters] Can't switch from the managed API to the advanced. It" + ' is probably an error, if this is really what you want, you have to first' + ' clear the numeric filters.');
}
return null;
};
SearchParameters.prototype = {
constructor: SearchParameters,
/**
* Remove all refinements (disjunctive + conjunctive + excludes + numeric filters)
* @method
* @param {undefined|string|SearchParameters.clearCallback} [attribute] optional string or function
* - If not given, means to clear all the filters.
* - If `string`, means to clear all refinements for the `attribute` named filter.
* - If `function`, means to clear all the refinements that return truthy values.
* @return {SearchParameters}
*/
clearRefinements: function clearRefinements(attribute) {
var clear = RefinementList.clearRefinement;
var patch = {
numericRefinements: this._clearNumericRefinements(attribute),
facetsRefinements: clear(this.facetsRefinements, attribute, 'conjunctiveFacet'),
facetsExcludes: clear(this.facetsExcludes, attribute, 'exclude'),
disjunctiveFacetsRefinements: clear(this.disjunctiveFacetsRefinements, attribute, 'disjunctiveFacet'),
hierarchicalFacetsRefinements: clear(this.hierarchicalFacetsRefinements, attribute, 'hierarchicalFacet')
};
if (patch.numericRefinements === this.numericRefinements && patch.facetsRefinements === this.facetsRefinements && patch.facetsExcludes === this.facetsExcludes && patch.disjunctiveFacetsRefinements === this.disjunctiveFacetsRefinements && patch.hierarchicalFacetsRefinements === this.hierarchicalFacetsRefinements) {
return this;
}
return this.setQueryParameters(patch);
},
/**
* Remove all the refined tags from the SearchParameters
* @method
* @return {SearchParameters}
*/
clearTags: function clearTags() {
if (this.tagFilters === undefined && this.tagRefinements.length === 0) return this;
return this.setQueryParameters({
tagFilters: undefined,
tagRefinements: []
});
},
/**
* Set the index.
* @method
* @param {string} index the index name
* @return {SearchParameters}
*/
setIndex: function setIndex(index) {
if (index === this.index) return this;
return this.setQueryParameters({
index: index
});
},
/**
* Query setter
* @method
* @param {string} newQuery value for the new query
* @return {SearchParameters}
*/
setQuery: function setQuery(newQuery) {
if (newQuery === this.query) return this;
return this.setQueryParameters({
query: newQuery
});
},
/**
* Page setter
* @method
* @param {number} newPage new page number
* @return {SearchParameters}
*/
setPage: function setPage(newPage) {
if (newPage === this.page) return this;
return this.setQueryParameters({
page: newPage
});
},
/**
* Facets setter
* The facets are the simple facets, used for conjunctive (and) faceting.
* @method
* @param {string[]} facets all the attributes of the algolia records used for conjunctive faceting
* @return {SearchParameters}
*/
setFacets: function setFacets(facets) {
return this.setQueryParameters({
facets: facets
});
},
/**
* Disjunctive facets setter
* Change the list of disjunctive (or) facets the helper chan handle.
* @method
* @param {string[]} facets all the attributes of the algolia records used for disjunctive faceting
* @return {SearchParameters}
*/
setDisjunctiveFacets: function setDisjunctiveFacets(facets) {
return this.setQueryParameters({
disjunctiveFacets: facets
});
},
/**
* HitsPerPage setter
* Hits per page represents the number of hits retrieved for this query
* @method
* @param {number} n number of hits retrieved per page of results
* @return {SearchParameters}
*/
setHitsPerPage: function setHitsPerPage(n) {
if (this.hitsPerPage === n) return this;
return this.setQueryParameters({
hitsPerPage: n
});
},
/**
* typoTolerance setter
* Set the value of typoTolerance
* @method
* @param {string} typoTolerance new value of typoTolerance ("true", "false", "min" or "strict")
* @return {SearchParameters}
*/
setTypoTolerance: function setTypoTolerance(typoTolerance) {
if (this.typoTolerance === typoTolerance) return this;
return this.setQueryParameters({
typoTolerance: typoTolerance
});
},
/**
* Add a numeric filter for a given attribute
* When value is an array, they are combined with OR
* When value is a single value, it will combined with AND
* @method
* @param {string} attribute attribute to set the filter on
* @param {string} operator operator of the filter (possible values: =, >, >=, <, <=, !=)
* @param {number | number[]} value value of the filter
* @return {SearchParameters}
* @example
* // for price = 50 or 40
* searchparameter.addNumericRefinement('price', '=', [50, 40]);
* @example
* // for size = 38 and 40
* searchparameter.addNumericRefinement('size', '=', 38);
* searchparameter.addNumericRefinement('size', '=', 40);
*/
addNumericRefinement: function addNumericRefinement(attribute, operator, v) {
var value = valToNumber_1(v);
if (this.isNumericRefined(attribute, operator, value)) return this;
var mod = merge$1({}, this.numericRefinements);
mod[attribute] = merge$1({}, mod[attribute]);
if (mod[attribute][operator]) {
// Array copy
mod[attribute][operator] = mod[attribute][operator].slice(); // Add the element. Concat can't be used here because value can be an array.
mod[attribute][operator].push(value);
} else {
mod[attribute][operator] = [value];
}
return this.setQueryParameters({
numericRefinements: mod
});
},
/**
* Get the list of conjunctive refinements for a single facet
* @param {string} facetName name of the attribute used for faceting
* @return {string[]} list of refinements
*/
getConjunctiveRefinements: function getConjunctiveRefinements(facetName) {
if (!this.isConjunctiveFacet(facetName)) {
return [];
}
return this.facetsRefinements[facetName] || [];
},
/**
* Get the list of disjunctive refinements for a single facet
* @param {string} facetName name of the attribute used for faceting
* @return {string[]} list of refinements
*/
getDisjunctiveRefinements: function getDisjunctiveRefinements(facetName) {
if (!this.isDisjunctiveFacet(facetName)) {
return [];
}
return this.disjunctiveFacetsRefinements[facetName] || [];
},
/**
* Get the list of hierarchical refinements for a single facet
* @param {string} facetName name of the attribute used for faceting
* @return {string[]} list of refinements
*/
getHierarchicalRefinement: function getHierarchicalRefinement(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
* @param {string} facetName name of the attribute used for faceting
* @return {string[]} list of refinements
*/
getExcludeRefinements: function getExcludeRefinements(facetName) {
if (!this.isConjunctiveFacet(facetName)) {
return [];
}
return this.facetsExcludes[facetName] || [];
},
/**
* Remove all the numeric filter for a given (attribute, operator)
* @method
* @param {string} attribute attribute to set the filter on
* @param {string} [operator] operator of the filter (possible values: =, >, >=, <, <=, !=)
* @param {number} [number] the value to be removed
* @return {SearchParameters}
*/
removeNumericRefinement: function removeNumericRefinement(attribute, operator, paramValue) {
if (paramValue !== undefined) {
if (!this.isNumericRefined(attribute, operator, paramValue)) {
return this;
}
return this.setQueryParameters({
numericRefinements: this._clearNumericRefinements(function (value, key) {
return key === attribute && value.op === operator && isEqualNumericRefinement(value.val, valToNumber_1(paramValue));
})
});
} else if (operator !== undefined) {
if (!this.isNumericRefined(attribute, operator)) return this;
return this.setQueryParameters({
numericRefinements: this._clearNumericRefinements(function (value, key) {
return key === attribute && value.op === operator;
})
});
}
if (!this.isNumericRefined(attribute)) return this;
return this.setQueryParameters({
numericRefinements: this._clearNumericRefinements(function (value, key) {
return key === attribute;
})
});
},
/**
* Get the list of numeric refinements for a single facet
* @param {string} facetName name of the attribute used for faceting
* @return {SearchParameters.OperatorList[]} list of refinements
*/
getNumericRefinements: function getNumericRefinements(facetName) {
return this.numericRefinements[facetName] || {};
},
/**
* Return the current refinement for the (attribute, operator)
* @param {string} attribute attribute in the record
* @param {string} operator operator applied on the refined values
* @return {Array.} refined values
*/
getNumericRefinement: function getNumericRefinement(attribute, operator) {
return this.numericRefinements[attribute] && this.numericRefinements[attribute][operator];
},
/**
* Clear numeric filters.
* @method
* @private
* @param {string|SearchParameters.clearCallback} [attribute] optional string or function
* - If not given, means to clear all the filters.
* - If `string`, means to clear all refinements for the `attribute` named filter.
* - If `function`, means to clear all the refinements that return truthy values.
* @return {Object.}
*/
_clearNumericRefinements: function _clearNumericRefinements(attribute) {
if (attribute === undefined) {
if (!objectHasKeys_1(this.numericRefinements)) {
return this.numericRefinements;
}
return {};
} else if (typeof attribute === 'string') {
if (!objectHasKeys_1(this.numericRefinements[attribute])) {
return this.numericRefinements;
}
return omit$1(this.numericRefinements, attribute);
} else if (typeof attribute === 'function') {
var hasChanged = false;
var numericRefinements = this.numericRefinements;
var newNumericRefinements = Object.keys(numericRefinements).reduce(function (memo, key) {
var operators = numericRefinements[key];
var operatorList = {};
operators = operators || {};
Object.keys(operators).forEach(function (operator) {
var values = operators[operator] || [];
var outValues = [];
values.forEach(function (value) {
var predicateResult = attribute({
val: value,
op: operator
}, key, 'numeric');
if (!predicateResult) outValues.push(value);
});
if (outValues.length !== values.length) {
hasChanged = true;
}
operatorList[operator] = outValues;
});
memo[key] = operatorList;
return memo;
}, {});
if (hasChanged) return newNumericRefinements;
return this.numericRefinements;
}
},
/**
* Add a facet to the facets attribute of the helper configuration, if it
* isn't already present.
* @method
* @param {string} facet facet name to add
* @return {SearchParameters}
*/
addFacet: function addFacet(facet) {
if (this.isConjunctiveFacet(facet)) {
return this;
}
return this.setQueryParameters({
facets: this.facets.concat([facet])
});
},
/**
* Add a disjunctive facet to the disjunctiveFacets attribute of the helper
* configuration, if it isn't already present.
* @method
* @param {string} facet disjunctive facet name to add
* @return {SearchParameters}
*/
addDisjunctiveFacet: function addDisjunctiveFacet(facet) {
if (this.isDisjunctiveFacet(facet)) {
return this;
}
return this.setQueryParameters({
disjunctiveFacets: this.disjunctiveFacets.concat([facet])
});
},
/**
* Add a hierarchical facet to the hierarchicalFacets attribute of the helper
* configuration.
* @method
* @param {object} hierarchicalFacet hierarchical facet to add
* @return {SearchParameters}
* @throws will throw an error if a hierarchical facet with the same name was already declared
*/
addHierarchicalFacet: function addHierarchicalFacet(hierarchicalFacet) {
if (this.isHierarchicalFacet(hierarchicalFacet.name)) {
throw new Error('Cannot declare two hierarchical facets with the same name: `' + hierarchicalFacet.name + '`');
}
return this.setQueryParameters({
hierarchicalFacets: this.hierarchicalFacets.concat([hierarchicalFacet])
});
},
/**
* Add a refinement on a "normal" facet
* @method
* @param {string} facet attribute to apply the faceting on
* @param {string} value value of the attribute (will be converted to string)
* @return {SearchParameters}
*/
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({
facetsRefinements: RefinementList.addRefinement(this.facetsRefinements, facet, value)
});
},
/**
* Exclude a value from a "normal" facet
* @method
* @param {string} facet attribute to apply the exclusion on
* @param {string} value value of the attribute (will be converted to string)
* @return {SearchParameters}
*/
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({
facetsExcludes: RefinementList.addRefinement(this.facetsExcludes, facet, value)
});
},
/**
* Adds a refinement on a disjunctive facet.
* @method
* @param {string} facet attribute to apply the faceting on
* @param {string} value value of the attribute (will be converted to string)
* @return {SearchParameters}
*/
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({
disjunctiveFacetsRefinements: RefinementList.addRefinement(this.disjunctiveFacetsRefinements, facet, value)
});
},
/**
* addTagRefinement adds a tag to the list used to filter the results
* @param {string} tag tag to be added
* @return {SearchParameters}
*/
addTagRefinement: function addTagRefinement(tag) {
if (this.isTagRefined(tag)) return this;
var modification = {
tagRefinements: this.tagRefinements.concat(tag)
};
return this.setQueryParameters(modification);
},
/**
* Remove a facet from the facets attribute of the helper configuration, if it
* is present.
* @method
* @param {string} facet facet name to remove
* @return {SearchParameters}
*/
removeFacet: function removeFacet(facet) {
if (!this.isConjunctiveFacet(facet)) {
return this;
}
return this.clearRefinements(facet).setQueryParameters({
facets: this.facets.filter(function (f) {
return f !== facet;
})
});
},
/**
* Remove a disjunctive facet from the disjunctiveFacets attribute of the
* helper configuration, if it is present.
* @method
* @param {string} facet disjunctive facet name to remove
* @return {SearchParameters}
*/
removeDisjunctiveFacet: function removeDisjunctiveFacet(facet) {
if (!this.isDisjunctiveFacet(facet)) {
return this;
}
return this.clearRefinements(facet).setQueryParameters({
disjunctiveFacets: this.disjunctiveFacets.filter(function (f) {
return f !== facet;
})
});
},
/**
* Remove a hierarchical facet from the hierarchicalFacets attribute of the
* helper configuration, if it is present.
* @method
* @param {string} facet hierarchical facet name to remove
* @return {SearchParameters}
*/
removeHierarchicalFacet: function removeHierarchicalFacet(facet) {
if (!this.isHierarchicalFacet(facet)) {
return this;
}
return this.clearRefinements(facet).setQueryParameters({
hierarchicalFacets: this.hierarchicalFacets.filter(function (f) {
return f.name !== facet;
})
});
},
/**
* Remove a refinement set on facet. If a value is provided, it will clear the
* refinement for the given value, otherwise it will clear all the refinement
* values for the faceted attribute.
* @method
* @param {string} facet name of the attribute used for faceting
* @param {string} [value] value used to filter
* @return {SearchParameters}
*/
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({
facetsRefinements: RefinementList.removeRefinement(this.facetsRefinements, facet, value)
});
},
/**
* Remove a negative refinement on a facet
* @method
* @param {string} facet name of the attribute used for faceting
* @param {string} value value used to filter
* @return {SearchParameters}
*/
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({
facetsExcludes: RefinementList.removeRefinement(this.facetsExcludes, facet, value)
});
},
/**
* Remove a refinement on a disjunctive facet
* @method
* @param {string} facet name of the attribute used for faceting
* @param {string} value value used to filter
* @return {SearchParameters}
*/
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({
disjunctiveFacetsRefinements: RefinementList.removeRefinement(this.disjunctiveFacetsRefinements, facet, value)
});
},
/**
* Remove a tag from the list of tag refinements
* @method
* @param {string} tag the tag to remove
* @return {SearchParameters}
*/
removeTagRefinement: function removeTagRefinement(tag) {
if (!this.isTagRefined(tag)) return this;
var modification = {
tagRefinements: this.tagRefinements.filter(function (t) {
return t !== tag;
})
};
return this.setQueryParameters(modification);
},
/**
* Generic toggle refinement method to use with facet, disjunctive facets
* and hierarchical facets
* @param {string} facet the facet to refine
* @param {string} value the associated value
* @return {SearchParameters}
* @throws will throw an error if the facet is not declared in the settings of the helper
* @deprecated since version 2.19.0, see {@link SearchParameters#toggleFacetRefinement}
*/
toggleRefinement: function toggleRefinement(facet, value) {
return this.toggleFacetRefinement(facet, value);
},
/**
* Generic toggle refinement method to use with facet, disjunctive facets
* and hierarchical facets
* @param {string} facet the facet to refine
* @param {string} value the associated value
* @return {SearchParameters}
* @throws will throw an error if the facet is not declared in the settings of the helper
*/
toggleFacetRefinement: function toggleFacetRefinement(facet, value) {
if (this.isHierarchicalFacet(facet)) {
return this.toggleHierarchicalFacetRefinement(facet, value);
} else if (this.isConjunctiveFacet(facet)) {
return this.toggleConjunctiveFacetRefinement(facet, value);
} else if (this.isDisjunctiveFacet(facet)) {
return this.toggleDisjunctiveFacetRefinement(facet, value);
}
throw new Error('Cannot refine the undeclared facet ' + facet + '; it should be added to the helper options facets, disjunctiveFacets or hierarchicalFacets');
},
/**
* Switch the refinement applied over a facet/value
* @method
* @param {string} facet name of the attribute used for faceting
* @param {value} value value used for filtering
* @return {SearchParameters}
*/
toggleConjunctiveFacetRefinement: function toggleConjunctiveFacetRefinement(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
throw new Error(facet + ' is not defined in the facets attribute of the helper configuration');
}
return this.setQueryParameters({
facetsRefinements: RefinementList.toggleRefinement(this.facetsRefinements, facet, value)
});
},
/**
* Switch the refinement applied over a facet/value
* @method
* @param {string} facet name of the attribute used for faceting
* @param {value} value value used for filtering
* @return {SearchParameters}
*/
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({
facetsExcludes: RefinementList.toggleRefinement(this.facetsExcludes, facet, value)
});
},
/**
* Switch the refinement applied over a facet/value
* @method
* @param {string} facet name of the attribute used for faceting
* @param {value} value value used for filtering
* @return {SearchParameters}
*/
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({
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 faceting
* @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: defaultsPure({}, mod, this.hierarchicalFacetsRefinements)
});
},
/**
* Adds a refinement on a hierarchical facet.
* @param {string} facet the facet name
* @param {string} path the hierarchical facet path
* @return {SearchParameter} the new state
* @throws Error if the facet is not defined or if the facet is refined
*/
addHierarchicalFacetRefinement: function addHierarchicalFacetRefinement(facet, path) {
if (this.isHierarchicalFacetRefined(facet)) {
throw new Error(facet + ' is already refined.');
}
if (!this.isHierarchicalFacet(facet)) {
throw new Error(facet + ' is not defined in the hierarchicalFacets attribute of the helper configuration.');
}
var mod = {};
mod[facet] = [path];
return this.setQueryParameters({
hierarchicalFacetsRefinements: defaultsPure({}, mod, this.hierarchicalFacetsRefinements)
});
},
/**
* Removes the refinement set on a hierarchical facet.
* @param {string} facet the facet name
* @return {SearchParameter} the new state
* @throws Error if the facet is not defined or if the facet is not refined
*/
removeHierarchicalFacetRefinement: function removeHierarchicalFacetRefinement(facet) {
if (!this.isHierarchicalFacetRefined(facet)) {
throw new Error(facet + ' is not refined.');
}
var mod = {};
mod[facet] = [];
return this.setQueryParameters({
hierarchicalFacetsRefinements: defaultsPure({}, mod, this.hierarchicalFacetsRefinements)
});
},
/**
* Switch the tag refinement
* @method
* @param {string} tag the tag to remove or add
* @return {SearchParameters}
*/
toggleTagRefinement: function toggleTagRefinement(tag) {
if (this.isTagRefined(tag)) {
return this.removeTagRefinement(tag);
}
return this.addTagRefinement(tag);
},
/**
* Test if the facet name is from one of the disjunctive facets
* @method
* @param {string} facet facet name to test
* @return {boolean}
*/
isDisjunctiveFacet: function isDisjunctiveFacet(facet) {
return this.disjunctiveFacets.indexOf(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 isHierarchicalFacet(facetName) {
return this.getHierarchicalFacetByName(facetName) !== undefined;
},
/**
* Test if the facet name is from one of the conjunctive/normal facets
* @method
* @param {string} facet facet name to test
* @return {boolean}
*/
isConjunctiveFacet: function isConjunctiveFacet(facet) {
return this.facets.indexOf(facet) > -1;
},
/**
* Returns true if the facet is refined, either for a specific value or in
* general.
* @method
* @param {string} facet name of the attribute for used for faceting
* @param {string} value, optional value. If passed will test that this value
* is filtering the given facet.
* @return {boolean} returns true if refined
*/
isFacetRefined: function isFacetRefined(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
return false;
}
return RefinementList.isRefined(this.facetsRefinements, facet, value);
},
/**
* Returns true if the facet contains exclusions or if a specific value is
* excluded.
*
* @method
* @param {string} facet name of the attribute for used for faceting
* @param {string} [value] optional value. If passed will test that this value
* is filtering the given facet.
* @return {boolean} returns true if refined
*/
isExcludeRefined: function isExcludeRefined(facet, value) {
if (!this.isConjunctiveFacet(facet)) {
return false;
}
return RefinementList.isRefined(this.facetsExcludes, facet, value);
},
/**
* Returns true if the facet contains a refinement, or if a value passed is a
* refinement for the facet.
* @method
* @param {string} facet name of the attribute for used for faceting
* @param {string} value optional, will test if the value is used for refinement
* if there is one, otherwise will test if the facet contains any refinement
* @return {boolean}
*/
isDisjunctiveFacetRefined: function isDisjunctiveFacetRefined(facet, value) {
if (!this.isDisjunctiveFacet(facet)) {
return false;
}
return RefinementList.isRefined(this.disjunctiveFacetsRefinements, facet, value);
},
/**
* Returns true if the facet contains a refinement, or if a value passed is a
* refinement for the facet.
* @method
* @param {string} facet name of the attribute for used for faceting
* @param {string} value optional, will test if the value is used for refinement
* if there is one, otherwise will test if the facet contains any refinement
* @return {boolean}
*/
isHierarchicalFacetRefined: function isHierarchicalFacetRefined(facet, value) {
if (!this.isHierarchicalFacet(facet)) {
return false;
}
var refinements = this.getHierarchicalRefinement(facet);
if (!value) {
return refinements.length > 0;
}
return refinements.indexOf(value) !== -1;
},
/**
* Test if the triple (attribute, operator, value) is already refined.
* If only the attribute and the operator are provided, it tests if the
* contains any refinement value.
* @method
* @param {string} attribute attribute for which the refinement is applied
* @param {string} [operator] operator of the refinement
* @param {string} [value] value of the refinement
* @return {boolean} true if it is refined
*/
isNumericRefined: function isNumericRefined(attribute, operator, value) {
if (value === undefined && operator === undefined) {
return !!this.numericRefinements[attribute];
}
var isOperatorDefined = this.numericRefinements[attribute] && this.numericRefinements[attribute][operator] !== undefined;
if (value === undefined || !isOperatorDefined) {
return isOperatorDefined;
}
var parsedValue = valToNumber_1(value);
var isAttributeValueDefined = findArray(this.numericRefinements[attribute][operator], parsedValue) !== undefined;
return isOperatorDefined && isAttributeValueDefined;
},
/**
* Returns true if the tag refined, false otherwise
* @method
* @param {string} tag the tag to check
* @return {boolean}
*/
isTagRefined: function isTagRefined(tag) {
return this.tagRefinements.indexOf(tag) !== -1;
},
/**
* Returns the list of all disjunctive facets refined
* @method
* @param {string} facet name of the attribute used for faceting
* @param {value} value value used for filtering
* @return {string[]}
*/
getRefinedDisjunctiveFacets: function getRefinedDisjunctiveFacets() {
var self = this; // attributes used for numeric filter can also be disjunctive
var disjunctiveNumericRefinedFacets = intersection_1(Object.keys(this.numericRefinements).filter(function (facet) {
return Object.keys(self.numericRefinements[facet]).length > 0;
}), this.disjunctiveFacets);
return Object.keys(this.disjunctiveFacetsRefinements).filter(function (facet) {
return self.disjunctiveFacetsRefinements[facet].length > 0;
}).concat(disjunctiveNumericRefinedFacets).concat(this.getRefinedHierarchicalFacets());
},
/**
* Returns the list of all disjunctive facets refined
* @method
* @param {string} facet name of the attribute used for faceting
* @param {value} value value used for filtering
* @return {string[]}
*/
getRefinedHierarchicalFacets: function getRefinedHierarchicalFacets() {
var self = this;
return intersection_1( // enforce the order between the two arrays,
// so that refinement name index === hierarchical facet index
this.hierarchicalFacets.map(function (facet) {
return facet.name;
}), Object.keys(this.hierarchicalFacetsRefinements).filter(function (facet) {
return self.hierarchicalFacetsRefinements[facet].length > 0;
}));
},
/**
* Returned the list of all disjunctive facets not refined
* @method
* @return {string[]}
*/
getUnrefinedDisjunctiveFacets: function getUnrefinedDisjunctiveFacets() {
var refinedFacets = this.getRefinedDisjunctiveFacets();
return this.disjunctiveFacets.filter(function (f) {
return refinedFacets.indexOf(f) === -1;
});
},
managedParameters: ['index', 'facets', 'disjunctiveFacets', 'facetsRefinements', 'facetsExcludes', 'disjunctiveFacetsRefinements', 'numericRefinements', 'tagRefinements', 'hierarchicalFacets', 'hierarchicalFacetsRefinements'],
getQueryParams: function getQueryParams() {
var managedParameters = this.managedParameters;
var queryParams = {};
var self = this;
Object.keys(this).forEach(function (paramName) {
var paramValue = self[paramName];
if (managedParameters.indexOf(paramName) === -1 && paramValue !== undefined) {
queryParams[paramName] = paramValue;
}
});
return queryParams;
},
/**
* Let the user set a specific value for a given parameter. Will return the
* same instance if the parameter is invalid or if the value is the same as the
* previous one.
* @method
* @param {string} parameter the parameter name
* @param {any} value the value to be set, must be compliant with the definition
* of the attribute on the object
* @return {SearchParameters} the updated state
*/
setQueryParameter: function setParameter(parameter, value) {
if (this[parameter] === value) return this;
var modification = {};
modification[parameter] = value;
return this.setQueryParameters(modification);
},
/**
* Let the user set any of the parameters with a plain object.
* @method
* @param {object} params all the keys and the values to be updated
* @return {SearchParameters} a new updated instance
*/
setQueryParameters: function setQueryParameters(params) {
if (!params) return this;
var error = SearchParameters.validate(this, params);
if (error) {
throw error;
}
var self = this;
var nextWithNumbers = SearchParameters._parseNumbers(params);
var previousPlainObject = Object.keys(this).reduce(function (acc, key) {
acc[key] = self[key];
return acc;
}, {});
var nextPlainObject = Object.keys(nextWithNumbers).reduce(function (previous, key) {
var isPreviousValueDefined = previous[key] !== undefined;
var isNextValueDefined = nextWithNumbers[key] !== undefined;
if (isPreviousValueDefined && !isNextValueDefined) {
return omit$1(previous, [key]);
}
if (isNextValueDefined) {
previous[key] = nextWithNumbers[key];
}
return previous;
}, previousPlainObject);
return new this.constructor(nextPlainObject);
},
/**
* Returns a new instance with the page reset. Two scenarios possible:
* the page is omitted -> return the given instance
* the page is set -> return a new instance with a page of 0
* @return {SearchParameters} a new updated instance
*/
resetPage: function resetPage() {
if (this.page === undefined) {
return this;
}
return this.setPage(0);
},
/**
* 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 _getHierarchicalFacetSortBy(hierarchicalFacet) {
return hierarchicalFacet.sortBy || ['isRefined:desc', 'name:asc'];
},
/**
* Helper function to get the hierarchicalFacet separator or the default one (`>`)
* @private
* @param {object} hierarchicalFacet
* @return {string} returns the hierarchicalFacet.separator or `>` as default
*/
_getHierarchicalFacetSeparator: function _getHierarchicalFacetSeparator(hierarchicalFacet) {
return hierarchicalFacet.separator || ' > ';
},
/**
* Helper function to get the hierarchicalFacet prefix path or null
* @private
* @param {object} hierarchicalFacet
* @return {string} returns the hierarchicalFacet.rootPath or null as default
*/
_getHierarchicalRootPath: function _getHierarchicalRootPath(hierarchicalFacet) {
return hierarchicalFacet.rootPath || null;
},
/**
* Helper function to check if we show the parent level of the hierarchicalFacet
* @private
* @param {object} hierarchicalFacet
* @return {string} returns the hierarchicalFacet.showParentLevel or true as default
*/
_getHierarchicalShowParentLevel: function _getHierarchicalShowParentLevel(hierarchicalFacet) {
if (typeof hierarchicalFacet.showParentLevel === 'boolean') {
return hierarchicalFacet.showParentLevel;
}
return true;
},
/**
* Helper function to get the hierarchicalFacet by it's name
* @param {string} hierarchicalFacetName
* @return {object} a hierarchicalFacet
*/
getHierarchicalFacetByName: function getHierarchicalFacetByName(hierarchicalFacetName) {
return find$1(this.hierarchicalFacets, function (f) {
return f.name === hierarchicalFacetName;
});
},
/**
* Get the current breadcrumb for a hierarchical facet, as an array
* @param {string} facetName Hierarchical facet name
* @return {array.} the path as an array of string
*/
getHierarchicalFacetBreadcrumb: function getHierarchicalFacetBreadcrumb(facetName) {
if (!this.isHierarchicalFacet(facetName)) {
return [];
}
var refinement = this.getHierarchicalRefinement(facetName)[0];
if (!refinement) return [];
var separator = this._getHierarchicalFacetSeparator(this.getHierarchicalFacetByName(facetName));
var path = refinement.split(separator);
return path.map(function (part) {
return part.trim();
});
},
toString: function toString() {
return JSON.stringify(this, null, 2);
}
};
/**
* Callback used for clearRefinement method
* @callback SearchParameters.clearCallback
* @param {OperatorList|FacetList} value the value of the filter
* @param {string} key the current attribute name
* @param {string} type `numeric`, `disjunctiveFacet`, `conjunctiveFacet`, `hierarchicalFacet` or `exclude`
* depending on the type of facet
* @return {boolean} `true` if the element should be removed. `false` otherwise.
*/
var SearchParameters_1 = SearchParameters;
function compareAscending(value, other) {
if (value !== other) {
var valIsDefined = value !== undefined;
var valIsNull = value === null;
var othIsDefined = other !== undefined;
var othIsNull = other === null;
if (!othIsNull && value > other || valIsNull && othIsDefined || !valIsDefined) {
return 1;
}
if (!valIsNull && value < other || othIsNull && valIsDefined || !othIsDefined) {
return -1;
}
}
return 0;
}
/**
* @param {Array