NAV Navbar
javascript

What is Searchkit?

const searchkit = new SearchkitManager("http://demo.searchkit.co/api/movies/")

const App = ()=> (
  <SearchkitProvider searchkit={searchkit}>
    <Layout>
      <TopBar>
        <SearchBox
          autofocus={true}
          searchOnChange={true}
          prefixQueryFields={["actors^1","type^2","languages","title^10"]}/>
      </TopBar>
      <LayoutBody>
        <SideBar>
          <HierarchicalMenuFilter
            fields={["type.raw", "genres.raw"]}
            title="Categories"
            id="categories"/>
          <RefinementListFilter
            id="actors"
            title="Actors"
            field="actors.raw"
            operator="AND"
            size={10}/>
        </SideBar>
        <LayoutResults>
          <ActionBar>

            <ActionBarRow>
              <HitsStats/>
            </ActionBarRow>

            <ActionBarRow>
              <SelectedFilters/>
              <ResetFilters/>
            </ActionBarRow>

          </ActionBar>
          <Hits mod="sk-hits-grid" hitsPerPage={10} itemComponent={MovieHitsGridItem}
            sourceFilter={["title", "poster", "imdbId"]}/>
          <NoHits/>
        </LayoutResults>
      </LayoutBody>
    </Layout>
  </SearchkitProvider>
)

ReactDOM.render(<App/>, document.getElementById('root'))

Searchkit is a suite of UI components built in react. The aim is rapidly create beautiful search applications using declarative components, and without being an ElasticSearch expert.

See Getting Started Live demo

Getting Started

This section will help you setup your project for searchkit, connect and index data with elasticsearch, use and extend searchkit components to your requirements.

This guide assumes you have experience with React and JSX or TSX. Below are links on these technologies.

Project Setup

Our recommended project setup is using webpack and typescript. We also support using searchkit with ES6 / Webpack and using normal library script file. Installing via NPM is recommended.

Using Module

We recommend using webpack for module dependency management of Searchkit's src, css and static assets. requires scss, file loaders to properly resolve searchkit dependencies. See searchkit boilerplate.

Installing via NPM

Searchkit is available on npm. Searchkit is written with typescript therefore typescript definition files are available.

npm install searchkit --save

Importing with webpack / ES6


import {
    SearchBox,
    RefinementListFilter,
    Hits,
    HitsStats,
    SearchkitComponent,
    SelectedFilters,
    MenuFilter,
    HierarchicalMenuFilter,
    Pagination,
    ResetFilters
} from "searchkit";

Using library script

Searchkit library script is available from bower or from jsdelivr CDN.

CDN Script include

    <link href="//cdn.jsdelivr.net/npm/searchkit@2.3.0-9/release/theme.css" rel="stylesheet"/>
    <script src="//cdn.jsdelivr.net/npm/react@16.0.0/umd/react.production.min.js"></script>
    <script src="//cdn.jsdelivr.net/npm/react-dom@16.0.0/umd/react-dom.production.min.js"></script>
    <script src="//cdn.jsdelivr.net/npm/searchkit@2.3.0-9/release/bundle.js"></script>

Connecting to Elasticsearch instance

There are many ways to connect searchkit to an elasticsearch instance. We officially support ES 2.0+ but also works on 1.7.2 +. If connecting directly to elasticsearch (not using the searchkit-express), multipleSearchers cannot be true.

Using a cloud hosted elasticsearch instance

Searchkit can use a cloud based elasticsearch instance. If using in production, make sure the url is read only.

Hosting providers

Configuration

const sk = new SearchkitManager("https://kili-eu-west-1.searchly.com/movies/", {
  basicAuth:"read:teetndhjnrspbzxxyfxmf5fb24suqxuj"
})

<SearchkitProvider searchkit={sk}>
...
</SearchkitProvider>

Using Local ES Server

Connecting Searchkit to your local elasticsearch instance. If you are are getting a cors related error, you will need to add the following to you `config/elasticsearch.yml' file.

http.cors.enabled : true
http.cors.allow-origin : "*" http.cors.allow-methods : OPTIONS, HEAD, GET, POST, PUT, DELETE http.cors.allow-headers : X-Requested-With,X-Auth-Token,Content-Type, Content-Length

Configuration

const searchkit = new SearchkitManager("http://localhost:9200/")


<SearchkitProvider searchkit={searchkit}>
...
</SearchkitProvider>

Proxy elasticsearch connection

We built a plugin for node express called searchkit-express. This proxies the search request to elasticsearch via the server. This allows to validate requests on the server and with options to apply additional filters before it reaches the elasticsearch instance. See searchkit-express setup for more information.

Configuration

const searchkit = new SearchkitManager("/")

<SearchkitProvider searchkit={searchkit}>
...
</SearchkitProvider>

Elasticsearch Indexing

The components require elasticsearch fields indexed in a particular way. For example the Hierarchical Menu needs data to be indexed in levels. See Indexing & Mapping Guide or the component's pages for more information.

Using Components

Once you have the project setup, elasticsearch correctly indexed with data, we can start by adding components to the page to interact with the elasticsearch instance. For this series of examples we are using typescript with webpack.

Adding basic components

To use searchkit, we need to instantiate a SearchkitManager with a elastic like host url as shown in connecting elasticsearch.

We then add Searchbox and hits components to the page. This should render the standard searchbox and hits to the page.

import React from "react";
import ReactDOM from "react-dom";

import {
  SearchkitManager, SearchkitProvider, SearchBox, Hits
} from "searchkit";

const searchkit = new SearchkitManager("/");

ReactDOM.render((
    <SearchkitProvider searchkit={searchkit}>
        <div>
            <SearchBox/>
            <Hits/>
        </div>
    </SearchkitProvider>
),  document.getElementById('root'))

Adding a filter based component

Each filter will require a unique id. This is used for url serialisation.

import React from "react";
import ReactDOM from "react-dom";

import {
  SearchkitManager, SearchkitProvider, SearchBox, Hits, RefinementListFilter
} from "searchkit";

const searchkit = new SearchkitManager("/");

ReactDOM.render((
    <SearchkitProvider searchkit={searchkit}>
    <div>
      <SearchBox/>
      <RefinementListFilter id="actors" field="actors.raw"/>
      <Hits/>
    </div>
    </SearchkitProvider>
),  document.getElementById('root'))

Wrapping search in React component

You may want to incapsulate the searchApp within a React component. You can be using the SearchkitComponent class.

import React from "react";
import ReactDOM from "react-dom";

import {
  SearchkitManager, SearchkitProvider, SearchkitComponent
} from "searchkit";

const searchkit = new SearchkitManager("/");

class SearchApp extends SearchkitComponent {
  render() {
    return (
      <div>
        <SearchBox/>
        <Hits/>
      </div>
    )
  }
}

ReactDOM.render((
    <SearchkitProvider searchkit={searchkit}>
        <div>
            <SearchApp/>
        </div>
    </SearchkitProvider>
),  document.getElementById('root'))

Default Queries

Sometimes we need to apply a default query which affects the entire search and is not serialized to the browser url.

SearchkitManager allows ability to add these

  import {
    SearchkitManager,
    TermQuery,
    BoolMust
  } from "searchkit";
  const searchkit = new SearchkitManager("/");
  searchkit.addDefaultQuery((query)=> {
    return query.addQuery(BoolMust([
        TermQuery("colour", "red"),
        TermQuery("colour", "orange")
      ])
    );
  })

ImmutableQuery + QueryBuilders

To see more detail on these apis have a look at these pages

Extending Components

Styling

Searchkit uses BEM concepts. All searchkit components allow you to change the BEM block via the mod attribute. When BEM block is overridden, no styles will be applied to the component and you can then apply your styles as required.

BEM Blocks

We use BEM-cn a BEM react library. Each component has one or more blocks (for example filters usually have 2 blocks, one for "container" and one for "option"). They will be accessible under this.bemBlocks. If you need an additional block or want to customize the block names, override the defineBEMBlocks method. Here is an example override

defineBEMBlocks() {
  var blockName = "refinement-list"
  return {
    container: blockName,
    option: `${blockName}-option`
  }
}

The container block would be accessible using this.bemBlocks.container.

this.bemBlocks.container("info").state({disabled:true}) -> "refinement-list__info is-disabled"

Overriding rendering

Some components have the feature to override the display component to allow you to control the markup and contents of the component using the itemComponent props. Example of overriding the default rendering of a hit from Hits component.


import {
  HitsStats,
  SearchkitComponent
} from "searchkit";

const HitItem = (props) => (
  <div className={props.bemBlocks.item().mix(props.bemBlocks.container("item"))}>
    <img className={props.bemBlocks.item("poster")} src={props.result._source.poster}/>
    <div className={props.bemBlocks.item("title")} dangerouslySetInnerHTML={{__html:_.get(props.result,"highlight.title",false) || props.result._source.title}}></div>
  </div>
)

class App extends SearchkitComponent {
  render(){
    <div>
      <SearchBox
        searchOnChange={true}
        queryOptions={{analyzer:"standard"}}
        queryFields={["title^5", "languages", "text"]}/>

      <Hits hitsPerPage={50} highlightFields={["title"]} sourceFilter={["title", "poster", "imdbId"]} itemComponent={HitItem}/>

    </div>
  }
}

The following components support this feature:

Components

To use searchkit, we need to instantiate a SearchkitManager with a elastic like host url. We then wrap a searchkit app and render to the page.

import React from "react";
import ReactDOM from "react-dom";

import {
  SearchkitManager, SearchkitProvider
} from "searchkit";

const searchkit = new SearchkitManager("http://localhost:9200/movies");

ReactDOM.render((
    <SearchkitProvider searchkit={searchkit}>
        <div>
            <SearchBox/>
            <Hits/>
        </div>
    </SearchkitProvider>
),  document.getElementById('root'))

The search box component is where your users type their search queries.

Example

Example


import {
  SearchBox,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {
  render(){
    <div>
      <SearchBox
        searchOnChange={true}
        queryOptions={{analyzer:"standard"}}
        queryFields={["title^5", "languages", "text"]}/>
    </div>
  }
}

Props

Translations

Hits

Hits component displays results from ElasticSearch. To customize each result, you need to implement a React component and pass into itemComponent prop. The component will receive a single hit object from the search results, which will include result._source which contains the untouched stored fields which were indexed.

Example

Example Usage


import { get } from "lodash";

import {
  Hits,
  SearchkitComponent,
  HitItemProps
} from "searchkit";

const HitItem = (props) => (
  <div className={props.bemBlocks.item().mix(props.bemBlocks.container("item"))}>
    <img className={props.bemBlocks.item("poster")} src={props.result._source.poster}/>
    <div className={props.bemBlocks.item("title")} dangerouslySetInnerHTML={{__html: get(props.result,"highlight.title",props.result._source.title)}}></div>
  </div>
)

class App extends SearchkitComponent {

  render(){
    <div>
      <Hits hitsPerPage={50} highlightFields={["title"]} sourceFilter={["title", "poster", "imdbId"]}
      mod="sk-hits-grid" itemComponent={HitItem}/>
    </div>
  }
}

Props

Searchkit Theming styles

Searchkit ships out the box with 2 styles of search results which are controlled via the mod property

Table List example using listComponent

Table example

class MovieHitsTable extends React.Component {

  render(){
    const { hits } = this.props
    return (
      <div style={{width: '100%', boxSizing: 'border-box', padding: 8}}>
        <table className="sk-table sk-table-striped" style={{width: '100%', boxSizing: 'border-box'}}>
          <thead>
            <tr>
              <th></th>
              <th>Title</th>
              <th>Year</th>
              <th>Rating</th>
            </tr>
          </thead>
          <tbody>
            {map(hits, hit => (
              <tr key={hit._id}>
                <td style={{margin: 0, padding: 0, width: 40}}>
                  <img data-qa="poster" src={hit._source.poster} style={{width: 40}}/>
                </td>
                <td>{hit._source.title}</td>
                <td>{hit._source.year}</td>
                <td>{hit._source.imdbRating}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    )
  }
}

class App extends SearchkitComponent {

  render(){
    <div>
      <Hits hitsPerPage={50} sourceFilter={["title", "poster", "imdbId", "imdbRating"]} listComponent={MovieHitsTable}/>
    </div>
  }
}

Demo

NoHits

NoHits component is displayed when the current query yields no results from Elasticsearch. NoHits may offer actions to help the user tweak their search to return results. NoHits will display an error when Elastic search responds with an error. You can override the display of NoHits by passing in a React Component in component or errorComponent props. See Overriding section for more details.

Example

Tweaking Query Actions

If the current query and filters applied returns no results, NoHits component will try to provide steps to adjust the search.

Elasticsearch Suggestions API

If suggestionsField prop is specified, it will use elasticsearch's suggestions api and show an action to search using the suggestion if found.

Query Without Filters

NoHits will provide an action to remove the filters if the query without filters will yield results.

Example Usage


import {
  Hits,
  NoHits,
  SearchkitProvider,
  SearchkitManager
} from "searchkit"

const searchkit = new SearchkitManager(<somehost>)

const App = ()=> (
  <SearchkitProvider searchkit={searchkit}>
    <div>
      <Hits hitsPerPage={10} sourceFilter={["title"]}/>
      <NoHits translations={{
        "NoHits.NoResultsFound":"No movies found were found for {query}",
        "NoHits.DidYouMean":"Search for {suggestion}",
        "NoHits.SearchWithoutFilters":"Search for {query} without filters"
      }} suggestionsField="title"/>
    </div>
  </SearchkitProvider>
)

Note here we use NoHits as a sibling to Hits

Overriding display for NoHits

If you need to override the display of No Hits, you can pass in a React component into either component or errorComponent to override the default display.


import {
  Hits,
  NoHits,
  SearchkitProvider,
  SearchkitManager
} from "searchkit"

const searchkit = new SearchkitManager(<somehost>)

const NoHitsDisplay = (props) => {
  const {bemBlocks, query, suggestion, noResultsLabel} = this.props
  return (
    <div data-qa="no-hits" className={bemBlocks.container()}>
      <div className={bemBlocks.container("info")}>
        no results for {query}!
      </div>
    </div>
  );
}

const NoHitsErrorDisplay = (props) => {
  const {errorLabel, bemBlocks, resetSearchFn, tryAgainLabel} = props

  return (
    <div data-qa="no-hits" className={bemBlocks.container()}>
      <div className={bemBlocks.container("info")}>
        An error has occurred!
      </div>
    </div>
  )
}

const App = ()=> (
  <SearchkitProvider searchkit={searchkit}>
    <div>
      <Hits hitsPerPage={10} sourceFilter={["title"]}/>
      <NoHits
      component={NoHitsDisplay}
      errorComponent={NoHitsErrorDisplay}
      translations={{
        "NoHits.NoResultsFound":"No movies found were found for {query}",
        "NoHits.DidYouMean":"Search for {suggestion}",
        "NoHits.SearchWithoutFilters":"Search for {query} without filters"
      }} suggestionsField="title"/>
    </div>
  </SearchkitProvider>
)

Props

Translations

Note NoHits will interpolate the query or suggestion into your translation

Initial loader

Initial loader is displayed when Searchkit hasn't yet received an elasticsearch response.

Example Usage


import {
  Hits,
  InitialLoader,
  HitItemProps
} from "searchkit";

const InitialLoaderComponent = (props) => (
  <div className={props.bemBlocks.item().mix(props.bemBlocks.container("item"))}>
    loading please wait...
  </div>
)

class App extends SearchkitComponent {

  render(){
    <div>
      <Hits hitsPerPage={50} highlightFields={["title"]} sourceFilter={["title"]}/>
      <InitialLoader component={InitialLoaderComponent}/>
    </div>
  }
}

Props

Pagination

The pagination component provides ability to go to next and previous page.

Example

Example


import {
  Pagination,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <Pagination showNumbers={true}/>
    </div>
  }
}

Props

PaginationSelect

If you would like a more compact rendering, you can use Searchkit's PaginationSelect component instead

<PaginationSelect/>

Translations

Refinement list

Lets the user refine the search results. You can specify if you want filters to be ORed or ANDed. For example, if you filter on a and b with OR, results with either the value a or b will match.

Example


import {
  RefinementListFilter,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

 render(){
    <div>
      <RefinementListFilter id="actors" title="Actors" field="actors.raw" operator="AND"/>
    </div>
  }
}

Overriding Filter Option Component

If you want to tweak the markup for a refinement option, you can use the itemComponent prop and pass in a new react component to be used to render each option.


import {
  Pagination,
  Hits,
  RefinementListFilter,
  SearchkitComponent
} from "searchkit";

const RefinementOption = (props) => (
  <div className={props.bemBlocks.option().state({selected:props.selected}).mix(props.bemBlocks.container("item"))} onClick={props.onClick}>
    <div className={props.bemBlocks.option("text")}>{props.label}</div>
    <div className={props.bemBlocks.option("count")}>{props.count}</div>
  </div>
)

class App extends SearchkitComponent {

  render(){
    <div>
      <RefinementListFilter
        field="languages.raw"
        title="Languages"
        id="languages" itemComponent={RefinementOption}/>
    </div>
  }
}

Props

Translations

Demo

Numeric Refinement List

Allows the user to refine results based on a numerical elasticsearch attribute. You specify an array of options for the user to select from. Will only allow the user to select one.

Example

Example


import {
  NumericRefinementListFilter,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <NumericRefinementListFilter id="metascore" title="Meta score" field="metaScore" options={[
        {title:"All"},
        {title:"up to 20", from:0, to:21},
        {title:"21 to 40", from:21, to:41},
        {title:"41 to 60", from:41, to:61},
        {title:"61 to 80", from:61, to:81},
        {title:"81 to 100", from:81, to:101}
      ]}/>
    </div>
  }
}

Props

Demo

Provides a way to navigate through results for a single attribute. Only one value can be selected at a time.

Example


import {
  Pagination,
  Hits,
  SearchkitComponent,
  MenuFilter
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <MenuFilter
        field="languages.raw"
        title="Languages"
        id="languages"/>
    </div>
  }
}

Overriding Filter Option Component

If you want to tweak the markup for a menu option, you can use the itemComponent prop and pass in a new react component to be used to render each option.


import {
  Pagination,
  Hits,
  SearchkitComponent,
  ItemList
} from "searchkit";

class App extends React.Component {

  render(){
    <div>
      <MenuFilter
        field="languages.raw"
        title="Languages"
        id="languages"
        listComponent={ItemList}/>
    </div>
  }
}

Props

List Component examples

Example

Translations

<MenuFilter
  field="languages.raw"
  title="Languages"
  id="languages"
  translations={{"All":"All languages"}}/>

Demo

Range Filter

The range filter lets users filter results within a numerical range. Provides a histogram so show the user where results are found on the range.

Example

Example


import {
  RangeFilter,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <RangeFilter field="metaScore" id="metascore" min={0} max={100} showHistogram={true} title="MetaScore"/>
    </div>
  }
}

Props

Translations

Dynamic Range Filter

The dynamic range filter lets users filter results within a numerical range. The min and max values are automatically computed by Elasticsearch using the data in the index.

Example

Example


import {
  DynamicRangeFilter,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <DynamicRangeFilter field="metaScore" id="metascore" title="MetaScore"/>
    </div>
  }
}

Props

Translations

Input Filter

A input box based filter which is similar to SearchBox, but can be used multiple times, general on the left side panel.

Example

Example

import {
  InputFilter,  
} from "searchkit";

<InputFilter
  id="author_q"
  title="Actors filter"
  placeholder="Search actors"
  searchOnChange={true}
  prefixQueryFields={["actors"]}
  queryFields={["actors"]}
/>

Props

Translations

Checkbox Filter

A checkbox filter component which can be used with either simple or more complicated filters to decide on/off behaviour.

Example

Example

import {
  CheckboxFilter,
  TermQuery,
  BoolMust,
  RangeQuery
} from "searchkit"

<CheckboxFilter id="rated-r" title="Rating" label="Rated R" filter={TermQuery("rated.raw", 'R')} />
<CheckboxFilter id="recent" title="Date" label="Recent" filter={RangeQuery("year", {gt: 2012})} />
<CheckboxFilter id="old-movies" title="Movile filter" label="Old movies" filter={
  BoolMust([
    RangeQuery("year", {lt: 1970}),
    TermQuery("type.raw", "Movie")
  ])} />

Filter property

Notice how we are able to use any arbitrary ES Query, Searchkit offers out the box Query Builders as seen above, but can be any elastic filter query. See QueryDSL for other query builders.

Props

TagFilter, TagFilterList, TagFilterConfig

A suite of components to provide tag filters within your search results.

Example

Example

Within a list item component we can use the TagFilterList and pass the values + field. Note that a RefinementListFilter or MenuFilter or TagFilterConfig is required with provided field so the filter can be added correctly. jsx <ul style={{ marginTop: 8, marginBottom: 8, listStyle: 'none', paddingLeft: 20 }}> <li>Rating: {rated}</li> <li>Writers: <TagFilterList field="writers.raw" values={writers} /></li> <li>Actors: <TagFilterList field="actors.raw" values={actors} /></li> </ul>

 List Item example

const MovieHitsListItem = (props)=> {
  const {bemBlocks, result} = props
  let url = "http://www.imdb.com/title/" + result._source.imdbId  
  const { title, poster, writers = [], actors = [], genres = [], plot, released, rated } = result._source;

  return (
    <div className={bemBlocks.item().mix(bemBlocks.container("item"))} data-qa="hit">
      <div className={bemBlocks.item("poster")}>
        <img data-qa="poster" src={result._source.poster}/>
      </div>
      <div className={bemBlocks.item("details")}>
        <a href={url} target="_blank"><h2 className={bemBlocks.item("title")}>{title}</h2></a>
        <h3 className={bemBlocks.item("subtitle")}>Released in {source.year}, rated {source.imdbRating}/10</h3>
        <ul style={{ marginTop: 8, marginBottom: 8, listStyle: 'none', paddingLeft: 20 }}>
          <li>Rating: {rated}</li>          
          <li>Writers: <TagFilterList field="writers.raw" values={writers} /></li>
          <li>Actors: <TagFilterList field="actors.raw" values={actors} /></li>
        </ul>        
      </div>
    </div>
  )
}

const App = ()=> {
  <TagFilterConfig id="genres" title="Genres" field="genres.raw" />
  <RefinementListFilter id="actors" title="Actors" field="actors.raw" size={10}/>
  ...
  <Hits hitsPerPage={12} itemComponent={MovieHitsListItem}/>
}

Note how we have a RefinementListFilter for actors.raw but needed to add a TagFilterConfig for genres.raw. The TagFilterConfig will not render anything, and will just provide configuration for that field.

TagFilterList

TagFilterList renders a list of tags with add/remove behaviour - filter (string): Required. The field id which should also map to the id of RefinementListFilter or MenuFilter or TagFilterConfig component within the page. - values (Array): The array of values per result, often read from the _source of each document

TagFilter

A Low level component to render each Tag, used if you want to full control the surrounding list. - filter (string): Required. The field id which should also map to the id of RefinementListFilter or MenuFilter or TagFilterConfig component within the page. - value (string): The value of the tag

TagFilterConfig

A non rendering config component which holds logic to add/remove filters. - field (string): Elastic search field to filter on - title (string): Title of the filter, used for selected filters rendering - id: (string): unique id used for url serialization - operator ('AND'|'OR'): If you filter on a and b with OR, results with either the value a or b will match. If you select a and b, results will show which have both a and b.

Selected Filters

This component shows all the filters that have been applied to the current search.

Example

Example


import {
  SelectedFilters,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <SelectedFilters/>
    </div>
  }
}

Overriding Selected Filter Component

If you want to tweak the markup for a selected filter item, you can use the itemComponent prop and pass in a React Component to be used instead to render each selected filter item.


import {
  SelectedFilters,
  SearchkitComponent
} from "searchkit";

const SelectedFilter = (props) => (
  <div className={props.bemBlocks.option()
    .mix(props.bemBlocks.container("item"))
    .mix(`selected-filter--${props.filterId}`)()}>
    <div className={props.bemBlocks.option("name")}>{props.labelKey}: {props.labelValue}</div>
    <div className={props.bemBlocks.option("remove-action")} onClick={props.removeFilter}>x</div>
  </div>
)

class App extends SearchkitComponent {

  render(){
    <div>
      <SelectedFilters itemComponent={SelectedFilter} />
    </div>
  }
}

Props

Grouped Selected Filters

An alternative component to SelectedFilters which groups the selected filters by Facet type and allows better use of screen real estate

Example

Example


import {
  GroupedSelectedFilters,
  Hits
} from "searchkit"

<div className="sk-layout__results sk-results-list">
  <div className="sk-results-list__action-bar sk-action-bar">    
    <div className="sk-action-bar__filters">
      <GroupedSelectedFilters/>
    </div>
  </div>
  <Hits hitsPerPage={10}/>
</div>

GroupedSelectedFilters props

Reset

This component clears all the selected filters and search query that are currently applied.

Example


import {
  ResetFilters,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <ResetFilters/>
    </div>
  }
}

Overriding Display Component

If you want to tweak the markup for reset filters, you can use the component prop and pass in a React Component to be used to render the reset button.


import {
  ResetFilters,
  SearchkitComponent
} from "searchkit";

class ResetFiltersDisplay extends React.Component {
    render(){
        const {bemBlock, hasFilters, translate, resetFilters} = this.props
        return (
            <div onClick={resetFilters} className={bemBlock().state({disabled:!hasFilters})}>
                <div className={bemBlock("reset")}>{translate("reset.clear_all")}</div>
            </div>
        )
    }
}

class App extends SearchkitComponent {

  render(){
    <div>
      <ResetFilters component={ResetFiltersDisplay} />
    </div>
  }
}

Props

Translations

Example overriding the translation


import {
  ResetFilters,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <ResetFilters translations={{"reset.clear_all":"Reset all filters"}}/>
    </div>
  }
}

Hierarchical Menu

Component which renders a tree like structure. Used for items which have multiple levels of categorization. Used for when documents have only one hierarchical category. For documents with more than one categories, see hierarchical Refinement filter.

Example

Indexing

Hierarchical Menu needs an array of level fields. Levels is derived from the order of the array. If an item was in /Appliances/Air Conditioners/Window Air Conditioners category, the setup would be as follows:

Mapping example

{
  "products":{
    "category":{
      "properties":{
       //includes all ids for flat level querying
       "all":{"type":"string", "index":"not_analyzed"},

       //tags bucketed by their level in the tree
       "lvl1":{"type":"string", "index":"not_analyzed"},
       "lvl2":{"type":"string", "index":"not_analyzed"},
       "lvl3":{"type":"string", "index":"not_analyzed"},
       //...
       "lvl10":{"type":"string", "index":"not_analyzed"}
    }
  }
}

Indexing example

{
  category.lvl1:"Appliances",
  category.lvl2:"Air Conditioners",
  category.lvl3:"Window Air Conditioners"
}

Example


import {
  HierarchicalMenuFilter,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

 render(){
    <div>
      <HierarchicalMenuFilter fields={["category.lvl1", "category.lvl2", "category.lvl3"]} title="Categories" id="categories"/>
    </div>
  }
}

Props

Demo

Hierarchical Refinement

Component which renders a tree like structure. Used for items which have multiple levels of categorization. For example product categories & folders. Difference between Hierarchical menu and Hierarchical refinement is refinement is able to handle more than one hierarchical category per document, menu can only have one.

Indexing

If a document has the following paths for the field taxonomy: /Color/Red/Firebrick /Color/Red/Orange Red /Color/Green/Sea Green /Color/Green/Lime Green

Mapping definition

Below is the definition needed for a hierarchical field, we are making hierarchical colors in this case. js color:{ type:"nested", properties:{ level:{type:"integer"}, ancestors:{type:"string", index:"not_analyzed"}, value:{type:"string", index:"not_analyzed"}, order:{type:"integer"} } }

Indexing the data

The document field for the hierarchical color field would be:


color:[
  {level:1, value:"Color", ancestors:[]},
  {level:2, value:"Red", ancestors:["Color"]},
  {level:3, value:"Firebrick", ancestors:["Color", "Red"]},
  {level:3, value:"Orange Red", ancestors:["Color", "Red"]},
  {level:2, value:"Green", ancestors:["Color"]},
  {level:3, value:"Sea Green", ancestors:["Color", "Green"]},
  {level:3, value:"Lime Green", ancestors:["Color", "Green"]}
]

The array must have unique values, no duplicates (Color cannot be repeated for example). Order can be passed in as an integer value. Order value is optional.

Example


import {
  HierarchicalRefinementFilter,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

 render(){
    <div>
      <HierarchicalRefinementFilter field="color" title="Colors" id="colors"/>
    </div>
  }
}

Props

Demo

View Switcher

Searchkit's view switcher components come in 2 seperate components. ViewSwitcherHits which toggles the hits display and ViewSwitcherToggle which is the actual toggle button to switch views.

Example Example Example

Example

import {
  Hits,
  ViewSwitcherHits,
  ViewSwitcherToggle  
} from "searchkit";

const MovieHitsGridItem = (props) => {
  const {result, bemBlocks} = props
  <div className={bemBlocks.item().mix(bemBlocks.container("item"))}>
    <img className={bemBlocks.item("poster")} src={result._source.poster}/>
    <div className={bemBlocks.item("title")}>{result._source.title}</div>
  </div>
}
const MovieHitsListItem = (props)=> {
  const {bemBlocks, result} = props  
  const source = result._source
  return (
    <div className={bemBlocks.item().mix(bemBlocks.container("item"))} data-qa="hit">
      <div className={bemBlocks.item("poster")}>
        <img data-qa="poster" src={result._source.poster}/>
      </div>
      <div className={bemBlocks.item("details")}>
        <h2 className={bemBlocks.item("title")}>{source.title}</h2>
        <h3 className={bemBlocks.item("subtitle")}>Released in {source.year}, rated {source.imdbRating}/10</h3>        
      </div>
    </div>
  )

const MovieHitsTable = (props)=> {  
  const { hits } = this.props
  return (
    <div style={{width: '100%', boxSizing: 'border-box', padding: 8}}>
      <table className="sk-table sk-table-striped" style={{width: '100%', boxSizing: 'border-box'}}>
        <thead>
          <tr>
            <th></th> <th>Title</th> <th>Year</th> <th>Rating</th>
          </tr>
        </thead>
        <tbody>
        {map(hits, hit => (
          <tr key={hit._id}>
            <td style={{margin: 0, padding: 0, width: 40}}>
              <img data-qa="poster" src={hit._source.poster} style={{width: 40}}/>
            </td>
            <td>{hit._source.title}</td>
            <td>{hit._source.year}</td>
            <td>{hit._source.imdbRating}</td>
          </tr>
          ))}
          </tbody>
      </table>
    </div>
    )  
}


class App extends React.Component {

  render(){
    <div>
      <ViewSwitcherToggle/>
      <ViewSwitcherHits
          hitsPerPage={12}
          sourceFilter={["plot", "title", "poster", "imdbId", "imdbRating", "year"]}
          hitComponents = {[
            {key:"grid", title:"Grid", itemComponent:MovieHitsGridItem, defaultOption:true},
            {key:"list", title:"List", itemComponent:MovieHitsListItem},
            {key:"table", title:"Table", listComponent:MovieHitsTable}
          ]}
          scrollTo="body"
      />
    </div>
  }
}

Configuration

ViewSwitcherHits accepts all the props that Hits expects with 1 additional hitComponents prop. The hitComponents prop is an array of views, with key, title, defaultOption and itemComponent or listComponent.

hitComponents = {[
  {key:"grid", title:"Grid", itemComponent:MovieHitsGridItem, defaultOption:true},
  {key:"list", title:"List", itemComponent:MovieHitsListItem},
  {key:"table", title:"Table", listComponent:MovieHitsTable}
]}

ViewSwitcherHits Props

ViewSwitcherToggle Props

Page Size Selector

A component which allows to change the results per page of the search results.

Example

Select and Toggle examples side by side

Example

import {
  Hits,
  PageSizeSelector,
  Select, Toggle  
} from "searchkit";


const App = ()=> (
  <div className="sk-layout__results sk-results-list">
    <div className="sk-results-list__action-bar sk-action-bar">
      <div className="sk-action-bar__info">
        <PageSizeSelector options={[4,12,24]} listComponent={Toggle}/>
      </div>
      <Hits hitsPerPage={12}/>
    </div>
  </div>
)

PageSizeSelector Props

Sorting Selector

This component lets you reorder your results. Each option requires a sortable Elasticsearch field and the order of which you want to sort by.

Example

Select and Toggle example side by side.

Example

import {
  SortingSelector,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
      <SortingSelector options={[
        {label:"Relevance", field:"_score", order:"desc", defaultOption:true},
        {label:"Latest Releases", field:"released", order:"desc"},
        {label:"Earliest Releases", field:"released", order:"asc", key:"earliest"},
        {label:"Highly Rated", key:"rating", fields: [
          {field:"rating", options: {order:"desc"}},
          {field:"prices", options: {order:"asc", "mode" : "avg"}}
        ]}
      ]}/>
    </div>
  }
}

Advanced Sorting

If you need to specify multiple sorting fields or have special options for sort field like mode, you can use the more advanced fields key (shown in example for "Highly Rated" option). This accepts an array of sort fields and allows sorting properties to be sent in the options object. Requires the key option to be specified.

Props

Default selected

The component will look for the defaultOption field on an option and use that as the default selected. If no defaultOption is provided, the component will choose the first option as the default.

Stats

This component lets you display how many results matched the query and other metrics on the results such as how fast the search was.

Example

import {
  HitsStats,
  SearchkitComponent
} from "searchkit";

class App extends SearchkitComponent {

  render(){
    <div>
        <HitsStats/>
    </div>
  }
}

Overriding

If you want to customise the display of HitsStats, you can override the display by passing in a ReactComponent into component prop.

import {
  HitsStats,
  SearchkitComponent
} from "searchkit";

const customHitStats = (props) => {
    const {resultsFoundLabel, bemBlocks, hitsCount, timeTaken} = props
    return (
        <div className={bemBlocks.container()} data-qa="hits-stats">
            <div className={bemBlocks.container("info")} data-qa="info">
                I found {hitsCount} in {timeTaken}ms!
            </div>
      </div>
    )
}

class App extends SearchkitComponent {

  render(){
    <div>
        <HitsStats component={customHitStats}/>
    </div>
  }
}

Props

Demo

 Props

Translations

Layout Components

Default set of layout components provided by Searchkit which work with Searchkit's default theme.

Example

Example

<SearchkitProvider searchkit={searchkit}>
  <Layout>
    <TopBar>
      <SearchBox
        autofocus={true}
        searchOnChange={true}
        prefixQueryFields={["actors^1","type^2","languages","title^10"]}/>
    </TopBar>
    <LayoutBody>
      <SideBar>
        <HierarchicalMenuFilter
          fields={["type.raw", "genres.raw"]}
          title="Categories"
          id="categories"/>
        <RefinementListFilter
          id="actors"
          title="Actors"
          field="actors.raw"
          operator="AND"
          size={10}/>
      </SideBar>
      <LayoutResults>
        <ActionBar>

          <ActionBarRow>
            <HitsStats/>
          </ActionBarRow>

          <ActionBarRow>
            <SelectedFilters/>
            <ResetFilters/>
          </ActionBarRow>

        </ActionBar>
        <Hits mod="sk-hits-grid" hitsPerPage={10} itemComponent={MovieHitsGridItem}
          sourceFilter={["title", "poster", "imdbId"]}/>
        <NoHits/>
      </LayoutResults>
    </LayoutBody>
  </Layout>
</SearchkitProvider>

Panel

This is a titled Panel component which can be collapsed, it is the default containerComponent used by our filter components

Example

import  {Panel} from "searchkit"

const PanelExample ()=> (
  <Panel title="My Panel" collapsable={true} defaultCollapsed={false}>
    <p>my content...</p>
  </Panel>
)

Props

Making a filter component collapsable

Example

  <MenuFilter field="type.raw" size={10}
    title="Movie Type" id="types"
    containerComponent={<Panel collapsable={true} defaultCollapsed={false}/>}/>
  />

Using an action bar component in a panel

Example

<Panel title="Selected filters" collapsable={true} defaultCollapsed={false}>
  <GroupedSelectedFilters/>
</Panel>

List Components

A Suite of UI Components which render lists in various ways and can be passed to many Searchkit components to change their list appearance.

Example

Many of SearchKit's components which render lists will support a listComponent prop where these components an be referenced

ItemList

Used to render list of facets or items

Example

ItemCheckboxList

Used to render list of facets or items when multiselecting

Example

ItemHistogramList

Used to render list of facets or items with a histogram bar showing the count. Requires a doc_count for each item, so won't work for Pagination, SortingSelector, and other non-filter components.

Example

TagCloud

Used to render list of facets or items where the count influences the size of text. Requires a doc_count for each item, so won't work for Pagination, SortingSelector, and other non-filter components.

Example

Tabs

Used to render tabs, often used for menu or view switching

Example

Select

Used to render a selectable list of items

Example

Toggle

Renders a toggle with single/multiple select behaviour

Example

Compatible parent components

RefinementListFilter

ItemList, ItemCheckboxList, ItemHistogramList, TagCloud, Tabs, Select, Toggle

ItemList, ItemCheckboxList, ItemHistogramList, TagCloud, Tabs, Select, Toggle

NumericRefinementListFilter

ItemList, ItemCheckboxList, ItemHistogramList, TagCloud, Tabs, Select, Toggle

ViewSwitcherToggle

ItemList, ItemCheckboxList, Tabs, Select, Toggle

PageSizeSelector

ItemList, ItemCheckboxList, Tabs, Select, Toggle

SortingSelector

ItemList, ItemCheckboxList, Tabs, Select, Toggle

Pagination

ItemList, ItemCheckboxList, Tabs, Select, Toggle

Range Components

A Suite of UI Components which render ranges in various ways and can be passed to some Searchkit components to change their range appearance.

Example

Currently works for RangeFilter using the rangeComponent prop

RangeSliderHistogram

RangeFilter's default style

Example

RangeSliderHistogramInput

Full-featured range component with a slider, histogram, and input. All other current range components are slimmed-down versions of this one.

Example

RangeInput

Range component containing only min/max input fields. Requires pressing "Go" to update the filter.

Example

RangeSlider

Simple slider, using rc-slider. Updates the filter when the handle is released.

Example

RangeHistogram

Renders a histogram with no filtering possibilities.

Example

RangeSliderInput

Example

RangeHistogramInput

Example

Compatible parent components

RangeFilter

Works with all range components

Theming

Searchkit provides out of the box css and theme to make it easier for developers to use Searchkit and also offers flexibility in implementing your own styles to customising the components display via display components.

Using Searchkit's Theme

Example

We provide an out of the box theme to get developers started quickly.

Getting Started

To use Searchkit's theme, add the theme.css file onto your site. You can use either the CDN link or the theme.css file in searchkit's release folder. If using the CDN link, please change the version number which matches your Searchkit version.


<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/searchkit/2.0.0/theme.css">

Layout Areas


// layout components
import {
  Layout, TopBar, LayoutBody, LayoutResults,
  ActionBar, ActionBarRow, SideBar
} from "searchkit"

<Layout>

  <TopBar>
      // red area
      <SearchBox .../>
  </TopBar>

  <LayoutBody>

    <SideBar>
      // yellow area
      <HierarchicalMenuFilter .../>
      <RefinementListFilter .../>
    </SideBar>

    <LayoutResults>

      <ActionBar>
        <ActionBarRow>
          // blue area
          <HitsStats .../>
          <ViewSwitcherToggle/>
          <SortingSelector .../>
        </ActionBarRow>

        <ActionBarRow>
          // aqua area
          <SelectedFilters/>
          <ResetFilters/>
        </ActionBarRow>

      </ActionBar>

      // green area
      <Hits ../>
      <Pagination .../>

    </LayoutResults>

  </LayoutBody>

</Layout>

When you include searchkit's theme.css, we provide you a basic layout.

Layout Max width

You can control the max-width of the layout by adding size prop to the layout component.

If no size class is applied, layout will use the full width of the browser.

Grid / List Hit Items

We provide out of the box styles for Hit items for Grid and List Views. See ViewSwitcherHits component.

Example

Example


const MovieHitsGridItem = (props)=> {
  const {bemBlocks, result} = props
  let url = "http://www.imdb.com/title/" + result._source.imdbId
  const source:any = _.extend({}, result._source, result.highlight)
  return (
    <div className={bemBlocks.item().mix(bemBlocks.container("item"))} data-qa="hit">
      <a href={url} target="_blank">
        // red area
        <img data-qa="poster" className={bemBlocks.item("poster")} src={source.poster} width="170" height="240"/>
        // green area
        <div data-qa="title" className={bemBlocks.item("title")} dangerouslySetInnerHTML={{__html:source.title}}>
        </div>
      </a>
    </div>
  )
}

Example


const MovieHitsListItem = (props)=> {
  const {bemBlocks, result} = props
  let url = "http://www.imdb.com/title/" + result._source.imdbId
  const source:any = _.extend({}, result._source, result.highlight)
  return (
    <div className={bemBlocks.item().mix(bemBlocks.container("item"))} data-qa="hit">
      <div className={bemBlocks.item("poster")}>
        // green area
        <img data-qa="poster" src={result._source.poster}/>
      </div>
      <div className={bemBlocks.item("details")}>
        // aqua area
        <a href={url} target="_blank"><h2 className={bemBlocks.item("title")} dangerouslySetInnerHTML={{__html:source.title}}></h2></a>
        // red area
        <h3 className={bemBlocks.item("subtitle")}>Released in {source.year}, rated {source.imdbRating}/10</h3>
        // blue area
        <ul className={bemBlocks.item("tags")}>
          <li>Genres: <TagFilterList field="genres.raw" values={genres} /></li>
          <li>Writers: <TagFilterList field="writers.raw" values={writers} /></li>
          <li>Actors: <TagFilterList field="actors.raw" values={actors} /></li>
        </ul>
        // yellow area
        <div className={bemBlocks.item("text")} dangerouslySetInnerHTML={{__html:source.plot}}></div>
      </div>
    </div>
  )
}

and to use these display components, use the ViewSwitcherHits component to switch between views.


<ViewSwitcherHits
    hitsPerPage={12} highlightFields={["title","plot"]}
    sourceFilter={["plot", "title", "poster", "imdbId", "imdbRating", "year"]}
    hitComponents = {[
      {key:"grid", title:"Grid", itemComponent:MovieHitsGridItem, defaultOption:true},
      {key:"list", title:"List", itemComponent:MovieHitsListItem}
    ]}
    scrollTo="body"
/>

Using one view type

If you only want to use the grid view or list view, you need to specify on the hits component, the mod prop to be either sk-hits-list for list components or sk-hits-grid for grid components.

  <Hits hitsPerPage={10} mod="sk-hits-list" itemComponent={ExampleHitsItem}/>

Extending Theme

You want to use the layout but want to adjust the colors, fonts to suit your app. Searchkit's Theming uses a set of default SCSS variables.

You can override the defaults by importing theme.scss into your own project and overriding the variables.

Example in webpack sass loader


$sk-action-text-color: red;

@import "~searchkit/theming/theme.scss";

Using Your Own Styles

If you want to take control of the component's styling to suit your app, you have a number of options.

Component's mod container

Most components support a mod prop which allows you to change the component's BEM block element. Useful for when you want one particular component to be styled different. See component API docs.

Overriding markup

Most components support overriding display components via the component, listComponent or itemComponent props. See component API docs.

Core

Searchkit Architecture

ElasticSearch's querying is very sophisticated and modelling relationship between query, components, results and url required a certain core architecture.

Searchkit's core classes

graph LR
  SearchkitManager-->|has many|Accessor
  SearchkitComponent-->|has many|Accessor

SearchkitManager

SearchkitManager is the top level class which manages a Searchkit application

Example Usage

import {
  SearchkitManager
} from "searchkit"

const host = "/"
// const host = "http//localhost:9200/movies"

const searchkit = new SearchkitManager(host, {
  httpHeaders:{},
  basicAuth:"key:val"
})

Options

Default Queries

Sometimes we need to apply a default query which affects the entire search and is not serialized to the browser url.

SearchkitManager allows ability to add these

  import {
    SearchkitManager,
    TermQuery,
    FilteredQuery,
    BoolShould
  } from "searchkit"

  const searchkit = new SearchkitManager("/")
  searchkit.addDefaultQuery((query)=> {
    return query.addQuery(FilteredQuery({
      filter:BoolShould([
        TermQuery("colour", "red"),
        TermQuery("colour", "orange")
      ])
    }))
  })

reloadSearch

If you want to reload the search even with query and state hasn't changed typescript searchkit.reloadSearch()

Query Processor

Searchkit offers ability to mutate the query just before its sent to ElasticSearch, this is so we can always support new apis for ElasticSearch and any custom logic you wish to add that is low level in searchkit normally.

searchkit.setQueryProcessor((plainQueryObject)=>{
  plainQueryObject.source = false
  return plainQueryObject
})

Note We only support 1 queryProcessor function currently, so multiple setQueryProcessor calls will override each other.

resultsListener

If you want to be notified when results changed ```typescript let removalFn = searchkit.addResultsListener((results)=>{ //do something with results })

//removalFn() if you want to stop listening ```

shouldPerformSearch

If you want to control whether Searchkit performs a search, you can provide a custom check function The shouldPerformSearch function is called with an instance of ImmutableQuery typescript //only search when there is a query string this.searchkit.shouldPerformSearch = (query)=> { return !!query.getQueryString() }

Accessors

Accessors are used to manage state and produce a fragment of an ElasticSearch query. This seperates all the query + state logic away from Searchkit Components. Accessors also hold a key which is used to serialize/deserialize to the browser url. Searchkit manages this complexity in the background for you.

Example usage

let accessor = new PaginationAccessor("p")
accessor.state = accessor.state.setValue(5)
//...when searching
// url serializes to ?p=5
// from field gets added to Elastic Query

List of some of Searchkit's existing accessors

ImmutableQuery

An immutable query builder which is passed around particular Accessors before being sent to ElasticSearch. This is used internally by Searchkit components

Example usage

import {
  ImmutableQuery,
  SimpleQueryString
} from "searchkit"

let query = new ImmutableQuery()
let newQuery = query.setSize(10).addQuery(
  SimpleQueryString("search term")
)

//query + newQuery are seperate

API

FieldOptions

Searchkit allows customizing certain components for use with nested or children fields

Supported components

Nested Example

  <RefinementListFilter
    id="embeddedTags"
    title= "Embedded Tags"
    field='tags.name'
    fieldOptions={{type:'nested', options:{path:'tags'}}}/>    

fieldOptions.options (type='nested')

Children Example

<RefinementListFilter
  id="childrenTags"
  title= "Children Tags"
  field='name'
  fieldOptions={{type:'children', options:{childType:'tags'}}}/>  

fieldOptions.options (type='children')

Query DSL builders

Searchkit ships with many query builders to be used internally and externally to searchkit. This is to remove boilerplate and errors when constructing complicated elastic queries.

Term based queries

TermQuery(field, value)

import {TermQuery} from "searchkit"
TermQuery("color", "red")

TermsQuery(field, values)

import {TermsQuery} from "searchkit"
TermsQuery("color", ["red", "orange"])

ExistsQuery(field)

import {ExistsQuery} from "searchkit"
ExistsQuery("color")

RangeQuery(field, options)

Available options: lt, lte, gt, gte, boost, format, time_zone

Numeric Range:

import {RangeQuery} from "searchkit"
RangeQuery("rating",  {gte: 1, lt: 11 })
/* {
  range:{
    rating: { gte:1, lt:11 }
  }
} */

Date Range:

import {RangeQuery} from "searchkit"
RangeQuery("date_received", { gte: "2005||/y" })
/* {
  range: {
    date_received: { gte: "2005||/y" }
  }
} */

Compound queries

BoolMust(filter)

import {BoolMust, TermQuery} from "searchkit"
//array
BoolMust([
  Term("color", "orange"),
  Term("brand", "boss")
])
//single filter
BoolMust(Term("color", "red"))

BoolMustNot(filter)

See BoolMust

BoolShould(filter)

See BoolMust

FilteredQuery(filteredOb)

import {FilteredQuery, MatchQuery, TermQuery} from "searchkit"
FilteredQuery({
  query:MatchQuery("title", "Star Wars"),
  filter:TermQuery("genre", "Action")
})

Full text based queries

MatchQuery(field, query, options)

import {MatchQuery} from "searchkit"
MatchQuery("color", "red yellow", {
    operator:'AND'
    //...other match query options
})

MatchPhrasePrefix(query, str)

import {MatchPhrasePrefix} from "searchkit"
MatchPhrasePrefix('title', 'spide')
//with optional boost
MatchPhrasePrefix('title^10', 'spide')

MultiMatchQuery(query, options)

import {MultiMatchQuery} from "searchkit"
MultiMatchQuery("red", {
    fields:["color", "song_title"],
    operator:"OR"
  //.. other multi match query options
})

SimpleQueryString(query, options)

import {SimpleQueryString} from "searchkit"
SimpleQueryString("red AND blue", {
    fields:["color", "song_title"],
    operator:"OR"
  //.. other simple query options
})

SearchkitProvider

A react component to provide a SearchkitManager instance to all descendant react components

Usage

import {
  SearchkitManager,
  SearchkitProvider
} from "searchkit"

const searchkit = new SearchkitManager("/")
const App = ()=> (
  <SearchkitProvider searchkit={searchkit}>
    //... app with searchkit components
  </SearchkitProvider>
)

ReactDOM.render(<App/>, document.getElementById("app"))

Note SearchkitProvider will add the SearchkitManager to the context and is automatically read by components extending SearchkitComponent

SearchkitComponent

A react component baseclass which offers convenience methods and references to instances of SearchkitManager and Searcher

  import {
    SearchkitComponent
  } from "searchkit"

  class MySearchkitComponent extends SearchkitComponent {

    //optional overridable methods

    // uses bem-cn library
    // this.bemBlocks.container() -> "container"
    // this.bemBlocks.option().mix(this.bemBlocks.container("item")) -> "option container__item"
    defineBEMBlocks(){
      return {
        "container":"container",
        "option":"item"
      }
    }

    defineAccessor(){
      //return a configured Accessor
    }

  }

Translate

All components run a translate function on any text displayed. This includes:

and many more in components.

Interpolations

Some translations contain interpolations, E.g. NoHits.NoResultsFound which has a default translation of No results found for {query}. which means the component will provide the query value.

How to override global defaults


const searchkit = new SearchkitManager("locahost:9200")

searchkit.translateFunction = (key) => {
  let translations = {
    "pagination.previous":"Previous page",
    "pagination.next":"Next page",
    "id1":"Color",
    "id2": "Red"
  }
  return translations[key]
}

Override component defaults

You can pass any component an object of translations you wish to override. Example below:

<SearchBox translations={{"searchbox.placeholder":"search movies"}} autofocus={true} searchOnChange={true} queryFields={["actors^1","type^2","languages","title^5", "genres^2"]}/>

List of some of the translation overrides with their defaults

ResetFilters

RefinementListFilter

NoHits

HitStats

Pagination

Migrating

Migrating from Searchkit 0.2.0

Removed config + classes

multipleSearchers removed

import {SearchkitManager} from "searchkit"
new SearchkitManager("/", {multipleSearchers:false}
//becomes
new SearchkitManager("/")

Searcher + SearcherProvider removed

import {
  Searcher,
  SearcherProvider,
  SearchkitManager,
  SearchkitProvider
} from "searchkit"

//becomes
import {
  SearchkitManager,
  SearchkitProvider
} from "searchkit"

Hits component no longer renders blank state

We have moved the no results found text + ui into a new NoHits Component ```jsx //0.2

//0.3.x ```

Migrating from Searchkit 0.3.x

Searchkit now uses lodash 4.0

The api is identical, but if you get terminal errors during development you might need to do a clean npm install.

RangeQuery api changed

  import {RangeQuery} from "searchkit"

  //before
  RangeQuery("price", 10, 21)

  //after
  RangeQuery("price", {
    gte:10, lt:21
  })

The new RangeQuery api now allows the following options typescript export interface RangeQueryOptions { lt?:number lte?:number gt?:number gte?:number boost?:number format?:string time_zone?:string }

Migrating from Searchkit 0.4.x

Overriding Display Components

Rather than extending a component class and overriding particular methods responsible for rendering, you now use the itemComponent or component prop and pass in a React component to do the rendering. The following components now support this feature: - Hits - InitialLoader - Menu - Refinement List - Reset - Selected Filters

Not all components support this feature yet but will expand in future releases. Let us know via github issues if there is one in particular you need.

Hits component no longer renders initial state

We have moved the initial loading view into a new Initial loader Component

Migrating from Searchkit 0.5.x

Hits sourceFilter property

Hits now supports the sourceFilter prop, we strongly advise you to use this as it will speed up your search and reduce a lot of wasted bandwidth. jsx <Hits hitsPerPage={50} sourceFilter={["title", "poster", "imdbId"]} itemComponent={HitItem}/>

HitStats component overrides

If you previously extended HitStats in order to customize the component, please switch to using the component prop. More details can be found in the HitStats documentation

Migrating from Searchkit 0.6.x

Styles & Theming

In 0.7, we revisited the way developers use our default styles. We now ship a supported layout and grid / list views. See theming section for more information

sk- prefix Classnames

In 0.7, we have prefixed all searchkit's classnames with sk- to prevent potential collisions with your own styles.

No more styles.css

We ship with only one css file, theme.css. This includes all component styles and basic searchkit layout.

Retain grid styling

Hits component requires mod="sk-hits-grid" to retain grid styling

Examples + Docs

Migrating from Searchkit 0.7.x

Styles & Theming

Some of Searchkit's internal classNames have changed, if you use the default Searchkit theme then no changes should be required, however custom css targeting certain elements may need to reviewed.

List Components

Many of Searchkit's components now support the listComponent property, so if you have extended components previously it might be worth checking if there is a simply way now using our out the box List Components or making your own.

RefinementListFilter, MenuFilter itemComponent changes

If you have implemented a custom itemComponent, the toggleFilter action function has been renamed to onClick, be sure to rename so that clicking facets works as expected.

Hits List Component

If you have needed to extend Hits internals to do views such as Table views, checkout the Hits docs as we now provide a simpler way to do these customizations.

Migrating from Searchkit 0.8.x

Breaking changes

Action bar classes renamed

sk-action-bar__info + sk-action-bar__filter renamed to sk-action-bar-row. We strongly recommend using Searchkit's new layout components, see more below.

Notable Changes

Layout components

Brand new layout components, rather than using div's with specific classNames, we now provide out the box layout components which will work towards responsive layout in future versions. We strongly encourage you all to use these new layout components.

Migrating from Searchkit 0.9.x

upgrade to history 2.x.x

We have upgraded internally our history library, which works with latest react-router. There shouldn't be any breaking changes, but keep a lookout in this area.

Serverside Guide

Using Express

Searchkit ships with an express plugin to proxy api calls to ElasticSearch

Note The use of a proxy is optional, you can configure the Searchkit client to connect directly to an ElasticSearch server

Installation

npm install searchkit-express --save
SearchkitExpress = require("searchkit-express")
var app = express()
//...

SearchkitExpress({
  host:process.env.ELASTIC_URL || "http://localhost:9200",  
  index:'movies',
  queryProcessor:function(query, req, res){
    //do neccessery permissions, prefilters to query object
    //then return it
    return query
  }
 }, app)

This will add the following endpoints to your root url which will route to the movies index on your elasticsearch instance * POST /_search

Custom router

If you wish to prefix the url or control the middleware for these particular routes SearchkitExpress allows manual creation of an express.Router

var app = express()

//...

var searchkitRouter = SearchkitExpress.createRouter({
  host:process.env.ELASTIC_URL || "http://localhost:9200",  
  index:'movies',
  queryProcessor:function(query, req, res){
    console.log(query)    
    return query
  }
 })
app.use("/movie-search", searchkitRouter)

This will result in the following api endpoints

Indexing & Mapping Guide

Elasticsearch querying is very sophisticated, and many of the features rely on an appropriate mapping configured.

Updating index

Straightforward to update the index. See Updating indices.

Analyzer extensions

Out the box, elasticsearch uses the default standard analyzer. We extend the default analyzer to offer word delimiter, html strip and char mapping.

{
  "analysis": {
    "char_filter": {
       "replace": {
        "type": "mapping",
        "mappings": [
          "&=> and "
        ]
      }
    },
    "filter": {
      "word_delimiter" : {
        "type" : "word_delimiter",
        "split_on_numerics" : false,
        "split_on_case_change" : true,
        "generate_word_parts" : true,
        "generate_number_parts" : true,
        "catenate_all" : true,
        "preserve_original":true,
        "catenate_numbers":true
      }
    },
    "analyzer": {
      "default": {
        "type": "custom",
        "char_filter": [
          "html_strip",
          "replace"
        ],
        "tokenizer": "whitespace",
        "filter": [
            "lowercase",
            "word_delimiter"
        ]
      }
    }
  }
}

Searchkit Mapping best practices

Indexing fields for filtering & searching

Often you will want a field to be both searchable and appear as filters. A field will need to be indexed in 2 ways to achieve this, and we can make use of multi fields

Mapping example
{
  "movie" : {
    "properties" : {
      "genres" : {
        "type" : "string",
        "fields" : {          
          "raw" : {
            "type" : "string",
            "index" : "not_analyzed"
          }
        }
      }
    }
  }
}

This mapping will mean the genres field is indexed in 2 ways. The genres path will be analyzed by elastic search meaning it will be tokenized and have the standard stop words removed which is ideal for a free text search. The genres.raw path will be left untouched by Elasticsearch, Searchkit would use not_analyzed paths to power aggregated search components.

Component examples

Using the genres.raw field jsx <RefinementListFilter id="genres" title="Genres" field="genres.raw" operator="AND"/> Using the field for searching using prefix jsx <SearchBox prefixQueryFields={["genres^1", "name^10"]}/>

Developer Guide

setup

Developing

Running E2Es

experimental npm run test:e2e-standalone to run e2es. Requires protractor v4 to be installed globally (npm i -g protractor ) and selenium webdriver to be running.