gmourier / instant-meilisearch

The search client to use MeiliSearch with InstantSearch.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Instant-MeiliSearch

Instant MeiliSearch

npm version Tests License Bors enabled

⚑ How to integrate a front-end search bar in your website using MeiliSearch

MeiliSearch is an open-source search engine. Discover what MeiliSearch is!

This library is a plugin to establish the communication between your MeiliSearch instance and the open-source InstantSearch tools (powered by Algolia) for your front-end application.
Instead of reinventing the wheel, we have opted to reuse the InstantSearch library for our own front-end tooling. We will contribute upstream any improvements that may result from our adoption of InstantSearch.

If you use Angular, React, or Vue, you might want to check out these repositories:

NB: If you don't have any MeiliSearch instance running and containing your data, you should take a look at this getting started page.

Table of Contents

Installation

Use npm or yarn to install instant-meilisearch:

npm install @meilisearch/instant-meilisearch
yarn add @meilisearch/instant-meilisearch

Usage

Basic

import { instantMeiliSearch } from '@meilisearch/instant-meilisearch'

const searchClient = instantMeiliSearch(
  'https://demos.meilisearch.com',
  'dc3fedaf922de8937fdea01f0a7d59557f1fd31832cb8440ce94231cfdde7f25'
)

Customization

import { instantMeiliSearch } from '@meilisearch/instant-meilisearch'

const searchClient = instantMeiliSearch(
  'https://demos.meilisearch.com',
  'dc3fedaf922de8937fdea01f0a7d59557f1fd31832cb8440ce94231cfdde7f25',
  {
    paginationTotalHits: 30, // default: 200.
    placeholderSearch: false, // default: true.
    primaryKey: 'id', // default: undefined
  }
)
  • placeholderSearch (true by default). Displays documents even when the query is empty.

  • paginationTotalHits (200 by default): The total (and finite) number of hits you can browse during pagination when using the pagination widget. If the pagination widget is not used, paginationTotalHits is ignored.
    Which means that, with a paginationTotalHits default value of 200, and hitsPerPage default value of 20, you can browse paginationTotalHits / hitsPerPage => 200 / 20 = 10 pages during pagination. Each of the 10 pages containing 20 results.
    The default value of hitsPerPage is set to 20 but it can be changed with InsantSearch.configure.
    ⚠️ MeiliSearch is not designed for pagination and this can lead to performances issues, so the usage of the pagination widget is not encouraged. However, the paginationTotalHits parameter lets you implement this pagination with less performance issue as possible: depending on your dataset (the size of each document and the number of documents) you might decrease the value of paginationTotalHits.
    More information about MeiliSearch and the pagination here.

  • primaryKey (undefined by default): Specify the field in your documents containing the unique identifier. 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.

Example with InstantSearch

The open-source InstantSearch library powered by Algolia provides all the front-end tools you need to highly customize your search bar environment.

InstantSearch requires that you provide an indexName. The indexName corresponds to the index uid in which your document are stored in MeiliSearch.

In index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
  </head>

  <body>
    <div>
      <div id="searchbox"></div>
      <div id="hits"></div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/dist/instant-meilisearch.umd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4"></script>
    <script src="./app.js"></script>
  </body>
</html>

In app.js:

const search = instantsearch({
  indexName: 'steam-video-games',
  searchClient: instantMeiliSearch(
    'https://demos.meilisearch.com',
    'dc3fedaf922de8937fdea01f0a7d59557f1fd31832cb8440ce94231cfdde7f25'
  ),
})

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

search.start()

πŸš€ For a full getting started example, please take a look at this CodeSandbox:

Edit MS + IS

πŸ’‘ If you have never used InstantSearch, we recommend reading this getting started documentation.

More Documentation

πŸ€– Compatibility with MeiliSearch and InstantSearch

Supported InstantSearch.js versions:

This package only guarantees the compatibility with the version v4 of InstantSearch.js. It may work with older or newer InstantSearch versions, but these are not tested nor officially supported at this time.

Supported MeiliSearch versions:

This package only guarantees the compatibility with the version v0.20.0 of MeiliSearch.

Node / NPM versions:

  • NodeJS >= 12.10 <= 14
  • NPM >= 6.x

API resources

List of all the components that are available in instantSearch and their compatibilty with MeiliSearch.

βœ… InstantSearch

instantSearch references

instantSearch is the main component. It manages the widget and lets you add new ones.

  • βœ… IndexName: uid of your index. required
  • βœ… SearchClient: Search client, in our case instantMeiliSearch. See customization for details on options. required
  • ❌ numberLocale: Does not work with both Algoliasearch and InstantMeiliSearch.
  • βœ… searchFunction: Surcharge the search function provided by the search client.
  • βœ… initialUiState: Determine the search state on app start.
  • βœ… onStateChange: Change search state on change (see option above).
  • βœ… stalledSearchDelay: Time in ms before search is considered stalled. Used for loader.
  • βœ… routing: browser URL synchronization, search parameters appear in current URL (guide).
  • βœ… insightsClient: Hook analytics to search actions (see insight section).
const search = instantsearch({
  indexName: 'instant_search',
  searchClient: instantMeiliSearch(
    'https://demos.meilisearch.com',
    'dc3fedaf922de8937fdea01f0a7d59557f1fd31832cb8440ce94231cfdde7f25',
    {
      // ... InstantMeiliSearch options
    }
  ),
  // ... InstantSearch options
  routing: true // for example
})

❌ Index

Index references

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.

βœ… SearchBox

SearchBox references

The searchBox widget is used to let the user perform a text-based query.

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
  • βœ… placeholder: Placeholder of the search box.
  • βœ… autofocus: Whether the search box is focused on arrival.
  • βœ… searchAsYouType: Whether result appears as you type or after pressing enter.
  • ❌ showReset: Does not work with both algoliaSearch and instantMeiliSearche
  • ❌ showSubmit: Does not work with both algoliaSearch and instantMeiliSearch
  • βœ… showLoadingIndicator: Whether to show the spinning loader.
  • βœ… queryHook: A function that is called just before the search is triggered.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.
instantsearch.widgets.searchBox({
  container: '#searchbox',
  autofocus: true,
  ...searchBoxOptions
}),

βœ… Configure

The configure widget lets you provide raw search parameters to the Algolia API without rendering anything.

Because these are the search parameters of AlgoliaSearch and not the InstantSearch parameters, some of them are ignored by InstantSearch.
Since we do not act as AlgoliaSearch on search parameters, detailed compatibility can be found in this issue.
This component should only be used if no other component provides the same configuration.

We also suggest looking at MeiliSearch's search parameters to determine how they act.

instantsearch.widgets.configure({
  hitsPerPage: 6,
  // other algoliaSearch parameters
})

❌ ConfigureRelatedItems

ConfigureRelatedItems references.

No compatibility with MeiliSearch because the component uses sumOrFiltersScores and optionalFilters search parameters that do not exist in MeiliSearch.

Panel

Panel references

The panel widget wraps other widgets in a consistent panel design.

  • βœ… hidden: Function to determine if pannel should be hidden on earch render.
  • βœ… collapsed: Function to determine if pannel should be collapsed on earch render.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.

❌ Autocomplete

Autocomplete references

Deprecated component in InstantSearch in favor of autocomplete package.

InstantMeiliSearch is not compatible with the autocomplete package.

βœ… Voice Search

Voice Search references

The voiceSearch widget lets the user perform a voice-based query.

βœ… Insight

Insight references

Search Insights lets you report click, conversion and view metrics.

More details about the subject are given in this issue.

Requires InstantSearch v4.8.3 or later.

  • βœ… insightsClient: Insight client uses search-insight.js.
  • βœ… insightsInitParams: Insight params.
  • βœ… onEvent?: function triggered on events.

βœ… Middleware

Middleware references

Middleware is a function returning an object with onStateChange, subscribe and unsubscribe functions. With the middleware API, you can inject functionalities in the InstantSearch lifecycle.

βœ… renderState

renderState references

Provides all the data and functions from the widgets.

It works only on widgets that are compatible with instantMeiliSearch.

βœ… Hits

hits references

Used to display a list of results.

  • βœ… container: The CSS Selector or HTMLElement to insert the hits into. required
  • βœ… escapeHTML: Escapes HTML tags in string values in returned hits.
  • βœ… transformItems: Function that maps over every hit the provided logic.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.
instantsearch.widgets.hits({
  container: '#hits',
  templates: {
    item: `
        <div>
          <div class="hit-name">
            {{#helpers.highlight}}{ "attribute": "title" }{{/helpers.highlight}}
          </div>
        </div>
      `,
  },
})

βœ… InfiniteHits

infiniteHits references

The infiniteHits widget is used to display a list of results with a β€œShow more” button.

  • βœ… container: The CSS Selector or HTMLElement to insert the hits into. required
  • βœ… escapeHTML: Escapes HTML tags in string values in returned hits.
  • βœ… transformItems: Function that maps over every hit the provided logic.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.
  • ❌ showPrevious: Does not work with both Algoliasearch and InstantMeiliSearch.
  • ❌ cache: Not added in InstantMeiliSearch.
instantsearch.widgets.infiniteHits({
  container: '#infinite-hits',
  templates: {
    item: `
      <h2>
        {{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}
      </h2>
    `,
  },
})

βœ… Highlight

highlight references

The highlight function returns an attribute from a hit into its highlighted form, when relevant.

  • βœ… attribute: The attribute of the record to highlight. required
  • βœ… hit: Hit object. required
  • βœ… highlightedTagName: HTML element to wrap the highlighted parts of the string.

See Hits for an example.

βœ… Snippet

Snippet references

The snippet function returns an attribute from a hit into its snippet form, when relevant.

  • βœ… attribute: The attribute of the record to snippet and highlight. required
  • βœ… hit: Hit object. required
  • βœ… highlightedTagName: HTML element to wrap the highlighted parts of the string.

Note that the attribute has to be added to attributesToSnippet in configuration. Highlight is applied on snippeted fields.

Snippeting is called cropping in MeiliSearch, more about it here. It is possible to change the size of the snippeting by adding its character size in the attributesToSnippet parameter.
For example: "description:40".

The 40 value represents the number of characters (rounded down to always have full words) and not the number of words. Thus, the snippet string size is always equal to or lower than 40 characters.

instantsearch.widgets.configure({
  attributesToSnippet: ['description:40'],
})
instantsearch.widgets.hits({
  // ...
  templates: {
    item: `
      <p>{{#helpers.snippet}}{ "attribute": "description" }{{/helpers.snippet}}</p>
    `,
  },
})

❌ Geo Search

Geo search references

No compatibility because MeiliSearch does not support Geo Search.

If you'd like to see it implemented please vote for it in the roadmap.

❌ Answers

Answers references.

No compatibility because MeiliSearch does not support this experimental feature.

βœ… RefinementList

Refinement list references

The refinementList widget is one of the most common widgets you can find in a search UI. With this widget, the user can filter the dataset based on facets.

  • βœ… container: The CSS Selector or HTMLElement to insert the refinements. required
  • βœ… attribute: The facet to display required
  • βœ… operator: How to apply facets, "AND" or "OR"
  • βœ… limit: How many facet values to retrieve.
  • βœ… showMore: Whether to display a button that expands the number of items.
  • βœ… showMoreLimit: The maximum number of displayed items. Does not work when showMoreLimit > limit.
  • ❌ searchable: Whether to add a search input to let the user search for more facet values. Not supported by MeiliSearch. If you'd like to see it implemented please vote.
  • ❌ searchablePlaceholder: The value of the search input’s placeholder. Not supported, see searchable.
  • ❌ searchableIsAlwaysActive: When false, disables the facet search input. Not supported, see searchable.
  • ❌ searchableEscapeFacetValues: When true, escapes the facet values. Not supported, see searchable.
  • ❌ sortBy: Not supported natively but can be implemented manually using transformItems options.
  • βœ… transformItems: A function to transform the items passed to the templates.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.

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

instantsearch.widgets.refinementList({
  container: '#refinement-list',
  attribute: 'genres',
})

❌ HierarchicalMenu

Hierarchical menu references

The hierarchicalMenu widget is used to create a navigation based on a hierarchy of facet attributes. It is commonly used for categories with subcategories.

No compatibility because MeiliSearch does not support hierarchical facets.

If you'd like get nested facets/hierarchical facets implemented, please vote for it in the roadmap.

βœ… RangeSlider

Range slider references

The rangeSlider widget provides a user-friendly way to filter the results, based on a single numeric range.

  • βœ… container: The CSS Selector or HTMLElement to insert the refinements. required
  • βœ… attribute: The name of the attribute in the document. required.
  • βœ… min: The minimum value for the input. required
  • βœ… max: The maximum value for the input. required
  • ❌ precision: The number of digits after the decimal point to use. Not compatible as only integers work with rangeSlider.
  • βœ… step: The number of steps between each handle move.
  • βœ… pips: Whether to show slider pips (ruler marks).
  • βœ… tooltips: Whether to show tooltips. The default tooltips show the raw value.
  • βœ… cssClasses: The CSS classes to override.

⚠️ The component is compatible but only by applying the following requirements:

1. Only integers

Only attributes with integers values are compatible.

2. Manual Min Max

Min and max of attributes are not returned from MeiliSearch and thus must be set manually.

  instantsearch.widgets.rangeSlider({
    // ...
    min: 0,
    max: 100000,
  }),

3. emove attributes from facet list

If the attribute is not in the attributesForFacetting setting list, an error is thrown. The error comes from InstantSearch adding the attribute in the facets list and MeiliSearch throwing an error when a non-existent facet is queried on search.

  instantsearch.widgets.rangeSlider({
    attribute: 'recommendationCount',
    // ...
  }),

Throws the following error.

{
  "message": "Attribute recommendationCount is not set as facet",
  "errorCode": "bad_request",
  "errorType": "invalid_request_error",
  "errorLink": "https://docs.meilisearch.com/errors#bad_request"
}

To avoid this error, the facet should be removed from the query parameter before it is sent to MeiliSearch.

The InstantSearch component provides an option called searchFunction that is called before instantMeiliSearch search function. By using this component it is possible to remove the attribute from the list.

const search = instantsearch({
  // ...
  searchFunction(helper) {
    const excludeFacets = ['recommendationCount']
    Object.assign(helper.state, {
      disjunctiveFacets: helper.state.disjunctiveFacets.filter(
        (facet) => !excludeFacets.includes(facet)
      ),
      facets: helper.state.facets.filter(
        (facet) => !excludeFacets.includes(facet)
      ),
    })
    helper.search()
  },
})

After these steps, rangeSlider becomes compatible.

βœ… Menu

Menu references

The menu widget displays a menu that lets the user choose a single value for a specific attribute.

  • βœ… container: The CSS Selector or HTMLElement to insert the menu. required
  • βœ… attribute: the name of the facet attribute. required
  • βœ… limit: How many facet values to retrieve.
  • βœ… showMore: Whether to display a button that expands the number of items.
  • βœ… showMoreLimit: The maximum number of displayed items. Does not work when showMoreLimit > limit
  • ❌ sortBy: Not supported natively but can be implemented manually using transformItems options.
  • βœ… transformItems: A function to transform the items passed to the templates.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.

βœ… currentRefinements

currentRefinements

The currentRefinements widget displays a list of refinements applied to the search.

  • βœ… container: The CSS Selector or HTMLElement to insert the current refinements UI. required
  • βœ… includedAttributes: The facet name of which checked attributes are included.
  • βœ… excludedAttributes: The facet name of which checked attributes are excluded.
  • βœ… cssClasses: The CSS classes to override.
  • βœ… transformItems: A function to transform the items passed to the templates.

βœ… RangeInput

Range input references

The rangeInput widget allows a user to select a numeric range using a minimum and maximum input.

  • βœ… container: The CSS Selector or HTMLElement to insert widget into. required
  • βœ… attribute: The name of the attribute in the document. required.
  • βœ… min: The minimum value for the input.
  • βœ… max: The maximum value for the input
  • βœ… precision: The number of digits after the decimal point to use.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.

⚠️ Not compatible with MeiliSearch by default, needs a workaround. See same workaround as RangeSlider.

βœ… MenuSelect

Menu select references

The menuSelect widget allows a user to select a single value to refine inside a select element.

  • βœ… container: The CSS Selector or HTMLElement to insert widget into. required
  • βœ… attribute: The name of the attribute in the document. required.
  • βœ… limit: How many facet values to retrieve.
  • ❌ sortBy: Not supported natively but can be implemented manually using transformItems options.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.
  • βœ… transformItems: A function to transform the items passed to the templates.

βœ… ToggleRefinement

Toggle refinement references

The numericMenu widget displays a list of numeric filters in a list. Those numeric filters are pre-configured when creating the widget.

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
  • βœ… attribute: The name of the attribute on which apply the refinement. required
  • βœ… on: The value of the refinement to apply on the attribute when checked.
  • βœ… off: The value of the refinement to apply on the attribute when unchecked.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.

The toggleRefinement widget provides an on/off filtering feature based on an attribute value.

βœ… NumericMenu

Numeric Menu references

The numericMenu widget displays a list of numeric filters in a list. Those numeric filters are pre-configured when creating the widget.

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
  • βœ… attribute: The name of the attribute in the document. required.
  • βœ… items: A list of all the options to display. required
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.
  • βœ… transformItems: function receiving the items, called before displaying them.

❌ RatingMenu

Rating menu references

The RatingMenu widget lets the user refine search results by clicking on stars. The stars are based on the selected attribute.

No compatibility because MeiliSearch does not support integers as facet and instantSearch uses facets information to showcase the UI elements.

βœ… ClearRefinements

Clear refinements references

The clearRefinement widget displays a button that lets the user clean every refinement applied to the search. You can control which attributes are impacted by the button with the options.

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
instantsearch.widgets.clearRefinements({
    container: '#clear-refinements',
  }),

βœ… Pagination

Pagination references

The pagination widget displays a pagination system allowing the user to change the current page.

We do not recommend using this widget as pagination slows the search responses. Instead, the InfiniteHits component is recommended.

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
  • βœ… showFirst: Whether to display the first-page link.
  • βœ… showPrevious: Whether to display the previous page link.
  • βœ… showNext: Whether to display the next page link.
  • βœ… showLast: Whether to display the last page link.
  • βœ… padding: The number of pages to display on each side of the current page.
  • βœ… totalPages: The maximum number of pages to browse.
  • βœ… scrollTo: Where to scroll after a click. Set to false to disable.
  • βœ… templates: The templates to use for the widget.
  • βœ… cssClasses: The CSS classes to override.
instantsearch.widgets.pagination({
  container: '#pagination',
})

βœ… HitsPerPage

Hits per page references

The hitsPerPage widget displays a dropdown menu to let the user change the number of displayed hits.

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
  • βœ… items: The list of available options required
  • βœ… cssClasses: The CSS classes to override.
  • βœ… transformItems: function receiving the items, called before displaying them.

❌ Breadcrumb

Breadcrumb references

The breadcrumb widget is a secondary navigation scheme that lets the user see where the current page is in relation to the facet’s hierarchy.

No compatibility because MeiliSearch does not support hierarchical facets.

If you'd like get nested facets implemented, please vote for it in the roadmap.

βœ… Stats

Stats references

The stats widget displays the total number of matching hits and the time it took to get them (time spent in the Algolia server).

  • βœ… container: The CSS Selector or HTMLElement to insert the widget into. required
  • βœ… cssClasses: The CSS classes to override.
  • βœ… transformItems: function receiving the items, called before displaying them.
instantsearch.widgets.stats({
  container: '#stats',
})

❌ Analytics

Analytics

Deprecated. See Insight.

❌ QueryRuleCustomData

QueryRuleCustomData references

You may want to use this widget to display banners or recommendations returned by Rules, and that match search parameters.

No compatibility because MeiliSearch does not support Rules.

❌ QueryRuleContext

Query rule context references

The queryRuleContext widget lets you apply ruleContexts based on filters to trigger context-dependent Rules.

No compatibility because MeiliSearch does not support Rules.

❌ SortBy

Sort by references

The sortBy widget displays a list of indices, allowing a user to change the way hits are sorted (with replica indices). Another common use case is to let the user switch between different indices.

No compatibility because MeiliSearch does not support hierarchical facets.

If you'd like to get the "SortBy" feature, please vote for it in the [roadmap]https://roadmap.meilisearch.com/c/32-sort-by?utm_medium=social&utm_source=portal_share).

❌ RelevantSort

Relevant Sort references

Virtual indices allow you to use Relevant sort, a sorting mechanism that favors relevancy over the attribute you’re sorting on.

βœ… Routing

Routing is configured inside instantSearch component. Please refer to the documentation for further implementation information.

Development Workflow and Contributing

Any new contribution is more than welcome in this project!

If you want to know more about the development workflow or want to contribute, please visit our contributing guidelines for detailed instructions!


MeiliSearch provides and maintains many SDKs and Integration tools like this one. We want to provide everyone with an amazing search experience for any kind of project. If you want to contribute, make suggestions, or just know what's going on right now, visit us in the integration-guides repository.

About

The search client to use MeiliSearch with InstantSearch.

License:MIT License


Languages

Language:TypeScript 59.3%Language:JavaScript 18.9%Language:CSS 9.2%Language:HTML 7.4%Language:Vue 4.3%Language:Shell 0.9%