Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3302604
Add disjunctive facet search
bidoubiwa Nov 23, 2022
29bda1f
Add condition on filtering caching
bidoubiwa Nov 23, 2022
bb0ef01
Add multi index search
bidoubiwa Nov 24, 2022
07760b6
Add datasets in tests assets
bidoubiwa Nov 24, 2022
073adf0
Update src/client/instant-meilisearch-client.ts
bidoubiwa Nov 24, 2022
7b4fc20
Improve filters
bidoubiwa Nov 24, 2022
ee46536
Improve readme
bidoubiwa Nov 24, 2022
560763d
Merge branch 'bump-meilisearch-v0.30.0-add_multi_index_search' of htt…
bidoubiwa Nov 24, 2022
a224d1a
fix merge conflicts
bidoubiwa Nov 24, 2022
8179471
Remove console logs from client
bidoubiwa Nov 24, 2022
4e1fbb2
Remove comments
bidoubiwa Nov 24, 2022
b33b7d9
Add tests on disjunctive facet search
bidoubiwa Nov 29, 2022
4547b56
remove console log
bidoubiwa Nov 29, 2022
238ee58
Update playground
bidoubiwa Nov 29, 2022
43a3dac
Add logging on react setup
bidoubiwa Nov 29, 2022
9f06d09
Improve README
bidoubiwa Nov 29, 2022
da79ff1
Roll back .gitignore
bidoubiwa Nov 29, 2022
6f79266
Fix readme errors
bidoubiwa Nov 29, 2022
0b130d0
Fix refinement list typo error
bidoubiwa Nov 29, 2022
e7b7ca8
Merge branch 'main' of github.com:meilisearch/instant-meilisearch int…
bidoubiwa Dec 7, 2022
2daa5c2
Fix end to end tests
bidoubiwa Dec 7, 2022
c833807
Fix linting error
bidoubiwa Dec 7, 2022
b1f785d
Update selectors in cypress to improve tests
bidoubiwa Dec 7, 2022
1bf47bf
Add json module resolver for tests
bidoubiwa Dec 7, 2022
42edcde
Add a wait in cypress method resolving to fast
bidoubiwa Dec 7, 2022
a6ab3fa
Remove useless wait in search-ui specs
bidoubiwa Dec 7, 2022
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
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ List of all the components that are available in [instantSearch](https://github.
### Table Of Widgets

- ✅ [InstantSearch](#-instantsearch)
- [index](#-index)
- [index](#-index)
- ✅ [SearchBox](#-searchbox)
- ✅ [Configure](#-configure)
- ❌ [ConfigureRelatedItems](#-configure-related-items)
Expand Down Expand Up @@ -339,15 +339,13 @@ const search = instantsearch({
})
```

### Index
### Index

[Index references](https://www.algolia.com/doc/api-reference/widgets/index-widget/js/)

`Index` is the component that lets you apply widgets to a dedicated index. It’s useful if you want to build an interface that targets multiple indices.

Not compatible as Meilisearch does not support federated search on multiple indexes.

If you'd like to see federated search implemented please vote for it in the [roadmap](https://roadmap.meilisearch.com/c/74-multi-index-search?utm_medium=social&utm_source=portal_share).
Using this component, instant-meilisearch does an http-request for each different `Index` widget added. More http requests are made when using the [`RefinementList`](#✅-refinementlist) widget.

### ✅ SearchBox

Expand Down Expand Up @@ -672,6 +670,9 @@ The `refinementList` widget is one of the most common widgets you can find in a
- ✅ templates: The templates to use for the widget.
- ✅ cssClasses: The CSS classes to override.

The `RefinementList` widget uses the `disjunctive facet search` principle when using the `or` operator. For each different facet category used, an additional http call is made.
For example, if I ask for `color=green` and `size=2`, three http requests are made. One for the hits, one for the `color` distribution and one for the `size` distribution. To provide any feedback on the subject, refer to [this discussion](https://github.com/meilisearch/product/issues/54).

The following example will create a UI component with the a list of genres on which you will be able to facet.

```js
Expand Down
19 changes: 11 additions & 8 deletions cypress/integration/search-ui.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,27 @@ describe(`${playground} playground test`, () => {

it('Sort by recommendationCound ascending', () => {
const select = `.ais-SortBy-select`
cy.get(select).select('steam-video-games:recommendationCount:asc')
cy.get(select).select('games:recommendationCount:asc')
cy.wait(1000)
cy.get(HIT_ITEM_CLASS).eq(0).contains('Deathmatch Classic')
})

it('Sort by default relevancy', () => {
const select = `.ais-SortBy-select`
cy.get(select).select('steam-video-games')
cy.get(select).select('games')
cy.wait(1000)
cy.get(HIT_ITEM_CLASS).eq(0).contains('Counter-Strike')
})

it('click on facets', () => {
const checkbox = `.ais-RefinementList-list .ais-RefinementList-checkbox`
cy.get(checkbox).eq(1).click()
it('click on facets ensure disjunctive facet search', () => {
const facet = `.ais-RefinementList-list`
const checkbox = `.ais-RefinementList-checkbox`
const facetCount = '.ais-RefinementList-count'
cy.get(facet).eq(0).find(checkbox).eq(1).click() // genres > action
cy.wait(1000)
cy.get(facet).eq(0).find(facetCount).eq(0).contains('5') // genres > action count
cy.get(facet).eq(1).find(facetCount).eq(0).contains('4') // players > multiplayer
cy.wait(1000)
cy.get(HIT_ITEM_CLASS).eq(1).contains('Team Fortress Classic')
cy.get(HIT_ITEM_CLASS).eq(1).contains('4.99 $')
})

it('Search', () => {
Expand All @@ -78,6 +81,6 @@ describe(`${playground} playground test`, () => {

it('Paginate Search', () => {
cy.get('.ais-InfiniteHits-loadMore').click()
cy.get(HIT_ITEM_CLASS).should('have.length', 11)
cy.get(HIT_ITEM_CLASS).should('have.length', 12)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this has been changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of the disjunctive facet search accepting more hits in return 😅

})
})
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
displayName: 'dom',
testPathIgnorePatterns: [...ignoreFiles, '<rootDir>/tests/build*'],
testMatch: ['**/*.tests.ts', '/tests/**/*.ts'],
setupFilesAfterEnv: ['<rootDir>/scripts/jest_teardown.js'],
},
{
globals: {
Expand All @@ -44,6 +45,7 @@ module.exports = {
testEnvironment: 'node',
testPathIgnorePatterns: [...ignoreFiles],
testMatch: ['**/*.tests.ts', '/tests/**/*.ts'],
setupFilesAfterEnv: ['<rootDir>/scripts/jest_teardown.js'],
},
],
}
12 changes: 12 additions & 0 deletions scripts/jest_teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { MeiliSearch } = require('meilisearch')

const HOST = 'http://localhost:7700'
const API_KEY = 'masterKey'

afterAll(async () => {
const client = new MeiliSearch({ host: HOST, apiKey: API_KEY })
await client.deleteIndex('movies')
const task = await client.deleteIndex('games')

await client.waitForTask(task.taskUid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you have a method that accepts more than one uid? (or I'm misleading because of other SDKs...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have one in js :(

})
4 changes: 3 additions & 1 deletion src/adapter/search-request-adapter/search-params-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ export function MeiliParamsCreator(searchContext: SearchContext) {
return meiliSearchParams
},
addFacets() {
if (facets?.length) {
if (Array.isArray(facets)) {
meiliSearchParams.facets = facets
} else if (typeof facets === 'string') {
meiliSearchParams.facets = [facets]
}
},
addAttributesToCrop() {
Expand Down
16 changes: 9 additions & 7 deletions src/adapter/search-request-adapter/search-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,20 @@ export function SearchResolver(
// Check if specific request is already cached with its associated search response.
if (cachedResponse) return cachedResponse

const cachedFacets = extractFacets(searchContext, searchParams)

// Make search request
const searchResponse = await client
.index(searchContext.indexUid)
.search(searchContext.query, searchParams)

// Add missing facets back into facetDistribution
searchResponse.facetDistribution = addMissingFacets(
cachedFacets,
searchResponse.facetDistribution
)
if (searchContext.keepZeroFacets) {
const cachedFacets = extractFacets(searchContext, searchParams)

// Add missing facets back into facetDistribution
searchResponse.facetDistribution = addMissingFacets(
cachedFacets,
searchResponse.facetDistribution
)
}

// query can be: empty string, undefined or null
// all of them are falsy's
Expand Down
65 changes: 37 additions & 28 deletions src/client/instant-meilisearch-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,40 +67,49 @@ export function instantMeiliSearch(
instantSearchRequests: readonly AlgoliaMultipleQueriesQuery[]
): Promise<{ results: Array<AlgoliaSearchResponse<T>> }> {
try {
const searchRequest = instantSearchRequests[0]
const searchContext: SearchContext = createSearchContext(
searchRequest,
instantMeiliSearchOptions,
defaultFacetDistribution
)
const searchResponses: { results: Array<AlgoliaSearchResponse<T>> } = {
results: [],
}

// Adapt search request to Meilisearch compliant search request
const adaptedSearchRequest = adaptSearchParams(searchContext)
const requests = instantSearchRequests

// Cache first facets distribution of the instantMeilisearch instance
// Needed to add in the facetDistribution the fields that were not returned
// When the user sets `keepZeroFacets` to true.
if (defaultFacetDistribution === undefined) {
defaultFacetDistribution = await cacheFirstFacetDistribution(
searchResolver,
searchContext
for (const searchRequest of requests) {
const searchContext: SearchContext = createSearchContext(
searchRequest,
instantMeiliSearchOptions,
defaultFacetDistribution
)
searchContext.defaultFacetDistribution = defaultFacetDistribution
}

// Search response from Meilisearch
const searchResponse = await searchResolver.searchResponse(
searchContext,
adaptedSearchRequest
)
// Adapt search request to Meilisearch compliant search request
const adaptedSearchRequest = adaptSearchParams(searchContext)

// Cache first facets distribution of the instantMeilisearch instance
// Needed to add in the facetDistribution the fields that were not returned
// When the user sets `keepZeroFacets` to true.
if (defaultFacetDistribution === undefined) {
defaultFacetDistribution = await cacheFirstFacetDistribution(
searchResolver,
searchContext
)
searchContext.defaultFacetDistribution = defaultFacetDistribution
}

// Search response from Meilisearch
const searchResponse = await searchResolver.searchResponse(
searchContext,
adaptedSearchRequest
)

// Adapt the Meilisearch responsne to a compliant instantsearch.js response
const adaptedSearchResponse = adaptSearchResponse<T>(
searchResponse,
searchContext
)
// Adapt the Meilisearch response to a compliant instantsearch.js response
const adaptedSearchResponse = adaptSearchResponse<T>(
searchResponse,
searchContext
)

searchResponses.results.push(adaptedSearchResponse.results[0])
}

return adaptedSearchResponse
return searchResponses
} catch (e: any) {
console.error(e)
throw new Error(e)
Expand Down
Loading