Skip to content
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ const searchClient = instantMeiliSearch(
`instant-meilisearch` offers some options you can set to further fit your needs.

- [`placeholderSearch`](#placeholder-search): Enable or disable placeholder search (default: `true`).
- [`paginationTotalHits`](#pagination-total-hits): Maximum total number of hits to create a finite pagination (default: `200`).
- [`finitePagination`](#finite-pagination): Used to work with the [`pagination`](#-pagination) widget (default: `false`) .
- [`primaryKey`](#primary-key): Specify the primary key of your documents (default `undefined`).
- [`keepZeroFacets`](#keep-zero-facets): Show the facets value even when they have 0 matches (default `false`).

Expand All @@ -93,7 +91,6 @@ const searchClient = instantMeiliSearch(
'https://integration-demos.meilisearch.com',
'99d1e034ed32eb569f9edc27962cccf90b736e4c5a70f7f5e76b9fab54d6a185',
{
paginationTotalHits: 30, // default: 200.
placeholderSearch: false, // default: true.
primaryKey: 'id', // default: undefined
// ...
Expand All @@ -110,35 +107,6 @@ When placeholder search is set to `false`, no results appears when searching on
{ placeholderSearch : true } // default true
```

### Pagination total hits

The total (and finite) number of hits (default: `200`) you can browse during pagination when using the [pagination widget](https://www.algolia.com/doc/api-reference/widgets/pagination/js/) or the [`infiniteHits` widget](#-infinitehits). If none of these widgets are used, `paginationTotalHits` is ignored.<br>

For example, using the `infiniteHits` widget, and a `paginationTotalHits` of 9. On the first search request 6 hits are shown, by clicking a second time on `load more` only 3 more hits are added. This is because `paginationTotalHits` is `9`.

Usage:

```js
{ paginationTotalHits: 50 } // default: 200
```

`hitsPerPage` has a value of `20` by default and can [be customized](#-hitsperpage).

### Finite Pagination

Finite pagination is used when you want to add a numbered pagination at the bottom of your hits (for example: `< << 1, 2, 3 > >>`).
To be able to know the amount of page numbers you have, a search is done requesting `paginationTotalHits` documents (default: `200`).
With the amount of documents returned, instantsearch is able to render the correct amount of numbers in the pagination widget.

Example:

```js
{ finitePagination: true } // default: false
```

⚠️ Meilisearch is not designed for pagination and this can lead to performances issues, so the usage `finitePagination` but also of the pagination widgets are not recommended.<br>
More information about Meilisearch and the pagination [here](https://github.com/meilisearch/documentation/issues/561).

### Primary key

Specify the field in your documents containing the [unique identifier](https://docs.meilisearch.com/learn/core_concepts/documents.html#primary-field) (`undefined` by default). By adding this option, we avoid instantSearch errors that are thrown in the browser console. In `React` particularly, this option removes the `Each child in a list should have a unique "key" prop` error.
Expand Down Expand Up @@ -906,8 +874,6 @@ instantsearch.widgets.clearRefinements({

The `pagination` widget displays a pagination system allowing the user to change the current page.

We do not recommend using this widget as pagination slows the search responses. Instead, the [InfiniteHits](#-infinitehits) component is recommended.

- ✅ container: The CSS Selector or HTMLElement to insert the widget into. _required_
- ✅ showFirst: Whether to display the first-page link.
- ✅ showPrevious: Whether to display the previous page link.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@meilisearch/instant-meilisearch",
"version": "0.8.1",
"version": "0.8.1-pagination-beta.0",
"private": false,
"description": "The search client to use Meilisearch with InstantSearch.",
"scripts": {
Expand Down Expand Up @@ -57,7 +57,7 @@
"url": "https://github.com/meilisearch/instant-meilisearch.git"
},
"dependencies": {
"meilisearch": "^0.27.0"
"meilisearch": "0.28.0-pagination-beta.0"
},
"devDependencies": {
"@babel/cli": "^7.18.9",
Expand Down
1 change: 0 additions & 1 deletion playgrounds/react/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const searchClient = instantMeiliSearch(
'https://integration-demos.meilisearch.com',
'99d1e034ed32eb569f9edc27962cccf90b736e4c5a70f7f5e76b9fab54d6a185',
{
paginationTotalHits: 60,
primaryKey: 'id',
}
)
Expand Down
114 changes: 12 additions & 102 deletions src/adapter/search-request-adapter/__tests__/search-params.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { adaptSearchParams } from '../search-params-adapter'
test('Adapt basic SearchContext ', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
defaultFacetDistribution: {},
finitePagination: false,
})
expect(searchParams.attributesToHighlight).toContain('*')
expect(searchParams.attributesToHighlight?.length).toBe(1)
Expand All @@ -14,11 +13,10 @@ test('Adapt basic SearchContext ', () => {
test('Adapt SearchContext with filters, sort and no geo rules ', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']],
sort: 'id < 1',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.filter).toStrictEqual([
Expand All @@ -33,12 +31,11 @@ test('Adapt SearchContext with filters, sort and no geo rules ', () => {
test('Adapt SearchContext with filters, sort and geo rules ', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']],
insideBoundingBox: '0,0,0,0',
sort: 'id < 1',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.filter).toStrictEqual([
Expand All @@ -54,11 +51,10 @@ test('Adapt SearchContext with filters, sort and geo rules ', () => {
test('Adapt SearchContext with only facetFilters and geo rules ', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']],
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.filter).toEqual([
Expand All @@ -73,11 +69,10 @@ test('Adapt SearchContext with only facetFilters and geo rules ', () => {
test('Adapt SearchContext with only sort and geo rules ', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
sort: 'id < 1',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)'])
Expand All @@ -89,123 +84,38 @@ test('Adapt SearchContext with only sort and geo rules ', () => {
test('Adapt SearchContext with no sort and no filters and geo rules ', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)'])
expect(searchParams.attributesToHighlight).toContain('*')
expect(searchParams.attributesToHighlight?.length).toBe(1)
})

test('Adapt SearchContext with finite pagination', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: true,
})

expect(searchParams.limit).toBe(20)
})

test('Adapt SearchContext with finite pagination on a later page', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 10, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: true,
})

expect(searchParams.limit).toBe(20)
})

test('Adapt SearchContext with finite pagination and pagination total hits lower than hitsPerPage', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 4, page: 0, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: true,
})

expect(searchParams.limit).toBe(4)
})

test('Adapt SearchContext with no finite pagination', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.limit).toBe(7)
})

test('Adapt SearchContext with no finite pagination on page 2', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 1, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.limit).toBe(13)
})

test('Adapt SearchContext with no finite pagination on page higher than paginationTotalHits', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 20, page: 40, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.limit).toBe(20)
})

test('Adapt SearchContext with no finite pagination and pagination total hits lower than hitsPerPage', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
pagination: { paginationTotalHits: 4, page: 0, hitsPerPage: 6 },
insideBoundingBox: '0,0,0,0',
defaultFacetDistribution: {},
finitePagination: false,
})

expect(searchParams.limit).toBe(4)
})

test('Adapt SearchContext placeholderSearch set to false', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
query: '',
pagination: { paginationTotalHits: 4, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
defaultFacetDistribution: {},
finitePagination: false,
placeholderSearch: false,
})

expect(searchParams.limit).toBe(0)
expect(searchParams.page).toBe(1)
expect(searchParams.hitsPerPage).toBe(0)
})

test('Adapt SearchContext placeholderSearch set to false', () => {
const searchParams = adaptSearchParams({
indexUid: 'test',
query: '',
pagination: { paginationTotalHits: 200, page: 0, hitsPerPage: 6 },
pagination: { page: 0, hitsPerPage: 6 },
defaultFacetDistribution: {},
finitePagination: false,
placeholderSearch: true,
})

expect(searchParams.limit).toBe(7)
expect(searchParams.page).toBe(1)
expect(searchParams.hitsPerPage).toBe(6)
})
22 changes: 5 additions & 17 deletions src/adapter/search-request-adapter/search-params-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function MeiliParamsCreator(searchContext: SearchContext) {
highlightPostTag,
placeholderSearch,
query,
finitePagination,
sort,
pagination,
} = searchContext
Expand Down Expand Up @@ -83,23 +82,12 @@ function MeiliParamsCreator(searchContext: SearchContext) {
}
},
addPagination() {
// Limit based on pagination preferences
if (
(!placeholderSearch && query === '') ||
pagination.paginationTotalHits === 0
) {
meiliSearchParams.limit = 0
} else if (finitePagination) {
meiliSearchParams.limit = pagination.paginationTotalHits
if (!placeholderSearch && query === '') {
meiliSearchParams.hitsPerPage = 0
meiliSearchParams.page = pagination.page + 1
} else {
const limit = (pagination.page + 1) * pagination.hitsPerPage + 1
// If the limit is bigger than the total hits accepted
// force the limit to that amount
if (limit > pagination.paginationTotalHits) {
meiliSearchParams.limit = pagination.paginationTotalHits
} else {
meiliSearchParams.limit = limit
}
meiliSearchParams.page = pagination.page + 1
meiliSearchParams.hitsPerPage = pagination.hitsPerPage
}
},
addSort() {
Expand Down
16 changes: 4 additions & 12 deletions src/adapter/search-request-adapter/search-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ const emptySearch: MeiliSearchResponse<Record<string, any>> = {
hits: [],
query: '',
facetDistribution: {},
limit: 0,
offset: 0,
estimatedTotalHits: 0,
page: 0,
hitsPerPage: 0,
totalPages: 0,
totalHits: 0,
processingTimeMs: 0,
}

Expand Down Expand Up @@ -41,20 +42,11 @@ export function SearchResolver(cache: SearchCacheInterface) {
return emptySearch
}

const { pagination } = searchContext

// In case we are in a `finitePagination`, only one big request is made
// containing a total of max the paginationTotalHits (default: 200).
// Thus we dont want the pagination to impact the cache as every
// hits are already cached.
const paginationCache = searchContext.finitePagination ? {} : pagination

// Create cache key containing a unique set of search parameters
const key = cache.formatKey([
searchParams,
searchContext.indexUid,
searchContext.query,
paginationCache,
])
const cachedResponse = cache.getEntry(key)

Expand Down
Loading