Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
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
15 changes: 8 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,19 @@ 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
64 changes: 36 additions & 28 deletions src/client/instant-meilisearch-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,40 +67,48 @@ 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
for (const searchRequest of requests) {
const searchContext: SearchContext = createSearchContext(
searchRequest,
instantMeiliSearchOptions,
defaultFacetDistribution
)

// 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
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
)
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 responsne 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
172 changes: 172 additions & 0 deletions tests/multi-index-search.tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { instantMeiliSearch } from '../src'
import { Movies, meilisearchClient } from './assets/utils'
import movies from './assets/movies.json'
import games from './assets/games.json'

describe('Multi-index search test', () => {
beforeAll(async () => {
const moviesIndex = meilisearchClient.index('movies')
const gamesIndex = meilisearchClient.index('games')

await moviesIndex.delete()
await gamesIndex.delete()

await moviesIndex.updateSettings({
filterableAttributes: ['genres', 'color', 'platforms'],
})
await gamesIndex.updateSettings({
filterableAttributes: ['genres', 'color', 'platforms'],
})

await moviesIndex.addDocuments(movies)
const response = await gamesIndex.addDocuments(games)

await meilisearchClient.waitForTask(response.taskUid)
})

test('searching on two indexes', async () => {
const customClient = instantMeiliSearch(
'http://localhost:7700',
'masterKey'
)

const response = await customClient.search<Movies>([
{
indexName: 'movies',
params: {
query: 'c',
},
},
{
indexName: 'games',
params: {
query: 'c',
},
},
])

const movieHits = response.results[0].hits
const gameHits = response.results[1].hits

expect(movieHits.length).toBe(1)
expect(gameHits.length).toBe(14)
})

test('searching on two indexes with scroll pagination', async () => {
const customClient = instantMeiliSearch(
'http://localhost:7700',
'masterKey'
)

const response = await customClient.search<Movies>([
{
indexName: 'movies',
params: {
hitsPerPage: 1,
page: 1,
},
},
{
indexName: 'games',
params: {
hitsPerPage: 1,
page: 1,
},
},
])

const movies = response.results[0]
const games = response.results[1]

expect(movies.hits.length).toBe(1)
expect(movies.page).toBe(1)
expect(movies.nbPages).toBe(3)

expect(games.hits.length).toBe(1)
expect(games.page).toBe(1)
expect(games.nbPages).toBe(3)
})

test('searching on two indexes with page selection pagination', async () => {
const customClient = instantMeiliSearch(
'http://localhost:7700',
'masterKey',
{
finitePagination: true,
}
)

const response = await customClient.search<Movies>([
{
indexName: 'movies',
params: {
hitsPerPage: 1,
page: 1,
},
},
{
indexName: 'games',
params: {
hitsPerPage: 1,
page: 1,
},
},
])

const movies = response.results[0]
const games = response.results[1]

expect(movies.hits.length).toBe(1)
expect(movies.page).toBe(1)
expect(movies.nbPages).toBe(6)

expect(games.hits.length).toBe(1)
expect(games.page).toBe(1)
expect(games.nbPages).toBe(15)
})

test('searching on two indexes with no placeholder search', async () => {
const customClient = instantMeiliSearch(
'http://localhost:7700',
'masterKey',
{
placeholderSearch: false,
}
)

const emptyResponse = await customClient.search<Movies>([
{
indexName: 'movies',
},
{
indexName: 'games',
},
])

const emptyMovies = emptyResponse.results[0]
const emptyGames = emptyResponse.results[1]

expect(emptyMovies.hits.length).toBe(0)
expect(emptyGames.hits.length).toBe(0)

const response = await customClient.search<Movies>([
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const response = await customClient.search<Movies>([
const response = await customClient.search<Movies>([

I want to understand this signature better since now you'll return two different types. What do these Movies does in this case? Can we get rid of it?

Copy link
Contributor Author

@bidoubiwa bidoubiwa Dec 7, 2022

Choose a reason for hiding this comment

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

Instantsearch requires a search method with a generic. Nonetheless, you are right that it makes no sense as there are multiple indexes now.

I looked at the code of algoliasearch and it seems the issue is at their side as well. multipleQueries is the method used for searching in instantsearch, see their searchClient.

{
indexName: 'movies',
params: {
query: 'a',
},
},
{
indexName: 'games',
params: {
query: 'a',
},
},
])
const movies = response.results[0]
const games = response.results[1]

expect(movies.hits.length).toBe(4)
expect(games.hits.length).toBe(15)
})
})
1 change: 1 addition & 0 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"suppressImplicitAnyIndexErrors": true,
"resolveJsonModule": true
},
"include": [
"./**/*"
Expand Down