@meilisearch/instant-meilisearch
Version:
The search client to use Meilisearch with InstantSearch.
127 lines • 4.73 kB
JavaScript
const filterEscapeRegExp = /([\\"])/g;
function getValueWithEscapedBackslashesAndQuotes(value) {
return value.replace(filterEscapeRegExp, '\\$1');
}
/**
* Transform InstantSearch [facet
* filter](https://www.algolia.com/doc/api-reference/api-parameters/facetFilters/)
* to Meilisearch compatible filter format. Change sign from `:` to `=`
* "facet:facetValue" becomes "facet=facetValue"
*
* Wrap both the facet and its facet value between quotes. This avoids
* formatting issues on facets containing multiple words. Escape backslash \ and
* quote " characters.
*
* 'My facet:My facet value' becomes '"My facet":"My facet value"'
*
* @param {string} filter
* @returns {string}
*/
function transformFacetFilter(filter) {
const escapedFilter = getValueWithEscapedBackslashesAndQuotes(filter);
const colonIndex = escapedFilter.indexOf(':');
const attribute = escapedFilter.slice(0, colonIndex);
const value = escapedFilter.slice(colonIndex + 1);
return `"${attribute}"="${value}"`;
}
/**
* Transform InstantSearch [numeric
* filter](https://www.algolia.com/doc/api-reference/api-parameters/numericFilters/)
* to Meilisearch compatible filter format.
*
* 'price:5.99 TO 100' becomes '"price" 5.99 TO 100'
*
* 'price = 5.99' becomes '"price"=5.99'
*
* Wrap the attribute between quotes. Escape backslash \ and quote " characters.
*
* @param {string} filter
* @returns {string}
*/
function transformNumericFilter(filter) {
const splitNumericFilter = () => {
const attributeMatch = filter.match(/^([^<!>:=]*)([<!>:=]+)(.*)$/);
if (attributeMatch) {
const [attribute, dirtyOperator, valueEnd] = attributeMatch.slice(1);
const operatorMatch = dirtyOperator.match(/^([<!>]?=|<|>|:){1}(.*)/) || [
'',
'',
];
const [operator, valueStart] = operatorMatch.slice(1);
const cleanedValue = valueStart + valueEnd;
return [attribute, operator, cleanedValue];
}
return [filter, '', ''];
};
const [attribute, operator, value] = splitNumericFilter();
const escapedAttribute = getValueWithEscapedBackslashesAndQuotes(attribute);
return `"${escapedAttribute.trim()}"${operator === ':' ? ' ' : operator}${value.trim()}`;
}
/**
* Iterate over all filters. Return the filters in a Meilisearch compatible
* format.
*
* @param {(filter: string) => string} transformCallback
* @param {SearchContext['facetFilters']} filters
* @returns {Filter}
*/
function transformFilters(transformCallback, filters) {
return typeof filters === 'string'
? transformCallback(filters)
: filters.map((filter) => typeof filter === 'string'
? transformCallback(filter)
: filter.map((nestedFilter) => transformCallback(nestedFilter)));
}
/**
* Return the filter in an array if it is a string If filter is array, return
* without change.
*
* @param {Filter} [filter]
* @returns {Array | undefined}
*/
function filterToArray(filter) {
return typeof filter === 'string' ? [filter] : filter;
}
/**
* Merge filters, transformedNumericFilters and transformedFacetFilters
* together.
*
* @param {string} filters
* @param {Filter} transformedNumericFilters
* @param {Filter} transformedFacetFilters
* @returns {Filter}
*/
function mergeFilters(filters, transformedNumericFilters, transformedFacetFilters) {
const adaptedNumericFilters = filterToArray(transformedNumericFilters);
const adaptedFacetFilters = filterToArray(transformedFacetFilters);
const adaptedFilters = [];
if (filters !== undefined) {
adaptedFilters.push(filters);
}
if (adaptedNumericFilters !== undefined) {
adaptedFilters.push(...adaptedNumericFilters);
}
if (adaptedFacetFilters !== undefined) {
adaptedFilters.push(...adaptedFacetFilters);
}
return adaptedFilters;
}
/**
* Adapt instantsearch.js filters to Meilisearch filters by combining and
* transforming all provided filters.
*
* @param {string | undefined} filters
* @param {SearchContext['numericFilters']} numericFilters
* @param {SearchContext['facetFilters']} facetFilters
* @returns {Filter}
*/
export function adaptFilters(filters, numericFilters, facetFilters) {
const transformedNumericFilters = numericFilters !== undefined
? transformFilters(transformNumericFilter, numericFilters)
: numericFilters;
const transformedFacetFilters = facetFilters !== undefined
? transformFilters(transformFacetFilter, facetFilters)
: facetFilters;
return mergeFilters(filters, transformedNumericFilters, transformedFacetFilters);
}
//# sourceMappingURL=filter-adapter.js.map