# Typesense Instantsearch Adapter

An adapter to use the awesome [Instantsearch.js](https://github.com/algolia/instantsearch.js) library with a [Typesense](https://typesense.org) Search Server, to build rich search interfaces.

[![NPM version][npm-image]][npm-url]
![downloads](https://img.shields.io/npm/dt/typesense-instantsearch-adapter?label=downloads)

Here is an example of UI you can build with this adapter: [songs-search.typesense.org](https://songs-search.typesense.org)

**Note:** If your search interface is built on a custom autocomplete component, or is based on [@algolia/autocomplete-js](https://www.algolia.com/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete/), then you don't need this adapter to use it with Typesense, as [typesense-js](https://github.com/typesense/typesense-js) library already supports client-side fetching data from any async data sources. Read more [here](https://github.com/typesense/typesense-instantsearch-adapter/issues/88#issuecomment-1021597634).

## Quick Links

- [Background](#background)
- [Quick Start](#quick-start)
  - [Starter App](#starter-app)
- [Installation](#installation)
- [Usage](#usage)
  - [With instantsearch.js](#with-instantsearchjs)
  - [With react-instantsearch](#with-react-instantsearch) (also works with React Native)
  - [With vue-instantsearch](#with-vue-instantsearch)
  - [With angular-instantsearch](#with-angular-instantsearch)
- [Widget Specific Instructions](#widget-specific-instructions)
- [Caching](#caching)
- [Compatibility](#compatibility)
- [Development](#development)
- [Help](#help)

## Background

The good folks over at Algolia have built and open-sourced [Instantsearch.js](https://github.com/algolia/instantsearch.js) which is a collection of out-of-the-box components that you can use to build interactive search experiences swiftly.

With the adapter in this repository, you'll be able to use [Instantsearch](https://github.com/algolia/instantsearch.js) (and its [React](https://github.com/algolia/react-instantsearch), [Vue](https://github.com/algolia/vue-instantsearch) and [Angular](https://github.com/algolia/angular-instantsearch) cousins) with data indexed in a Typesense search server.

If you haven't used Instantsearch before, we recommend going through their Getting Started guide [here](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/#build-a-simple-ui).
Once you go through the guide, follow the instructions below to plug the Typesense adapter into Instantsearch.

## Quick Start

Here's a guide on building a quick search interface with Typesense and InstantSearch.js: [https://typesense.org/docs/0.20.0/guide/search-ui-components.html](https://typesense.org/docs/0.20.0/guide/search-ui-components.html)

### Starter App

Here's a demo starter app that shows you how to use the adapter: [https://github.com/typesense/typesense-instantsearch-demo](https://github.com/typesense/typesense-instantsearch-demo)

## Installation

```shell
$ npm install --save typesense-instantsearch-adapter @babel/runtime
```

or

```shell
$ yarn add typesense-instantsearch-adapter @babel/runtime
```

or, you can also directly include the adapter via a script tag in your HTML:

```html
<script src="https://cdn.jsdelivr.net/npm/typesense-instantsearch-adapter@2/dist/typesense-instantsearch-adapter.min.js"></script>

<!-- You might want to pin the version of the adapter used if you don't want to always receive the latest minor version -->
```

Since this is an adapter, it **will not install** the Instantsearch library automatically for you. You need to install one of the following in your application directly:

- [instantsearch.js](https://github.com/algolia/instantsearch.js)
- [react-instantsearch](https://github.com/algolia/react-instantsearch)
- [vue-instantsearch](https://github.com/algolia/vue-instantsearch)
- [angular-instantsearch](https://github.com/algolia/angular-instantsearch)

You'll find information on how to get started with each of the above libraries in their respective repos.

We'd also recommend checking out [create-instantsearch-app](https://github.com/algolia/create-instantsearch-app) to create your Search UI from a starter template.

## Usage

### With [instantsearch.js](https://github.com/algolia/instantsearch.js)

```javascript
import instantsearch from "instantsearch.js";
import { searchBox, hits } from "instantsearch.js/es/widgets";
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "abcd", // Be sure to use an API key that only allows search operations
    nodes: [
      {
        host: "localhost",
        path: "", // Optional. Example: If you have your typesense mounted in localhost:8108/typesense, path should be equal to '/typesense'
        port: "8108",
        protocol: "http",
      },
    ],
    cacheSearchResultsForSeconds: 2 * 60, // Cache search results from server. Defaults to 2 minutes. Set to 0 to disable caching.
  },
  // The following parameters are directly passed to Typesense's search API endpoint.
  //  So you can pass any parameters supported by the search endpoint below.
  //  query_by is required.
  additionalSearchParameters: {
    query_by: "name,description,categories",
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

const search = instantsearch({
  searchClient,
  indexName: "products",
});
search.addWidgets([
  searchBox({
    container: "#searchbox",
  }),
  hits({
    container: "#hits",
    templates: {
      item: `
        <div class="hit-name">
          {{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}
        </div>
      `,
    },
  }),
]);

search.start();
```

You can add any of the Instantsearch widgets [here](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/) that are [supported](#widget-compatibility) by the adapter.

You'll also find a working example in [test/support/testground](test/support/testground). To run it, run `npm run testground` from the project root folder.

### With [react-instantsearch](https://github.com/algolia/react-instantsearch)

```jsx harmony
import React from "react";
import ReactDOM from "react-dom";
import { SearchBox } from "react-instantsearch-dom";
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "abcd", // Be sure to use an API key that only allows search operations
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "", // Optional. Example: If you have your typesense mounted in localhost:8108/typesense, path should be equal to '/typesense'
        protocol: "http",
      },
    ],
    cacheSearchResultsForSeconds: 2 * 60, // Cache search results from server. Defaults to 2 minutes. Set to 0 to disable caching.
  },
  // The following parameters are directly passed to Typesense's search API endpoint.
  //  So you can pass any parameters supported by the search endpoint below.
  //  query_by is required.
  additionalSearchParameters: {
    query_by: "name,description,categories",
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <SearchBox />
    <Hits />
  </InstantSearch>
);
```

You can then add any of the Instantsearch-React widgets [here](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/react/) that are [supported](#widget-compatibility) by the adapter.

The instructions above also apply to React Native.

### With [vue-instantsearch](https://github.com/algolia/vue-instantsearch)

App.vue:

```vue
<template>
  <ais-instant-search :search-client="searchClient" index-name="products">
    <ais-search-box />
    <ais-hits>
      <div slot="item" slot-scope="{ item }">
        <h2>{{ item.name }}</h2>
      </div>
    </ais-hits>
  </ais-instant-search>
</template>

<script>
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "abcd", // Be sure to use an API key that only allows search operations
    nodes: [
      {
        host: "localhost",
        path: "", // Optional. Example: If you have your typesense mounted in localhost:8108/typesense, path should be equal to '/typesense'
        port: "8108",
        protocol: "http",
      },
    ],
    cacheSearchResultsForSeconds: 2 * 60, // Cache search results from server. Defaults to 2 minutes. Set to 0 to disable caching.
  },
  // The following parameters are directly passed to Typesense's search API endpoint.
  //  So you can pass any parameters supported by the search endpoint below.
  //  query_by is required.
  additionalSearchParameters: {
    query_by: "name,description,categories",
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

export default {
  data() {
    return {
      searchClient,
    };
  },
};
</script>
```

You can then add any of the Instantsearch widgets [here](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/vue/) that are [supported](#widget-compatibility) by the adapter.

### With [angular-instantsearch](https://github.com/algolia/angular-instantsearch)

```javascript
// app.component.ts
import { Component } from "@angular/core";
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "abcd", // Be sure to use an API key that only allows search operations
    nodes: [
      {
        host: "localhost",
        path: "", // Optional. Example: If you have your typesense mounted in localhost:8108/typesense, path should be equal to '/typesense'
        port: "8108",
        protocol: "http",
      },
    ],
    cacheSearchResultsForSeconds: 2 * 60, // Cache search results from server. Defaults to 2 minutes. Set to 0 to disable caching.
  },
  // The following parameters are directly passed to Typesense's search API endpoint.
  //  So you can pass any parameters supported by the search endpoint below.
  //  query_by is required.
  additionalSearchParameters: {
    query_by: "name,description,categories",
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  config = {
    indexName: "products",
    searchClient,
  };
}
```

You can then add any of the Instantsearch widgets [here](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/angular/) that are [supported](#widget-compatibility) by the adapter.

## Widget Specific Instructions

### `hierarchicalMenu`

For this widget, you want to create independent fields in the collection's schema with this specific naming convention:

- `field.lvl0`
- `field.lvl1`
- `field.lvl2`

for a nested hierarchy of `field.lvl0 > field.lvl1 > field.lvl2`

Each of these fields can also hold an array of values. This is useful for handling multiple hierarchies.

### `sortBy`

When instantiating this widget, you want to set the value of the index name to this particular format:

```javascript
search.addWidgets([
  sortBy({
    container: "#sort-by",
    items: [
      { label: "Default", value: "products" },
      { label: "Price (asc)", value: "products/sort/price:asc" },
      { label: "Price (desc)", value: "products/sort/price:desc" },
    ],
  }),
]);
```

The generalized pattern for the value attribute is: `<index_name>[/sort/<sort_by>]`. The adapter will use the value in `<sort_by>` as the value for the `sort_by` search parameter.

### `configure`

If you need to specify a `filter_by` search parameter for Typesense, you want to use the `configure` InstantSearch widget, along with `facetFilters`, `numericFilters` or `filters`.

The format for `facetFilters` and `numericFilters` is the same as Algolia's as described [here](https://www.algolia.com/doc/api-reference/api-parameters/facetFilters/).
But `filters` needs to be in Typesense's `filter_by` format as described in this table [here](https://typesense.org/docs/latest/api/documents.html#query-parameters).

Setting `filter_by` inside the `additionalQueryParameters` config only works when the widgets are loaded initially, because InstantSearch internally overrides the `filter_by` field subsequently.
Read more [here](https://github.com/typesense/typesense-instantsearch-adapter/issues/17#issuecomment-746912375).

### `index`

For Federated / Multi-Index Search, you'd need to use the `index` widget. To then be able to specify different search parameters for each index/collection, you can specify them using the `collectionSpecificSearchParameters` configuration:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "abcd", // Be sure to use an API key that only allows search operations
    nodes: [{ host: "localhost", path: "/", port: "8108", protocol: "http" }],
  },
  // Search parameters that are common to all collections/indices go here:
  additionalSearchParameters: {
    numTypos: 3,
  },
  // Search parameters that need to be *overridden* on a per-collection-basis go here:
  collectionSpecificSearchParameters: {
    products: {
      query_by: "name,description,categories",
    },
    brands: {
      query_by: "name",
    },
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;
```

Essentially, any parameters set in `collectionSpecificSearchParameters` will be merged with the values in `additionalSearchParameters` when querying Typesense, effectively overriding values in `additionalSearchParameters` on a per-collection-basis.

#### Union Search

> Available as of typesense-instantsearch-adapter `2.10.0` and Typesense Server `v28.0`

Union search allows you to merge search results from multiple search queries into a single ordered set of hits. This is particularly useful when you want to combine results from different filters or collections.

To enable union search, set the `union` parameter to `true` when instantiating the adapter:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  union: true, // <======= Enable union search
  collectionSpecificSearchParameters: {
    products: {
      query_by: "name,description,categories",
    },
    brands: {
      query_by: "name",
    },
  },
});
```

When union search is enabled, the adapter will merge search results from multiple search requests into a single ordered result set. This differs from regular multi-search in the following ways:

- **Pagination**: Only global pagination parameters are considered, not individual search pagination parameters
- **Sorting**: All search requests must have the same type, count, and order of sorting fields for the union to work correctly
- **Result merging**: Results from each search query are combined into one final result set

The union search feature leverages Typesense's `multi_search` endpoint with the `union: true` parameter to provide seamless result merging at the server level. For more information, refer to the [Union Search Documentation](https://typesense.org/docs/28.0/api/federated-multi-search.html#union-search)

### `geoSearch`

Algolia uses `_geoloc` by default for the name of the field that stores the lat long values for a record.
In Typesense, you can name the geo location field anything. If you use a name other than `_geoloc`, you need to specify it when initializing the adapter like below, so InstantSearch can access it:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  geoLocationField: "lat_lng_field", // <<======
  additionalSearchParameters,
});
```

### `dynamicWidgets`

> Available as of Typesense Server `v0.25.0.rc12`

This [`dynamicWidgets`](https://www.algolia.com/doc/api-reference/widgets/dynamic-facets/js/) widget works out of the box with no additional changes,
but if you want to control the order in which these facets are displayed in the UI
Instantsearch expects a parameter called [`renderingContent`](https://www.algolia.com/doc/api-reference/api-parameters/renderingContent/) to be set.

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  renderingContent: {
    // <<===== Add this, only if you want to control the order of the widgets displayed by dynamicWidgets
    facetOrdering: {
      facets: {
        order: ["size", "brand"], // <<===== Change this as needed
      },
    },
  },
  additionalSearchParameters,
});
```

Read more about all available options for `renderingContent` in [Algolia's documentation here](https://www.algolia.com/doc/api-reference/api-parameters/renderingContent/?client=javascript#examples).

### Special characters in field names / values

> Available as of typesense-instantsearch-adapter `2.7.0-2`

- If any string fields in your documents have a colon `:` in their values (for eg, let's say there's a field called `{ brand: "a:b" }`, then you would need to add a parameter like below when instantiating the adapter:

  ```js
  const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
    server: {
      apiKey: "xyz",
      nodes: [
        {
          host: "localhost",
          port: "8108",
          path: "/",
          protocol: "http",
        },
      ],
    },
    facetableFieldsWithSpecialCharacters: ["brand"], // <======= Add string fields that have colons in their values here, to aid in parsing
    additionalSearchParameters,
  });
  ```

- If any numeric field _names_ in your documents have special characters like `>`, `<`, `=` (for eg, let's say there's a field called `{ price>discount: 3.0 }`) then you would need to add a parameter like below when instantiating the adapter:

  ```js
  const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
    server: {
      apiKey: "xyz",
      nodes: [
        {
          host: "localhost",
          port: "8108",
          path: "/",
          protocol: "http",
        },
      ],
    },
    facetableFieldsWithSpecialCharacters: ["price>discount"], // // <======= Add numeric fields that have >, < or = in their names, to aid in parsing
    additionalSearchParameters,
  });
  ```

### Setting `facet_by` options

> Available as of typesense-instantsearch-adapter `2.8.0-1` and Typesense Server `v0.26.0.rc25`

The `facet_by` parameter is managed by InstantSearch internally when you use the various filter widgets.

But if you need to pass custom options to the `facet_by` parameter (eg: server-side sort options), then you can use the `facetByOptions` parameter as shown below:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  facetByOptions: {
    brand: "(sort_by: _alpha:asc)",
    category: "(sort_by: _alpha:desc)",
  }, // <======= Add any facet_by parameter as a key value pair. Don't forget the surrounding parentheses in the value.
  collectionSpecificFacetByOptions: {
    collection1: {
      brand: "(sort_by: _alpha:desc)",
    },
  }, // <======= Use this parameter if multiple collections share the same field names, and you want to use different options for each field. This will override facetByOptions for that particular collection.
  additionalSearchParameters,
});
```

Note that for sorting in refinementLists, in addition to sorting on the Typesense Server-side, you'd also need to pass the [`sortBy`](https://www.algolia.com/doc/api-reference/widgets/refinement-list/js/#widget-param-sortby) parameter to the refinementList widget to also sort the results appropriately on the client-side.

### Setting `filter_by` options

> Available as of typesense-instantsearch-adapter `2.8.0-5`

The `filter_by` parameter is managed by InstantSearch internally when you use the various filter widgets.

By default, the adapter uses exact filtering (`filter_by: field:=value`) when sending the queries to Typesense.
If you need to configure the adapter to use `:` (non-exact word-level filtering - `filter_by: field:value`), you want to instantiate the adapter using the `filterByOptions` configuration:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  filterByOptions: {
    brand: { exactMatch: false }, // <========== Add this to do non-exact word-level filtering
    category: { exactMatch: false },
  },
  collectionSpecificFilterByOptions: {
    collection1: {
      brand: { exactMatch: false },
    },
  }, // <======= Use this parameter if multiple collections share the same field names, and you want to use different options for each field. This will override filterByOptions for that particular collection.
  additionalSearchParameters,
});
```

### Flipping negative refinement operators

By default, excluded values from `refinementList` are encoded as list filters,
like `field:![a,b]`, while included OR values are encoded as `field:[a,b]`.

If you want to flip how negative refinements are encoded, set
`flipNegativeRefinementOperator: true`:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  flipNegativeRefinementOperator: true,
  additionalSearchParameters,
});
```

With this option enabled:

- `operator: "and"` with negative values is merged as:
  `field:![a,b]`
- `operator: "or"` with negative values is expanded as:
  `(field:!a || field:!b)`
- positive OR behavior is unchanged:
  `field:[a,b]`

### Disabling overrides for certain sorts

> Available as of typesense-instantsearch-adapter `2.9.0-0`

Here's a way to disable overrides / curation rules, when users select a particular sort order:

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  sortByOptions: {
    "field1:desc,field2:desc": { enable_overrides: false }, // <========== Add this to disable sorting when this particular Typesense `sort_by` string is generated by the sortBy widget
  },
  collectionSpecificSortByOptions: {
    collection2: {
      "field1:desc,field2:desc": { enable_overrides: false },
    },
  }, // <======= Use this parameter if multiple collections share the same field names, and you want to use different options for each field. This will override sortByOptions for that particular collection.
  additionalSearchParameters,
});
```

If you have a sortBy widgets configured with an indexName value of `products/sort/price:asc` for eg, then the key inside `sortByOptions` should be `price:asc`.

### Grouped Hits

> Available as of typesense-instantsearch-adapter `2.7.1-4`

By default, when `group_by` is used as a search parameters, the adapter flattens the results across all groups into a single list of sequential hits.

If you'd like to preserve the groups, you want to set `flattenGroupedHits: false` when instantiating the adapter.

This will place the first hit in a group as the primary hit, and then add all hits in the group inside a `_grouped_hits` key inside each hit.

```js
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  flattenGroupedHits: false, // <=======
  additionalSearchParameters,
});
```

### Vector Search

> Available as of typesense-instantsearch-adapter `2.7.0-3`

The general idea is to first hook into the query life-cycle of Instantsearch, intercept the typed query and send it to an embedding API, fetch the embeddings and then send the vectors to Typesense to do a nearest neighbor vector search.

Here's a demo that you can run locally to see this in action: [https://github.com/typesense/showcase-hn-comments-semantic-search](https://github.com/typesense/showcase-hn-comments-semantic-search).

Here's how to do this in Instantsearch.js:

```javascript
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "xyz",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        path: "/",
        protocol: "http",
      },
    ],
  },
  additionalSearchParameters,
});
// from https://github.com/typesense/showcase-hn-comments-semantic-search/blob/8a33006cae58b425c53f56a64e1273e808cd9375/src/js/index.js#L101
const searchClient = typesenseInstantsearchAdapter.searchClient;
search = instantsearch({
  searchClient,
  indexName: INDEX_NAME,
  routing: true,
  async searchFunction(helper) {
    // This fetches 200 (nearest neighbor) results for semantic / hybrid search

    let query = helper.getQuery().query;
    const page = helper.getPage(); // Retrieve the current page

    if (query !== "" && ["semantic", "hybrid"].includes($("#search-type-select").val())) {
      console.log(helper.getQuery().query);
      helper
        .setQueryParameter(
          "typesenseVectorQuery", // <=== Special parameter that only works in typesense-instantsearch-adapter@2.7.0-3 and above
          `embedding:([], k:200)`,
        )
        .setPage(page)
        .search();
      console.log(helper.getQuery().query);
    } else {
      helper.setQueryParameter("typesenseVectorQuery", null).setPage(page).search();
    }
  },
});
```

## Caching

There are two modes of caching:

1. **Server-side caching:**

   To enable server-side caching, add a parameter called `useServerSideSearchCache: true` in the `server` configuration block of the typesense-instantsearch-adapter like this:

   ```javascript
   const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
     server: {
       apiKey: "...",
       nearestNode: {...},
       nodes: [...],
       useServerSideSearchCache: true // <<< Add this to send use_cache as a query parameter instead of post body parameter
     },
     additionalSearchParameters: {...}
   });
   ```

   This will cause the adapter to add `?use_cache=true` as a URL query parameter to all search requests initiated by the adapter, which will then cause Typesense Server to enable server-side caching for these requests.

2. **Client-side caching:**

   The adapter also has client-side caching enabled by default, to prevent unnecessary network calls to the server. The TTL for this client-side cache can be configured like this:

   ```javascript
   const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
    server: {
      apiKey: "...",
      nearestNode: {...},
      nodes: [...],
      cacheSearchResultsForSeconds: 2 * 60 // <<< Add this to configure the TTL for client-side cache in the browser
    },
    additionalSearchParameters: {...}
   });
   ```

## Compatibility

| Typesense Server | typesense-instantsearch-adapter | instantsearch.js | react-instantsearch | vue-instantsearch | angular-instantsearch |
| ---------------- | ------------------------------- | ---------------- | ------------------- | ----------------- | --------------------- |
| \>= v0.25.0      | \>= v2.7.1                      | \>= 4.51         | \>= 6.39            | \>= 4.8           | \>= 4.4               |
| \>= v0.25.0.rc14 | \>= v2.7.0-1                    | \>= 4.51         | \>= 6.39            | \>= 4.8           | \>= 4.4               |
| \>= v0.25.0.rc12 | \>= v2.6.0                      | \>= 4.51         | \>= 6.39            | \>= 4.8           | \>= 4.4               |
| \>= v0.24        | \>= v2.5.0                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |
| \>= v0.21        | \>= v2.0.0                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |
| \>= v0.19        | \>= v1.0.0                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |
| \>= v0.15        | \>= v0.3.0                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |
| \>= v0.14        | \>= v0.2.0                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |
| \>= v0.13        | \>= v0.1.0                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |
| \>= v0.12        | \>= v0.0.4                      | \>= 4.2.0        | \>= 6.0.0           | \>= 2.2.1         | \>= 3.0.0             |

If a particular version of the above libraries don't work with the adapter, please open a Github issue with details.

### Widget Compatibility

This adapter works with all widgets in [this list](https://www.algolia.com/doc/api-reference/widgets/js/)

## Development

```shell
$ npm install
$ npm run typesenseServer
$ FORCE_REINDEX=true npm run indexTestData

$ npm link typesense-instantsearch-adapter
$ npm run testground

$ npm test
```

To release a new version, we use the [np](https://github.com/sindresorhus/np) package:

```shell
$ npm install --global np
$ np

# Follow instructions that np shows you

```

## Help

If you have any questions or run into any problems, please create a Github issue and we'll try our best to help.

© 2020-present Typesense, Inc.

[npm-image]: https://badge.fury.io/js/typesense-instantsearch-adapter.svg
[npm-url]: https://npmjs.org/package/typesense-instantsearch-adapter
