Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
32 changes: 0 additions & 32 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
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)
})
21 changes: 6 additions & 15 deletions src/adapter/search-request-adapter/search-params-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,12 @@ export function adaptSearchParams(
const { pagination } = searchContext

// Limit based on pagination preferences
if (
(!placeholderSearch && query === '') ||
pagination.paginationTotalHits === 0
) {
meiliSearchParams.limit = 0
} else if (searchContext.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
}

const sort = searchContext.sort
Expand All @@ -122,5 +112,6 @@ export function adaptSearchParams(
}
}

console.log(meiliSearchParams)
return meiliSearchParams
}
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
10 changes: 3 additions & 7 deletions src/adapter/search-response-adapter/hits-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { PaginationContext, SearchContext } from '../../types'
import { adaptPagination } from './pagination-adapter'
import type { SearchContext } from '../../types'
import { adaptFormattedFields } from './format-adapter'
import { adaptGeoResponse } from './geo-reponse-adapter'

Expand All @@ -11,14 +10,11 @@ import { adaptGeoResponse } from './geo-reponse-adapter'
*/
export function adaptHits(
hits: Array<Record<string, any>>,
searchContext: SearchContext,
paginationContext: PaginationContext
searchContext: SearchContext
): any {
const { primaryKey } = searchContext
const { hitsPerPage, page } = paginationContext
const paginatedHits = adaptPagination(hits, page, hitsPerPage)

let adaptedHits = paginatedHits.map((hit: Record<string, any>) => {
let adaptedHits = hits.map((hit: Record<string, any>) => {
// Creates Hit object compliant with InstantSearch
if (Object.keys(hit).length > 0) {
const {
Expand Down
Loading