Skip to content

Commit a84846e

Browse files
committed
Add facetStats compatibility to be aware of min and max for MS v1.1.0
1 parent 1bcbcfc commit a84846e

File tree

5 files changed

+121
-44
lines changed

5 files changed

+121
-44
lines changed

README.md

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -741,54 +741,14 @@ The `rangeSlider` widget provides a user-friendly way to filter the results, bas
741741
- ✅ attribute: The name of the attribute in the document. _required_.
742742
- ✅ min: The minimum value for the input. _required_
743743
- ✅ max: The maximum value for the input. _required_
744-
- precision: The number of digits after the decimal point to use. Not compatible as only integers work with `rangeSlider`.
744+
- precision: The number of digits after the decimal point to use. Not compatible as only integers work with `rangeSlider`.
745745
- ✅ step: The number of steps between each handle move.
746746
- ✅ pips: Whether to show slider pips (ruler marks).
747747
- ✅ tooltips: Whether to show tooltips. The default tooltips show the raw value.
748748
- ✅ cssClasses: The CSS classes to override.
749749

750-
#### ⚠️ The component is compatible but only by applying the following requirements:
750+
To be able to use the `rangeSlider` on an attribute, the attribute must be in the[`filterableAttributes`](https://docs.meilisearch.com/reference/features/filtering_and_faceted_search.html#configuring-filters) and must contain numeric values.
751751

752-
#### 1. Manual Min Max
753-
754-
Min and max of attributes are not returned from Meilisearch and thus **must be set manually**.
755-
756-
```js
757-
instantsearch.widgets.rangeSlider({
758-
// ...
759-
min: 0,
760-
max: 100000,
761-
}),
762-
```
763-
764-
#### 2. Attribute must be in `filterableAttributes`
765-
766-
If the attribute is not in the [`filterableAttributes`](https://docs.meilisearch.com/reference/features/filtering_and_faceted_search.html#configuring-filters) setting list, filtering on this attribute is not possible.
767-
768-
Example:
769-
Given the attribute `id` that has not been added in `filterableAttributes`:
770-
771-
```js
772-
instantsearch.widgets.rangeSlider({
773-
attribute: 'id',
774-
// ...
775-
}),
776-
```
777-
778-
The widget throws the following error:
779-
780-
```json
781-
{
782-
"message": " .. attribute `id` is not filterable, available filterable attributes are: author, price, genres",
783-
"errorCode": "bad_request",
784-
"errorType": "invalid_request_error",
785-
"errorLink": "https://docs.meilisearch.com/errors#bad_request"
786-
}
787-
```
788-
789-
To avoid this error, the attribute must be added to the [`filterableAttributes` setting](https://docs.meilisearch.com/reference/api/filterable_attributes.html#get-filterable-attributes).
790-
791-
After these steps, `rangeSlider` becomes compatible.
792752

793753
### ✅ Menu
794754

@@ -832,7 +792,7 @@ The `rangeInput` widget allows a user to select a numeric range using a minimum
832792
- ✅ templates: The templates to use for the widget.
833793
- ✅ cssClasses: The CSS classes to override.
834794

835-
⚠️ Not compatible with Meilisearch by default, needs a workaround. See workaround in [RangeSlider](#-rangeslider) section.
795+
To be able to use the `RangeInput` on an attribute, the attribute must be in the[`filterableAttributes`](https://docs.meilisearch.com/reference/features/filtering_and_faceted_search.html#configuring-filters) and must contain numeric values.
836796

837797
### ✅ MenuSelect
838798

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { searchClient, dataset, meilisearchClient } from './assets/utils'
2+
3+
describe('Facet stats tests', () => {
4+
beforeAll(async () => {
5+
const deleteTask = await meilisearchClient.deleteIndex('movies')
6+
await meilisearchClient.waitForTask(deleteTask.taskUid)
7+
await meilisearchClient
8+
.index('movies')
9+
.updateFilterableAttributes(['genres', 'release_date', 'id'])
10+
const documentsTask = await meilisearchClient
11+
.index('movies')
12+
.addDocuments(dataset)
13+
await meilisearchClient.index('movies').waitForTask(documentsTask.taskUid)
14+
})
15+
16+
test('Facet stats on an empty facets array', async () => {
17+
const response = await searchClient.search([
18+
{
19+
indexName: 'movies',
20+
params: {
21+
query: '',
22+
facets: [],
23+
},
24+
},
25+
])
26+
27+
expect(response.results[0].facets_stats?.release_date).toEqual(undefined)
28+
})
29+
30+
test('Facet stats on a facet with no numeric values', async () => {
31+
const response = await searchClient.search([
32+
{
33+
indexName: 'movies',
34+
params: {
35+
query: '',
36+
facets: ['genres'],
37+
},
38+
},
39+
])
40+
41+
expect(response.results[0].facets_stats?.genres).toEqual(undefined)
42+
})
43+
44+
test('Facet stats on two facet', async () => {
45+
const response = await searchClient.search([
46+
{
47+
indexName: 'movies',
48+
params: {
49+
query: '',
50+
facets: ['release_date', 'id'],
51+
},
52+
},
53+
])
54+
55+
expect(response.results[0].facets_stats?.release_date).toEqual({
56+
avg: 0,
57+
max: 1065744000,
58+
min: 233366400,
59+
sum: 0,
60+
})
61+
expect(response.results[0].facets_stats?.id).toEqual({
62+
avg: 0,
63+
max: 30,
64+
min: 2,
65+
sum: 0,
66+
})
67+
})
68+
})
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {
2+
AlgoliaSearchResponse,
3+
MeiliFacetStats,
4+
AlgoliaFacetStats,
5+
} from '../../types'
6+
7+
export function adaptFacetStats(
8+
meiliFacetStats: MeiliFacetStats
9+
): AlgoliaSearchResponse['facets_stats'] {
10+
const facetStats = Object.keys(meiliFacetStats).reduce(
11+
(stats: AlgoliaFacetStats, facet: string) => {
12+
stats[facet] = { ...meiliFacetStats[facet], avg: 0, sum: 0 } // Set at 0 as these numbers are not provided by Meilisearch
13+
14+
return stats
15+
},
16+
{} as AlgoliaFacetStats
17+
)
18+
return facetStats
19+
}

packages/instant-meilisearch/src/adapter/search-response-adapter/search-response-adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { adaptHits } from './hits-adapter'
88
import { adaptTotalHits } from './total-hits-adapter'
99
import { adaptPaginationParameters } from './pagination-adapter'
1010
import { adaptFacetDistribution } from './facet-distribution-adapter'
11+
import { adaptFacetStats } from './adapt-facet-stats'
1112

1213
/**
1314
* Adapt multiple search results from Meilisearch
@@ -54,6 +55,7 @@ export function adaptSearchResult<T>(
5455
query,
5556
indexUid,
5657
facetDistribution: responseFacetDistribution = {},
58+
facetStats = {},
5759
} = meiliSearchResult
5860

5961
const facets = Object.keys(responseFacetDistribution)
@@ -86,6 +88,7 @@ export function adaptSearchResult<T>(
8688
hits,
8789
params: '',
8890
exhaustiveNbHits: false,
91+
facets_stats: adaptFacetStats(facetStats),
8992
}
9093
return adaptedSearchResult
9194
}

packages/instant-meilisearch/src/types/types.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ export type {
1515
}
1616
export type { SearchResponse as AlgoliaSearchResponse } from '@algolia/client-search'
1717

18-
export type { Filter, FacetDistribution, MeiliSearch } from 'meilisearch'
18+
export type {
19+
Filter,
20+
FacetDistribution,
21+
MeiliSearch,
22+
FacetStats as MeiliFacetStats,
23+
} from 'meilisearch'
1924

2025
export type InstantSearchParams = AlgoliaMultipleQueriesQuery['params']
2126

@@ -109,3 +114,25 @@ export type MultiSearchResolver = {
109114
instantSearchPagination: PaginationState[]
110115
) => Promise<MeilisearchMultiSearchResult[]>
111116
}
117+
118+
export type AlgoliaFacetStats = Record<
119+
string,
120+
{
121+
/**
122+
* The minimum value in the result set.
123+
*/
124+
min: number
125+
/**
126+
* The maximum value in the result set.
127+
*/
128+
max: number
129+
/**
130+
* The average facet value in the result set.
131+
*/
132+
avg: number
133+
/**
134+
* The sum of all values in the result set.
135+
*/
136+
sum: number
137+
}
138+
>

0 commit comments

Comments
 (0)