---
# System prepended metadata

title: Building App Search Experiences with ReactiveSearch and OpenSearch

---

# Building App Search Experiences with ReactiveSearch and OpenSearch

![reactivesearchxopensearch.png](https://i.imgur.com/oOl1jI6.png)

A great end-user search experience contributes to higher customer engagement, leading to a better discovery experience which naturally boosts conversion and sales. Custom search engines like Elasticsearch and OpenSearch can be invaluable tools to deliver these experiences on.

In this post, we will show how to use [ReactiveSearch](https://reactivsearch.io) to scaffold powerful 🪄 search experiences in record time. We will show the setup of ReactiveSearch API server with [OpenSearch](https://opensearch.org) and then use its React UI components library to create a book search app, in just under an hour 🚀

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-final-app-opensearch-81bpu?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-final-app-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

## ReactiveSearch: A declarative API for searching with Elasticsearch

[ReactiveSearch](https://reactivesearch.io) is an open-source, declarative API for building search experiences powered by Elasticsearch. It allows capturing the search intent without exposing the entire DSL, is 4x more compact than Elasticsearch's DSL, offers typed API and comes with a free hosted IDE that allows building rich search experiences 10x faster.

Here's an example search query that searches for the word `"chronicles"` over fields `original_title` and `authors` with specific weights. It is generalized to work on any text (prefix, phrase) and is ~80 lines of code to write with Elasticsearch's DSL. The same query can be declaratively expressed in ~20 lines of code with the ReactiveSearch API (**4x smaller**).

![query-comparison.png](https://i.imgur.com/oX01wxx.png)

ReactiveSearch also comes with low-code search UI kits for popular web and mobile frameworks such as React, Vue, React Native, Flutter or a headless version in Vanilla JavaScript that can be used with frameworks like [Angular](https://medium.appbase.io/building-a-search-ui-with-angular-and-elasticsearch-d8a101ace1d) or Svelte. With over 5,000 Github stars and 1M+ downloads, you're in great company to create your SaaS and E-Commerce search UIs with the ReactiveSearch UI kits. These kits offer ReactiveSearch's declarative API as composable UI components (over 20+ different types of components are provided out-of-the-box), handle state management and common search related utilities such as capturing search and click based analytics, server-side rendering support, headless mode to bring your own design components, and callback events to create side-effects.

![ReactiveSearch UI kits](https://i.imgur.com/Z2LR5Yf.png)

Choose your UI library from [here](https://docs.appbase.io/docs/reactivesearch/gettingstarted) to explore how to get started.

## **2. OpenSearch: The History**

[OpenSearch](https://opensearch.org/) is an Apache 2.0 fork of Elasticsearch 7.10.2 (the last version licensed under Apache 2.0) maintained by Amazon and contributors. It came into being after Elastic relicensed future Elasticsearch, Kibana and other offerings under [SSPL / Elastic license](https://www.elastic.co/blog/licensing-change). As of this post, [OpenSearch 1.1.0 is the latest available version](https://opensearch.org/blog/releases/2021/10/Launch-Announcement-1-1/).

## **3. Setting up OpenSearch locally**

The best way of learning a new software is by actually trying it. Thanks to the continuous distribution under open-source, we can install in our local computer without any price string attached. The easiest way to install is to use the Docker image. Having said that, the prerequisite to the instructions is Docker. If you haven’t had it, check Docker's official [website](https://www.docker.com/) and install it.

OpenSearch Docker images use `amazonlinux:2` as the base image. We will create a network first:

```bash
docker network create reactivesearch
```

Next, we will run the opensearch container. The current image only works for a `linux/amd64` architecture. `linux/arm64` will be supported with OpenSearch 1.1:

```bash
docker run --name opensearch --rm -d -p 9200:9200 -e http.port=9200 -e discovery.type=single-node -e http.max_content_length=10MB -e http.cors.enabled=true -e http.cors.allow-origin=\* -e http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization -e http.cors.allow-credentials=true --net=reactivesearch opensearchproject/opensearch:latest
```

Note that there are more options in the command. Amongst them, `discovery.type=single-node` was added to avoid production bootstrap checks.

When you set up Elasticsearch or, actually, any other open-source software, it is common that users face unexpected issues derived from misconfigurations and incompatibility with their local environment. Elasticsearch used to display the issues as warnings that can be missed by people. To ensure that these settings receive attention, Elasticsearch puts bootstrap checks for startup, thus OpenSearch does that too.

Since we run this in our local environment with a single node, without the single node option, it will complain. To suppress it, we need to pass that option. Back to local setup. There were more added variables in the docker command as below:

```
http.cors.enabled=true
http.cors.allow-origin=* # you can also whitelist a specific domain instead of allowing all origins
http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization # allows additional headers
http.cors.allow-credentials=true
http.max_content_length=10MB
```

All of these are to allow cross-origin requests via our search web app that we will be building using ReactiveSearch. There are numerous network setting parameters. To find out more options, check the Elasticsearch [network reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html#http-settings).

The docker command will download the OpenSearch image and start running it. Let’s test if our OpenSearch is running normally. Open a new terminal and try:

```bash
curl -XGET https://localhost:9200 -u admin:admin --insecure
```

This will give you the JSON response like:

```json
{
     "name" : "9e2732ce355c",
     "cluster_name" : "docker-cluster",
     "cluster_uuid" : "RoLlFtjqTVyNnkTqbMFgqw",
     "version" : {
          "distribution" : "opensearch",
          "number" : "1.0.0",
          "build_type" : "tar",
          "build_hash" : "34550c5b17124ddc59458ef774f6b43a086522e3",
          "build_date" : "2021-07-02T23:22:21.383695Z",
          "build_snapshot" : false,
          "lucene_version" : "8.8.2",
          "minimum_wire_compatibility_version" : "6.8.0",
          "minimum_index_compatibility_version" : "6.0.0-beta1"
     },
     "tagline" : "The OpenSearch Project: <https://opensearch.org/>"
}
```

Let’s try the next command to retrieve specifications of your node.

```bash
curl -XGET https://localhost:9200/_cat/nodes?v -u admin:admin --insecure
```

This will give your the specs detail on your node as below.

```
ip   heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.17.0.2     20     97 10  0.19  0.47   0.32 dimr   *   9c53483a4c02
```

Finally, let’s check the list of plugins in your OpenSearch by:

```bash
curl -XGET https://localhost:9200/_cat/plugins?v -u admin:admin --insecure
```

This will return all the components in your node.

```
name         component                       version
3408201bf61f opensearch-alerting             1.0.0.0
3408201bf61f opensearch-anomaly-detection    1.0.0.0
3408201bf61f opensearch-asynchronous-search  1.0.0.0
3408201bf61f opensearch-index-management     1.0.1.0
3408201bf61f opensearch-job-scheduler        1.0.0.0
3408201bf61f opensearch-knn                  1.0.0.0
3408201bf61f opensearch-notebooks            1.0.0.0
3408201bf61f opensearch-performance-analyzer 1.0.1.0
3408201bf61f opensearch-reports-scheduler    1.0.0.0
3408201bf61f opensearch-security             1.0.1.0
3408201bf61f opensearch-sql                  1.0.0.0
```

Since we confirmed that it is running normally in your local, if you want to stop the container, run the docker stop command using the container name that we've set: `opensearch`.

```
docker stop opensearch
```

## **4. Setup ReactiveSearch API server to run along with OpenSearch**

Now that we’ve successfully configured OpenSearch, let’s try to run ReactiveSearch API along with OpenSearch. First, create a new config file named config.env with the following contents:

```bash
ES_CLUSTER_URL=https://admin:admin@opensearch:9200
USERNAME=reactivesearch-admin
PASSWORD=my-secure-password
```

These will form the Basic Auth credential with root access. Feel free to modify either of these values:

```bash
docker run --rm -d --name reactivesearch -p 8000:8000 --net=reactivesearch --env-file=config.env ghcr.io/appbaseio/reactivesearch-api
```

**Note:** The above command uses the Apache 2.0 licensed ReactiveSearch API image. You can use it's commercial image instead that's available over at hub.docker.com/repository/docker/appbaseio/reactivesearch-api.



To test if it’s running normally, run:

```bash
curl localhost:8000 -u reactivesearch-admin:my-secure-password
```

This will give you a response that looks like:

```json
{
  "name" : "cd34cadba56f",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "hrHHeJzwTP-X3zc49ZRt5w",
  "version" : {
    "number" : "7.10.2",
    "build_flavor" : "oss",
    "build_type" : "docker",
    "build_hash" : "747e1cc71def077253878a59143c1f785afa92b9",
    "build_date" : "2021-01-13T00:42:12.435326Z",
    "build_snapshot" : false,
    "lucene_version" : "8.7.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```


## **5. Building App Search with ReactiveSearch + OpenSearch**

We will create a books search application based on a dataset of ~10,000 books using ReactiveSearch and OpenSearch.

**Indexing Data**

Here's a [link to the books dataset](https://raw.githubusercontent.com/appbaseio-apps/booksearch/master/books-dataset.json) that we will be using for our search app. You can also see this data via [Dejavu](https://dejavu.appbase.io/?appname=good-books-ds&url=https://04717bb076f7:be54685e-db84-4243-975b-5b32ee241d31@appbase-demo-ansible-abxiydt-arc.searchbase.io&mode=edit), a databrowser for Elasticsearch and OpenSearch.

Now that we have setup ReactiveSearch API server with OpenSearch, we can make use of the appbase.io dashboard to import this dataset into our search index.

1. Go to [dash.appbase.io](https://dash.appbase.io) 
2. Enter the ReactiveSearch API server URL with the root username and password we created earlier
3. Create an index. Let's name this as `good-books`
4. Go to Browse Data view of the index
    1. Click on the `+ Add New Data` button
    2. Paste the JSON as is
5. Reload the view to see all the data indexed

<aside>
    💡 Alternatively, choose an <b>Interactive Tutorial</b> to import one of the preset sample datasets from our dashboard.

<br/>
<br/>

![https://i.imgur.com/zm3JiSN.png](https://i.imgur.com/zm3JiSN.png)

</aside>

**Building the Book search app**

This is how our final app will look like at the end of following this tutorial 🚀

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-final-app-opensearch-81bpu?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-final-app-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

We can either add ReactiveSearch to an existing app or create a boilerplate app with [Create React App (CRA)](https://github.com/facebookincubator/create-react-app). For this quickstart guide, we will use the CRA.

```
create-react-app my-awesome-search && cd my-awesome-search
```

OR

Alternatively, you can go to Codesandbox.io and choose the React Template.

![Choose React template from Codesandbox.io](https://i.imgur.com/Vl4BVZ0.png)


### Step 1: Install ReactiveSearch

We will fetch and install [reactivesearch](https://www.npmjs.com/package/@appbaseio/reactivesearch) module using yarn or npm.

```
yarn add @appbaseio/reactivesearch
```

or

```
npm install @appbaseio/reactivesearch
```

OR

Alternatively, you can directly add the `@appbaseio/reactivesearch` dependency to codesandbox.io.

---

### Step 2: Adding the first component

Lets add our first ReactiveSearch component: [ReactiveBase](https://docs.appbase.io/docs/reactivesearch/v3/overview/reactivebase/), it is a provider component that allows specifying the Elasticsearch/OpenSearch index to connect to. 

We will update our `src/App.js` file to add ReactiveBase component.

```jsx
import React from "react";
import { ReactiveBase, DataSearch } from "@appbaseio/reactivesearch";

function App() {
  return (
    <ReactiveBase
      url="https://admin:admin@opensearch:9200"
      app="good-books"
      credentials="reactivesearch-admin:our-secure-password"
      enableAppbase
    >
      {/* Our components will go over here */}
      Hello from ReactiveSearch 👋
    </ReactiveBase>
  );
}

export default App;
```

**Note:** You can set `enableAppbase={false}` if you are directly connecting to an Elasticsearch service without using the appbase.io API gateway. However, we **now offer an open-source and free** version of appbase.io service (called ReactiveSearch API) and highly recommend using it over querying your Elasticsearch cluster directly. appbase.io as an API gateway provides a declarative search API, ability to use query rules with the search engine relevance, actionable analytics and access control for search.

This is how the app should look after running the `yarn start` command.

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-reactivebase-opensearch-oq76h?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-reactivebase-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

---

### Step 3: Adding Search and Aggregation components

For this app, we will be using [DataSearch](https://docs.appbase.io/docs/reactivesearch/v3/search/datasearch/), [MultiList](https://docs.appbase.io/docs/reactivesearch/v3/list/multilist/) and [SingleRange](https://docs.appbase.io/docs/reactivesearch/v3/range/singlerange/) components for searching and filtering on the index. And [ResultCard](https://docs.appbase.io/docs/reactivesearch/v3/result/resultcard/) component for showing the search results.

Lets add them within the ReactiveBase component. But before we do that, we will look at the important props for each.

### DataSearch

```jsx
<DataSearch
    componentId="searchbox"
    dataField={[
        {
            "field": "authors",
            "weight": 3
        },
        {
            "field": "authors.autosuggest",
            "weight": 1
        },
        {
            "field": "original_title",
            "weight": 5
        },
        {
            "field": "original_title.autosuggest",
            "weight": 1
        },
    ]}
    placeholder="Search for books or authors"
/>
```

The **[DataSearch](https://docs.appbase.io/docs/reactivesearch/v3/search/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.

At this point, you should see the following:

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-datasearch-opensearch-ecs85?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-datasearch-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

### MultiList

Next, we will add the **[MultiList](https://docs.appbase.io/docs/reactivesearch/v3/list/multilist/)** component. As the name suggests, it creates a multiple selection aggregation (aka facet) to filter our search results by.

```jsx
<MultiList
    componentId="authorsfilter"
    dataField="authors.keyword"
    title="Filter by Authors"
    aggregationSize={5}
/>
```

Aggregation components like MultiList fire a term type query. You can think of a term query as an exact match query, unlike a search query which involves more nuances. The use of the `.keyword` suffix for the `authors` field informs the search engine that the query here is of an exact type.

The `aggregationSize` prop is used to specify the total aggregations (think buckets) that you want returned based on the dataField value.

**Note:** The `dataField` value in MultiList is of string type, since an aggregation is always performed on a single field. In contrast, you may want to search on multiple fields in different ways, so the DataSearch component uses an array of fields instead.

### SingleRange

Next, we will add the **[SingleRange](https://docs.appbase.io/docs/reactivesearch/v3/range/singlerange/)** component for creating a ratings based filter for our book search.

```jsx
<SingleRange
    componentId="ratingsfilter"
    dataField="average_rating"
    title="Filter by Ratings"
    data={[
        { start: 4, end: 5, label: '4 stars and up' },
        { start: 3, end: 5, label: '3 stars and up' },
    ]}
    defaultValue="4 stars and up"
/>
```

The SingleRange operates on a numeric datatype field and fires a range query. The `data` prop of SingleRange allows specifying a [start, end] range and a label associated with it. Using `defaultValue`, we can preselect a particular option. In this case, we’re preselecting all the books that have a rating of `4 stars and up`.

At this point, this is how our app should be looking:

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-datasearch-aggregations-opensearch-xxrm8?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-datasearch+aggregations-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

We have just added completely functional search and aggregation components! 🙌

### Step 4: Adding Results Component

We just need a results component to display the books that we’re searching for. We will use the [ReactiveList](https://docs.appbase.io/docs/reactivesearch/v3/result/reactivelist/) component with the [ResultCard](https://docs.appbase.io/docs/reactivesearch/v3/result/resultcard/) preset.

```jsx
<ReactiveList
    componentId="results"
    size={6}
    pagination={true}
    react={{
        and: ["searchbox", "authorsfilter", "ratingsfilter"]
    }}
    render={({ data }) => (
        <ReactiveList.ResultCardsWrapper>
        {data.map((item) => (
            <ResultCard key={item._id}>
            <ResultCard.Image src={item.image} />
            <ResultCard.Title
                dangerouslySetInnerHTML={{
                __html: item.original_title
                }}
            />
            <ResultCard.Description>
                {item.authors + " " + "*".repeat(item.average_rating)}
            </ResultCard.Description>
            </ResultCard>
        ))}
        </ReactiveList.ResultCardsWrapper>
    )}
/>
```

The `react` prop here specifies that the result should depend on the queries for our searchbox, authors filter and the ratings filter. It’s pretty neat!

In the `render` method, we are using the ResultCard preset to iterate over each result (aka hit) and set the image, title and description values of the card layout.

At this point, you should be seeing our entire app functionally (minus the layouting and styles):

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-results-opensearch-i3xws?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-results-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

We have built our entire search UI in just 60 lines of code!

---

### Step 5: Adding Layout and Styles

ReactiveSearch doesn’t use a layout system internally. If you are using a grid from Bootstrap or Materialize, you can use that. Here, will just make use of CSS Flex.

If you are new to Flex, we recommend a quick read of [this article](https://css-tricks.com/snippets/css/a-guide-to-flexbox/).

With ~6 more lines, our final app layout looks as follows.

```jsx
<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>
```

Add some margins between the search and result component, and voila! Our final app is ready:

<iframe src="https://codesandbox.io/embed/reactivesearch-quickstart-final-app-opensearch-81bpu?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="reactivesearch-quickstart-final-app-opensearch"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

If you like to run this setup locally, clone the [ReactiveSearch starter app](https://github.com/appbaseio-apps/reactivesearch-starter-app).

## Next Steps

Now that we've created our first search app with ReactiveSearch, we can [explore the REST API examples](https://docs.appbase.io/api/examples/rest/) to check out how to build search APIs for use-cases such as faceted search, suggestions, instant results search and geo search.

Next, explore appbase.io's control plane at [dash.appbase.io](http://dash.appbase.io) to configure search relevance, synonyms and query rules with a point-and-click control plane.

![appbase.io dashboard control plane view](https://i.imgur.com/cct2bbf.png)

**Image:** Some of the configuration options available to set search relevance.

## **Do more with appbase.io**

![https://i.imgur.com/QjBkE7R.png](https://i.imgur.com/QjBkE7R.png)

[Appbase.io](http://appbase.io) provides a commercial version of ReactiveSearch API that builds on the Apache 2.0 licensed open-source version and provides Search/click analytics, a search relevance control plane, a #NoCode UI Builder, caching and stored queries.

You can deploy appbase.io in two modes:

- You can install an OpenSearch + appbase.io cluster: You get a fully managed OpenSearch experience with all the appbase.io platform features baked in. [Get started over here](https://appbase.io/partnership/opensearch).
- Or you can also install appbase.io with your existing OpenSearch cluster, e.g. one that's hosted with AWS OpenSearch service. There are three options here.
    - [Deploy with appbase.io cloud](https://docs.appbase.io/docs/hosting/byoc/#using-appbaseio),
    - [Use the appbase.io AMI available on AWS Marketplace](https://docs.appbase.io/docs/hosting/byoc/#using-ami), or
    - [Use our Docker Image](https://docs.appbase.io/docs/hosting/byoc/#using-docker) (we also provide a Helm chart for deploying to K8S).

appbase.io can be installed on our cloud or self-hosted with Docker.