Skip to main content

Reactive Search Starter

Shenzhen, China

ReactiveSearch.io provides a supercharged Elasticsearch experience for creating the most demanding app search experiences with a no-code search relevance control plane, UI builder, out-of-the-box search analytics and low-code UI components.

To be able to use the library with your own installation of Elasticsearch or Opensearch you first need to define a user role for the search client. This role needs to have read access to all indexies you want to be able to run searches against:

Reactive Search Starter

I am going to use this role on an anonymous user to be able to run searches against the Elasticsearch REST API without needing to authenticate - Note that to be able to use the Anonymous User you need to activate it in elasticsearch/config/elasticsearch.yml:

Reactive Search Starter

I can now create a file that holds the information my client needs to connect to the Elasticsearch cluster, e.g.:

ElasticParam.js

export const _connectionString = 'https://my.search.com';
export const _index = 'my_index_2022_12_28'; 

The Search Index Mapping I am using here has a few keys that I will be running searches against:

  • "field": "title",
  • "field": "description",
  • "field": "tags",
  • "field": "abstract",
  • "field": "short",

Create-React-App

With Elasticsearch/Opensearch set up we can now start coding the search interface. For this example I am going to scaffold a basic React.js App and install the ReactiveSearch library using:

npx create-react-app reactive-search-starter && cd reactive-search-starter
npm install @appbaseio/reactivesearch

Connecting to the Cluster

Update src/App.js to add ReactiveBase component and import the Elasticsearch connection info created earlier:

import React from 'react'
import { ReactiveBase, DataSearch } from '@appbaseio/reactivesearch'

import { _connectionString, _index } from './ElasticParam'

function App() {
  return (
    <ReactiveBase
        url={_connectionString}
        app={_index}
        theme={{
          colors: {
            primaryColor: "#bebebe",
            textColor: "#7e7e7e"
          },
        }}
    >
      PlaceHolder Text to be Replaced by later Search Components
    </ReactiveBase>
  );
}

export default App

Adding Search and Aggregation Components

For this app, I will be using DataSearch and MultiList components for searching and filtering on the index. And ResultCard component for showing the search results.

DataSearch

The DataSearch component creates a searchbox UI component that queries on the specified fields with weights as specified by dataField prop. That's all it takes to create a functional search component.

<DataSearch
	componentId="searchbox"
  placeholder="Search Assistant"
	dataField={[
		{
			"field": "title",
			"weight": 5
		},
		{
			"field": "description",
			"weight": 3
		},
		{
			"field": "tags.raw",
			"weight": 5
		},
		{
			"field": "abstract",
			"weight": 2
		},
		{
			"field": "short",
			"weight": 2
		},
	]}
/>

MultiList

Next, we have to add the MultiList component. This creates a multiple selection aggregation to filter the search results by.

<MultiList
	componentId="searchfilter"
	dataField="tags.raw"
	title="Filter by Tags"
	aggregationSize={10}
/>

Aggregation components like MultiList fire a term type query. You can think of a term query as an exact match query. The field you are using to aggregate over needs to be of type keyword. In my mapping this is the case for tags.raw. The aggregationSize prop is used to specify the total aggregations (think buckets) that you want returned based on the dataField value.

Adding Results Component

The results component displays the articles that we're searching for. We will use the ReactiveList component with the ResultCard preset.

<ReactiveList
  componentId="results"
  dataField="_score"
  size={35}
  pagination={true}
  react={{
    and: ["searchbox", "searchfilter"]
  }}
  render={({ data }) => (
    <ReactiveList.ResultCardsWrapper>
    {data.map((item) => (
      <ResultCard key={item._id}>
      <ResultCard.Image src={item.imagesquare} />
      <ResultCard.Title
        dangerouslySetInnerHTML={{
        __html: item.title
        }}
      />
      <ResultCard.Description>
        {item.abstract}
      </ResultCard.Description>
      </ResultCard>
    ))}
    </ReactiveList.ResultCardsWrapper>
  )}
/>

Adding Layout and Styles

ReactiveSearch doesn't use a layout system internally. But it is easy to add some CSS Flex:

<ReactiveBase>
	<div style={{ display: "flex", flexDirection: "row" }}>
		<div style={{ display: "flex", flexDirection: "column", width: "30%", margin: "10px" }}>
			<MultiList/>
			<SingleRange/>
		</div>
		<div style={{ display: "flex", flexDirection: "column", width: "66%" }}>
			<DataSearch/>
			<ReactiveList/>
		</div>
	</div>
</ReactiveBase>

Reactive Search Starter