Skip to content

Commit e338adf

Browse files
Merge #1105
1105: Fix multi words filters not formatted correctly r=bidoubiwa a=bidoubiwa fixes: #1083 Facets containing multiple words, for example `In stock`, were wrongly formatted. Meilisearch requires multi-words in filters to be wrapped in double quotes. The solution provided is to wrap every facet attribute and facet value in double quotes. Before: 'In stock="value"' After '"In stock"="value"' Co-authored-by: Charlotte Vermandel <[email protected]>
2 parents ed92bd2 + bcf09c9 commit e338adf

File tree

4 files changed

+47
-25
lines changed

4 files changed

+47
-25
lines changed

.changeset/tender-dancers-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@meilisearch/instant-meilisearch": patch
3+
---
4+
5+
Fixes a bug where if a facet contained multiple words it failed

packages/instant-meilisearch/src/adapter/search-request-adapter/__tests__/search-params.test.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,25 @@ describe('Parameters adapter', () => {
2626
})
2727

2828
expect(searchParams.filter).toStrictEqual([
29-
['genres="Drama"', 'genres="Thriller"'],
30-
['title="Ariel"'],
29+
['"genres"="Drama"', '"genres"="Thriller"'],
30+
['"title"="Ariel"'],
3131
])
3232
expect(searchParams.sort).toStrictEqual(['id < 1'])
3333
expect(searchParams.attributesToHighlight).toContain('*')
3434
expect(searchParams.attributesToHighlight?.length).toBe(1)
3535
})
3636

37+
test('adapting multi-word filters', () => {
38+
const searchParams = adaptSearchParams({
39+
...DEFAULT_CONTEXT,
40+
facetFilters: [['My Genre:Science Fiction']],
41+
})
42+
43+
expect(searchParams.filter).toStrictEqual([
44+
['"My Genre"="Science Fiction"'],
45+
])
46+
})
47+
3748
test('adapting a searchContext with matching strategy', () => {
3849
const searchParams = adaptSearchParams({
3950
...DEFAULT_CONTEXT,
@@ -55,8 +66,8 @@ describe('Geo filter adapter', () => {
5566

5667
expect(searchParams.filter).toStrictEqual([
5768
'_geoBoundingBox([0, 0], [0, 0])',
58-
['genres="Drama"', 'genres="Thriller"'],
59-
['title="Ariel"'],
69+
['"genres"="Drama"', '"genres"="Thriller"'],
70+
['"title"="Ariel"'],
6071
])
6172
expect(searchParams.sort).toStrictEqual(['id < 1'])
6273
expect(searchParams.attributesToHighlight).toContain('*')
@@ -72,8 +83,8 @@ describe('Geo filter adapter', () => {
7283

7384
expect(searchParams.filter).toEqual([
7485
'_geoBoundingBox([0, 0], [0, 0])',
75-
['genres="Drama"', 'genres="Thriller"'],
76-
['title="Ariel"'],
86+
['"genres"="Drama"', '"genres"="Thriller"'],
87+
['"title"="Ariel"'],
7788
])
7889
expect(searchParams.attributesToHighlight).toContain('*')
7990
expect(searchParams.attributesToHighlight?.length).toBe(1)

packages/instant-meilisearch/src/adapter/search-request-adapter/filter-adapter.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,41 @@
11
import type { Filter, SearchContext } from '../../types'
2-
import { replaceColonByEqualSign } from '../../utils'
32

43
/**
5-
* Transform InstantSearch filter to Meilisearch filter.
6-
* Change sign from `:` to `=` in nested filter object.
7-
* example: [`genres:comedy`] becomes [`genres=comedy`]
4+
* Transform InstantSearch filter to Meilisearch compatible filter format.
5+
* Change sign from `:` to `=`
6+
* "facet:facetValue" becomes "facet=facetValue"
7+
*
8+
* Wrap both the facet and its facet value between quotes.
9+
* This avoid formating issues on facets containing multiple words.
10+
*
11+
* 'My facet:My facet value' becomes '"My facet":"My facet value"'
12+
*
13+
* @param {string} filter?
14+
* @returns {Filter}
15+
*/
16+
function transformFilter(filter: string): string {
17+
return filter.replace(/(.*):(.*)/i, '"$1"="$2"')
18+
}
19+
20+
/**
21+
* Itterate over all filters.
22+
* Return the filters in a Meilisearch compatible format.
823
*
924
* @param {SearchContext['facetFilters']} filters?
1025
* @returns {Filter}
1126
*/
12-
function transformFilter(filters?: SearchContext['facetFilters']): Filter {
27+
function transformFilters(filters?: SearchContext['facetFilters']): Filter {
1328
if (typeof filters === 'string') {
14-
return replaceColonByEqualSign(filters)
29+
return transformFilter(filters)
1530
} else if (Array.isArray(filters))
1631
return filters
1732
.map((filter) => {
1833
if (Array.isArray(filter))
1934
return filter
20-
.map((nestedFilter) => replaceColonByEqualSign(nestedFilter))
35+
.map((nestedFilter) => transformFilter(nestedFilter))
2136
.filter((elem) => elem)
2237
else {
23-
return replaceColonByEqualSign(filter)
38+
return transformFilter(filter)
2439
}
2540
})
2641
.filter((elem) => elem)
@@ -89,8 +104,8 @@ export function adaptFilters(
89104
numericFilters: SearchContext['numericFilters'],
90105
facetFilters: SearchContext['facetFilters']
91106
): Filter {
92-
const transformedFilter = transformFilter(facetFilters || [])
93-
const transformedNumericFilter = transformFilter(numericFilters || [])
107+
const transformedFilter = transformFilters(facetFilters || [])
108+
const transformedNumericFilter = transformFilters(numericFilters || [])
94109

95110
return mergeFilters(
96111
transformedFilter,

packages/instant-meilisearch/src/utils/string.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,6 @@ export function isString(str: any): boolean {
66
return typeof str === 'string' || str instanceof String
77
}
88

9-
/**
10-
* @param {string} filter
11-
* @returns {string}
12-
*/
13-
export function replaceColonByEqualSign(filter: string): string {
14-
// will only change first occurence of `:`
15-
return filter.replace(/:(.*)/i, '="$1"')
16-
}
17-
189
/**
1910
* @param {any[]} arr
2011
* @returns {string}

0 commit comments

Comments
 (0)