typesense / typesense-instantsearch-adapter

A JS adapter library to build rich search interfaces with Typesense and InstantSearch.js
MIT License
361 stars 62 forks source link

Features: Please allow a better way to have multiple searchClients #200

Closed sangdth closed 3 months ago

sangdth commented 3 months ago

Description

Currently our business requires a global search, which mean one query returns results from multiple collections.

And our nature of collections do not share any common query key, it looks like this:

    leads: 'agent_name,demo_category',
    cases: 'name,status',
    users: 'first_name,last_name,email',
    documents: 'key,content',

so we can not use the Multi Search feature.

The current adapter only be used on single provider InstantSearch, so it will be so cumbersome, in our case it will look like:

            <InstantSearch
              searchClient={leadSearchClient} 
              indexName='leads'
            >
              <Configure hitsPerPage={12} />
              <SearchBox />
              <Hits />
            </InstantSearch>

            <InstantSearch
              searchClient={caseSearchClient}
              indexName='cases'
            >
              <Configure hitsPerPage={12} />
              <Hits />
            </InstantSearch>

            <InstantSearch
              searchClient={documentSearchClient}
              indexName='documents'
            >
              <Configure hitsPerPage={12} />
              <Hits />
            </InstantSearch>

Suggestion

I think it would be better if we could have something like this:

  export const queryMap: Record<string, string> = {
    leads: 'agent_name,demo_category',
    cases: 'name,status',
    users: 'first_name,last_name,email',
    documents: 'key,content',
  };

  const { searchClient } = new TypesenseInstantSearchAdapter({
    server: {
      apiKey: process.env.NEXT_PUBLIC_TYPESENSE_SEARCH_ONLY_API_KEY ?? '',
      nodes: [
        {
          host: process.env.NEXT_PUBLIC_TYPESENSE_HOST ?? 'localhost',
          port: parseInt(process.env.NEXT_PUBLIC_TYPESENSE_PORT ?? '8108'),
          protocol: process.env.NEXT_PUBLIC_TYPESENSE_PROTOCOL ?? 'http',
        },
      ],
      cacheSearchResultsForSeconds: 2 * 60,
    },
    additionalSearchParameters: {
      queryMap: queryMap,
      num_typos: 1,
    },
  });

then when we consume, it would just change:

  const [indexes, setIndexes] = useState(['leads', 'cases', 'users', 'documents']);

  <InstantSearch
       searchClient={searchClient} 
       indexNames={indexes}  // this can be dynamic
   >
      <Configure hitsPerPage={12} />
      <SearchBox />
      <Hits />
 </InstantSearch>
sangdth commented 3 months ago

My apologies, I forgot that the InstantSearch is coming from Algolia, so the changes are super hard.

jasonbosco commented 3 months ago

My apologies, I forgot that the InstantSearch are coming from Algolia, so the changes is super hard.

Yeah...

Btw, you might want to consider using the Index widget in Instantsearch, if federated multi-index search is what you're looking to implement.

Here's an example: https://federated-search.typesense.org

You'll see the source code linked in the description.

sangdth commented 3 months ago

Thank you @jasonbosco , I've checked the source code, look like the best way to archive my goal is to use at least one common name field across collections.

sangdth commented 3 months ago

Updated:

For future devs that hitting the wall with their heads like me:

  <InstantSearch
    future={{ preserveSharedStateOnUnmount: true }}
    searchClient={searchClient}
  >
    <Configure hitsPerPage={5} />
    <SearchBox />
    <Index indexName="leads">
       <Hits />
    </Index>
    <Index indexName="cases">
       <Hits />
    </Index>
  </InstantSearch>

This way seems work.

May I ask, how can I use the multi_search with ReactInstantSearch? What is the proper indexName value? I see nowhere in the documentation.