Appbase.io
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 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.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully