<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ReactiveSearch Blog]]></title><description><![CDATA[ReactiveSearch is an open-source, declarative API for building search experiences powered by Elasticsearch, OpenSearch and MongoDB Atlas Search.

Tutorials and updates on all things #search]]></description><link>https://blog.reactivesearch.io</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1647009079277/CWXjtvynb.png</url><title>ReactiveSearch Blog</title><link>https://blog.reactivesearch.io</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 08:16:59 GMT</lastBuildDate><atom:link href="https://blog.reactivesearch.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[ReactiveSearch is Now Fully Open Source]]></title><description><![CDATA[The entire ReactiveSearch stack is now completely free and open-source: API gateway, dashboard, search relevancy, pipelines, AI search, analytics, access control, caching, UI builder and more. No payw]]></description><link>https://blog.reactivesearch.io/reactivesearch-is-opensource</link><guid isPermaLink="true">https://blog.reactivesearch.io/reactivesearch-is-opensource</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[opensearch]]></category><dc:creator><![CDATA[Reactive Search]]></dc:creator><pubDate>Tue, 24 Feb 2026 16:12:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/589eed69710ea4825970a84c/eefb724a-6c00-4681-ab4b-e381e4fc069d.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>The entire ReactiveSearch stack is now completely free and open-source: API gateway, dashboard, search relevancy, pipelines, AI search, analytics, access control, caching, UI builder and more. No paywalls, no hosted-only features.</p>
</blockquote>
<h2>What is ReactiveSearch?</h2>
<p>ReactiveSearch is an API gateway that sits between your client applications and Elasticsearch (or OpenSearch). It supercharges your search cluster with:</p>
<ul>
<li><p><strong>Search Relevancy</strong> — fine-tune results without touching Elasticsearch configs</p>
</li>
<li><p><strong>Pipelines</strong> — composable, scriptable query and indexing pipelines</p>
</li>
<li><p><strong>AI Search</strong> — vector search, kNN, hybrid search out of the box</p>
</li>
<li><p><strong>Analytics</strong> — search analytics, click analytics, conversions</p>
</li>
<li><p><strong>Access Control</strong> — API keys, Role-based access, field-level security</p>
</li>
<li><p><strong>Speed &amp; Caching</strong> — query caching and rate limiting</p>
</li>
<li><p><strong>UI Builder</strong> — no-code search UI builder</p>
</li>
<li><p><strong>Data Management</strong> — import, browse and manage your indices visually</p>
</li>
</ul>
<p>It works with any client — React, Vue, Flutter, React Native, vanilla JavaScript, REST APIs — or the built-in no-code UI builder.</p>
<img src="https://cdn.hashnode.com/uploads/covers/589eed69710ea4825970a84c/cd069e74-4455-497b-8608-a7f4496bf942.svg" alt="" style="display:block;margin:0 auto" />

<h2>Get Started in 4 Steps</h2>
<p>The entire stack runs with Docker. Here's how to get it up in under 5 minutes.</p>
<h3>1. Clone and start the services</h3>
<pre><code class="language-shell">git clone https://github.com/appbaseio/reactivesearch-api-docker.git \
  &amp;&amp; cd reactivesearch-api-docker

docker-compose -f docker-compose-with-elasticsearch.yaml up -d
</code></pre>
<p>This starts Elasticsearch, ReactiveSearch API, Nginx (with TLS) and Fluent Bit (for log shipping) — all with a single command.</p>
<details>
<summary>Using OpenSearch?</summary>
<p>Use the OpenSearch compose file instead:</p><pre class="not-prose"><code class="language-shell">docker-compose -f docker-compose-with-opensearch.yaml up -d</code></pre>
</details>

<h3>2. Verify the service is running</h3>
<pre><code class="language-shell">curl http://localhost:8000 -u rs-admin-user:rs-password
</code></pre>
<p>You should see a response like:</p>
<pre><code class="language-json">{
  "name": "elasticsearch",
  "cluster_name": "docker-cluster",
  "version": {
    "number": "9.3.0",
    "build_flavor": "default",
    "build_type": "docker",
    "build_hash": "17b451d8...",
    "build_date": "2026-01-29T10:05:46.708Z",
    "build_snapshot": false,
    "lucene_version": "10.3.2"
  },
  "tagline": "You Know, for Search"
}
</code></pre>
<p>This confirms ReactiveSearch is running and connected to your search cluster.</p>
<h3>3. Connect the Dashboard</h3>
<p>Open <a href="http://dash.reactivesearch.io">dash.reactivesearch.io</a> in your browser and sign in.</p>
<img src="https://cdn.hashnode.com/uploads/covers/589eed69710ea4825970a84c/9f2159a2-2543-445a-8e49-c247f2ec31f4.png" alt="" style="display:block;margin:0 auto" />

<h3>4. Start building</h3>
<p>After signing in you'll land on the <strong>Cluster Overview</strong> — your central hub for managing indices, configuring search relevancy, building search UIs, setting up analytics and more.</p>
<img src="https://cdn.hashnode.com/uploads/covers/589eed69710ea4825970a84c/bb922ac1-bec7-4b16-bb34-f9feade3172a.png" alt="" style="display:block;margin:0 auto" />

<h2>Why Open Source</h2>
<p>Open source has always been central to ReactiveSearch. Projects like <a href="https://github.com/appbaseio/reactivesearch">ReactiveSearch UI</a> (1M+ installs, 4.9K GitHub stars) and <a href="https://github.com/appbaseio/dejavu">Dejavu</a> (4.7M Docker pulls, 8.5K GitHub stars) have been community staples for years.</p>
<p>And with ReactiveSearch project having reached a maturity, it now makes sense to give it fully to the community. With the current state of AI tooling, anyone can self-host, customize and extend it without friction. This is the most complete version of ReactiveSearch, and now it's yours.</p>
<p>The API and UI libraries are available under the Apache 2.0 license (and MIT license for some UI libraries).</p>
<p>You can support the project by <a href="https://github.com/sponsors/appbaseio">sponsoring ReactiveSearch on GitHub</a>.</p>
<h2>Resources</h2>
<ul>
<li><p><a href="https://docs.reactivesearch.io/docs/gettingstarted/quickstart/"><strong>Quickstart Guide</strong></a> — Import data, preview search and build your first search UI</p>
</li>
<li><p><a href="https://docs.reactivesearch.io/docs/pipelines/how-to/"><strong>Create a Search Pipeline</strong></a> — Learn how to create a search pipeline</p>
</li>
<li><p><a href="https://reactivesearch.io/how-to/build-search-ui"><strong>Build a Search UI</strong></a> — Hands-on demos with ReactiveSearch UI libraries</p>
</li>
<li><p><a href="https://github.com/appbaseio/reactivesearch-api"><strong>GitHub</strong></a> — Star the repo, fork it, contribute</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[ReactiveSearch v8.24.0: Native kNN (Vector) search and filtering for Elasticsearch]]></title><description><![CDATA[ReactiveSearch API has introduced first-class support for vector search (kNN) since v8.24.0 (released in Feb 2025). This enables developers to seamlessly integrate vector similarity search alongside standard filters (term, range, or geo) on Elasticse...]]></description><link>https://blog.reactivesearch.io/reactivesearch-8-24-0-native-knn-vector-search-for-elasticsearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/reactivesearch-8-24-0-native-knn-vector-search-for-elasticsearch</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[VectorSearch]]></category><category><![CDATA[vector embeddings]]></category><category><![CDATA[openai]]></category><category><![CDATA[reactivesearch]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Mon, 10 Feb 2025 14:16:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739197438679/0e73a60e-5ea8-4631-a159-ccb6666f2ca0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>ReactiveSearch API</strong> has introduced first-class support for vector search (kNN) since <strong>v8.24.0</strong> (released in Feb 2025). This enables developers to seamlessly integrate vector similarity search alongside standard filters (term, range, or geo) on <strong>Elasticsearch</strong> and OpenSearch. In this post, we will focus on kNN searching with Elasticsearch.</p>
<p>ReactiveSearch API enables you to build bespoke search UIs by taking search intent and transforming it (structured queries, AI embeddings, filters) directly into optimized search engine DSL queries. This lets you focus on designing relevance, not wrangling low-level DSL syntax.</p>
<p><strong>Why Vector Search?</strong></p>
<p>Vector search (kNN) opens up semantic and similarity-based use cases:</p>
<p>• <strong>Semantic Text Search:</strong> Retrieve documents based on their semantic similarity rather than keyword overlap.</p>
<p>• <strong>Image Similarity:</strong> Rank images by feature vectors (embeddings), enabling “find similar” functionality.</p>
<p>• <strong>Recommendation Engines:</strong> Find near-neighbor items for personalization or content-based recommendations.</p>
<p>• <strong>Anomaly Detection:</strong> Identify outliers by distance metrics in high-dimensional embedding space.</p>
<p>Many production teams already rely on Elasticsearch for text-based queries and can now easily incorporate vector-based similarity with the same cluster.</p>
<p><strong>A Quick Demo of ReactiveSearch kNN</strong></p>
<p>ReactiveSearch simplifies vector search by letting you combine it with typical search filters. Here’s an example request to the <code>_reactivesearch</code> API endpoint showing a <strong>kNN</strong> query combined with a term filter (tags.keyword) and a traditional text-based search (one<em>_</em>liner):</p>
<iframe src="https://play.reactivesearch.io/embed/tWX2nTavbvklHU8whhnw" width="100%" height="600px" style="border:none">
</iframe>

<p>This request is translated into a native Elasticsearch DSL query using the <code>knn</code> clause and a filter. The <code>vector_data</code> field holds high-dimensional embeddings, and the final ranking of results is determined by vector similarity.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"_source"</span>: {
        <span class="hljs-attr">"excludes"</span>: [],
        <span class="hljs-attr">"includes"</span>: [
            <span class="hljs-string">"name"</span>,
            <span class="hljs-string">"one_liner"</span>,
            <span class="hljs-string">"tags"</span>
        ]
    },
    <span class="hljs-attr">"knn"</span>: {
        <span class="hljs-attr">"field"</span>: <span class="hljs-string">"vector_data"</span>,
        <span class="hljs-attr">"filter"</span>: {
            <span class="hljs-attr">"bool"</span>: {
                <span class="hljs-attr">"must"</span>: [
                    {
                        <span class="hljs-attr">"bool"</span>: {
                            <span class="hljs-attr">"must"</span>: [
                                {
                                    <span class="hljs-attr">"bool"</span>: {
                                        <span class="hljs-attr">"filter"</span>: {
                                            <span class="hljs-attr">"term"</span>: {
                                                <span class="hljs-attr">"tags.keyword"</span>: <span class="hljs-string">"Fashion"</span>
                                            }
                                        }
                                    }
                                },
                                {
                                    <span class="hljs-attr">"bool"</span>: {
                                        <span class="hljs-attr">"must"</span>: {
                                            <span class="hljs-attr">"bool"</span>: {
                                                <span class="hljs-attr">"minimum_should_match"</span>: <span class="hljs-number">1</span>,
                                                <span class="hljs-attr">"should"</span>: [
                                                    {
                                                        <span class="hljs-attr">"multi_match"</span>: {
                                                            <span class="hljs-attr">"fields"</span>: [
                                                                <span class="hljs-string">"one_liner"</span>
                                                            ],
                                                            <span class="hljs-attr">"operator"</span>: <span class="hljs-string">"or"</span>,
                                                            <span class="hljs-attr">"query"</span>: <span class="hljs-string">"dresses"</span>,
                                                            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"cross_fields"</span>
                                                        }
                                                    },
                                                    {
                                                        <span class="hljs-attr">"multi_match"</span>: {
                                                            <span class="hljs-attr">"fields"</span>: [
                                                                <span class="hljs-string">"one_liner"</span>
                                                            ],
                                                            <span class="hljs-attr">"fuzziness"</span>: <span class="hljs-number">0</span>,
                                                            <span class="hljs-attr">"operator"</span>: <span class="hljs-string">"or"</span>,
                                                            <span class="hljs-attr">"query"</span>: <span class="hljs-string">"dresses"</span>,
                                                            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"best_fields"</span>
                                                        }
                                                    },
                                                    {
                                                        <span class="hljs-attr">"multi_match"</span>: {
                                                            <span class="hljs-attr">"fields"</span>: [
                                                                <span class="hljs-string">"one_liner"</span>
                                                            ],
                                                            <span class="hljs-attr">"operator"</span>: <span class="hljs-string">"or"</span>,
                                                            <span class="hljs-attr">"query"</span>: <span class="hljs-string">"dresses"</span>,
                                                            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"phrase"</span>
                                                        }
                                                    },
                                                    {
                                                        <span class="hljs-attr">"multi_match"</span>: {
                                                            <span class="hljs-attr">"fields"</span>: [
                                                                <span class="hljs-string">"one_liner"</span>
                                                            ],
                                                            <span class="hljs-attr">"operator"</span>: <span class="hljs-string">"or"</span>,
                                                            <span class="hljs-attr">"query"</span>: <span class="hljs-string">"dresses"</span>,
                                                            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"phrase_prefix"</span>
                                                        }
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        },
        <span class="hljs-attr">"k"</span>: <span class="hljs-number">12</span>,
        <span class="hljs-attr">"num_candidates"</span>: <span class="hljs-number">12</span>,
        <span class="hljs-attr">"query_vector"</span>: [
            <span class="hljs-number">-0.002622403</span>,
            <span class="hljs-number">-0.008986259</span>,
            <span class="hljs-number">-0.012236752</span>,
            ....
            <span class="hljs-comment">//1536 values</span>
        ]
    },
    <span class="hljs-attr">"size"</span>: <span class="hljs-number">10</span>
}
</code></pre>
<p>ReactiveSearch automatically translates this high-level request into a detailed Elasticsearch <strong>kNN</strong> query with integrated filters. By simply specifying your intent, you get advanced vector semantics wrapped in a developer-friendly interface.</p>
<p><strong>Note:</strong> ReactiveSearch is able to translate the search text input into an embedding vector based on a pre-configured model that can set through AI preferences.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739193911824/67a47a93-ddce-4446-a817-51ad2de8f0c0.png" alt="ReactiveSearch AI preferences UI panel where user can set the default embedding model to use." class="image--center mx-auto" /></p>
<p>For embedding model choice, there is support for <code>text-embedding-3-small</code>, <code>text-embedding-3-large</code> and <code>text-embedding-ada-002</code>.</p>
<h3 id="heading-from-intent-to-performance-leveraging-elasticsearch-at-scale">From Intent to Performance: Leveraging Elasticsearch at Scale</h3>
<p>Since v8.16.0, Elasticsearch has been released as open source under the AGPL and has received multiple performance boosts in the 8.x series that make vector search production-ready at scale. Notable enhancements include improved concurrency for kNN graph traversal, advanced quantization (8‑bit/4‑bit) to reduce memory usage, and integrated filtering that enables sub‑50 ms kNN queries even when applying term or range constraints. Combined with robust indexing and search concurrency, these updates ensure that Elasticsearch can efficiently handle millions of vector documents.</p>
<p><strong>Elasticsearch Performance &amp; Tuning</strong></p>
<p>• <strong>Mapping &amp; Index Creation</strong>:</p>
<p>• Enable index:true on dense_vector fields for approximate kNN.</p>
<p>• Consider quantized field types (int8_hnsw or int4_hnsw) to drastically reduce memory usage.</p>
<p>• For large embeddings (up to 4096 dims), ensure your cluster runs Elasticsearch 8.11+.</p>
<p>• <strong>HNSW Parameters</strong>:</p>
<p>• <strong>m</strong>: Higher means better recall at the cost of more memory.</p>
<p>• <strong>ef_construction</strong>: Boost indexing quality vs. speed trade-off.</p>
<p>• <strong>num_candidates</strong>: Raise this at query time for higher accuracy (with some latency increase).</p>
<p>• <strong>Index Lifecycle &amp; Segment Management</strong>:</p>
<p>• Avoid overly frequent refresh intervals to limit overhead when building HNSW graphs.</p>
<p>• Use force merges or well-sized shards to keep query latencies low.</p>
<p>• <strong>Filtering &amp; Hybrid Queries</strong>:</p>
<p>• Elasticsearch 8.x supports filters directly in knn queries.</p>
<p>• Combine text-based matches and vector queries in the same DSL call for powerful hybrid search.</p>
<hr />
<p><strong>Ready to add vector search to your application?</strong></p>
<p>With <strong>ReactiveSearch API v8.24.0</strong>, you can seamlessly turn semantic intent—like text queries, AI-driven embeddings, and domain filters—into optimized Elasticsearch <strong>kNN</strong> queries. From sub-50ms latencies on millions of documents to powerful hybrid filtering and re-ranking, Elasticsearch 8.x has become a production-ready powerhouse for vector-based retrieval.</p>
<p><strong>Ready to get started?</strong> Just add a <code>vectorDataField</code> in your ReactiveSearch queries and fine-tune Elasticsearch’s HNSW parameters. You’ll be serving lightning-fast semantic search results in no time. Create a new search cluster or use the upgrade option from your cluster dashboard to use v8.24.0</p>
]]></content:encoded></item><item><title><![CDATA[Enhancing Your KNN Search UI with Faceting: A Follow-Up Guide]]></title><description><![CDATA[1. Introduction
In our previous post, we explored how vector (KNN) search lets you search for what you mean rather than what you type. In this post, we’re taking it one step further by integrating faceting capabilities into our search UI. By combinin...]]></description><link>https://blog.reactivesearch.io/hybrid-search-knn-search-ui-with-faceting</link><guid isPermaLink="true">https://blog.reactivesearch.io/hybrid-search-knn-search-ui-with-faceting</guid><category><![CDATA[VectorSearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[React]]></category><category><![CDATA[opensearch]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Thu, 06 Feb 2025 13:14:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738848012156/b1a6aa12-1681-41c0-a314-39127366bb03.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-1-introduction">1. Introduction</h2>
<p><a target="_blank" href="https://blog.reactivesearch.io/vector-search-ui-with-reactivesearch-and-elasticsearch#heading-indexing-data">In our previous post</a>, we explored how vector (KNN) search lets you search for what you mean rather than what you type. In this post, we’re taking it one step further by integrating faceting capabilities into our search UI. By combining semantic search with faceting, users can filter results (for example, by industry) and get even more precise matches.</p>
<p><strong>Note:</strong> With ReactiveSearch UI, you can now seamlessly integrate vector search capabilities with most of your UI components. This hybrid search approach merges vector search with traditional keyword search techniques to deliver contextual, precise results across all of your data in any modality, with less effort.</p>
<blockquote>
<p>Try out the live demo:  <a target="_blank" href="https://oss-demos.reactivesearch.io/knn-with-faceting?search=%22gene+editing%22">Knn search with faceting demo</a></p>
</blockquote>
<h2 id="heading-2-why-faceting-matters">2. Why Faceting Matters</h2>
<ul>
<li><strong>Refined Results:</strong> Facets let users quickly narrow down search outputs by specific categories.</li>
<li><strong>Enhanced Usability:</strong> Merging semantic search with traditional filters creates a more intuitive experience.</li>
<li><strong>Hybrid Search:</strong> This approach combines vector search for contextual understanding with faceting for categorical filtering.</li>
</ul>
<p>Under the hood, ReactiveSearch leverages the Approximate kNN query DSL in Elasticsearch and OpenSearch, applying faceting during the kNN search phase to deliver precise, context-aware results.</p>
<h2 id="heading-3-setting-up-the-environment">3. Setting Up the Environment</h2>
<p>Before diving in, ensure that you:</p>
<ul>
<li>Set up a ReactiveSearch cluster using ReactiveSearch Cloud (compatible with both Elasticsearch and OpenSearch).</li>
<li>Index your vector dataset. You can use this <a target="_blank" href="https://blog.reactivesearch.io/vector-search-ui-with-reactivesearch-and-elasticsearch#heading-indexing-data">indexing utility</a> to help get started.</li>
<li>Install the necessary dependencies:<ul>
<li><code>@appbaseio/reactivesearch</code> (for building the search UI)</li>
<li><code>@emotion/styled</code> (for styling components)</li>
<li><code>react-icons</code> (for adding iconography)</li>
</ul>
</li>
</ul>
<p><strong>Note:</strong></p>
<ul>
<li><strong>Vector Field Mapping:</strong> Configure the field used for vector indexing and searching as a <code>dense_vector</code> in your Elasticsearch index mapping. If you're using the index utility provided by Elasticsearch, it will configure this for you.</li>
</ul>
<h2 id="heading-4-building-the-faceted-knn-search-ui">4. Building the Faceted KNN Search UI</h2>
<h3 id="heading-reactivebase-and-searchbox">ReactiveBase and SearchBox</h3>
<p>We start by wrapping our UI with <code>ReactiveBase</code> to connect to the search cluster and add a <code>SearchBox</code> for capturing semantic queries.</p>
<pre><code class="lang-jsx">&lt;ReactiveBase
  app=<span class="hljs-string">"yc-companies-dataset"</span>
  url=<span class="hljs-string">"https://appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
  credentials=<span class="hljs-string">"a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61"</span>
  transformRequest={<span class="hljs-function">(<span class="hljs-params">request</span>) =&gt;</span> {
    <span class="hljs-comment">// Custom query transformation logic</span>
  }}
&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Container</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>K-Nearest Neighbors Search with Facets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
      <span class="hljs-attr">componentId</span>=<span class="hljs-string">"search"</span>
      <span class="hljs-attr">dataField</span>=<span class="hljs-string">{[</span>"<span class="hljs-attr">name</span>", "<span class="hljs-attr">one_liner</span>"]}
      <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Semantic search for startup companies"</span>
      <span class="hljs-attr">autosuggest</span>=<span class="hljs-string">{false}</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">1rem</span>" }}
      <span class="hljs-attr">URLParams</span>
    /&gt;</span>
    {/* Additional layout components go here */}
  <span class="hljs-tag">&lt;/<span class="hljs-name">Container</span>&gt;</span></span>
&lt;/ReactiveBase&gt;
</code></pre>
<h3 id="heading-faceting-with-multilist">Faceting with MultiList</h3>
<p>Next, add a <code>MultiList</code> component that enables filtering by industries. It is configured to react to the search input.</p>
<pre><code class="lang-jsx">&lt;FacetContainer&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MultiList</span>
    <span class="hljs-attr">componentId</span>=<span class="hljs-string">"industries"</span>
    <span class="hljs-attr">dataField</span>=<span class="hljs-string">"industries.keyword"</span>
    <span class="hljs-attr">title</span>=<span class="hljs-string">"Industries"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Filter by industries"</span>
    <span class="hljs-attr">showSearch</span>=<span class="hljs-string">{false}</span>
    <span class="hljs-attr">react</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">and:</span> ["<span class="hljs-attr">search</span>"] }}
    <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">1rem</span>" }}
  /&gt;</span></span>
&lt;/FacetContainer&gt;
</code></pre>
<h3 id="heading-displaying-results-with-reactivelist">Displaying Results with ReactiveList</h3>
<p>The <code>ReactiveList</code> component renders our search results. Each result displays company details like logo, name, description, team size, stage, industry tags, and a website link.</p>
<pre><code class="lang-jsx">&lt;ResultsContainer&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveList</span>
    <span class="hljs-attr">componentId</span>=<span class="hljs-string">"results"</span>
    <span class="hljs-attr">vectorDataField</span>=<span class="hljs-string">"vector_data"</span>
    <span class="hljs-attr">dataField</span>=<span class="hljs-string">"_score"</span>
    <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span>
    <span class="hljs-attr">pagination</span>=<span class="hljs-string">{false}</span>
    <span class="hljs-attr">react</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">and:</span> ["<span class="hljs-attr">search</span>", "<span class="hljs-attr">industries</span>"] }}
    <span class="hljs-attr">includeFields</span>=<span class="hljs-string">{[</span>
      "<span class="hljs-attr">name</span>",
      "<span class="hljs-attr">one_liner</span>",
      "<span class="hljs-attr">long_description</span>",
      "<span class="hljs-attr">team_size</span>",
      "<span class="hljs-attr">stage</span>",
      "<span class="hljs-attr">industries</span>",
      "<span class="hljs-attr">website</span>",
      "<span class="hljs-attr">small_logo_thumb_url</span>",
    ]}
    <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">data</span> }) =&gt;</span> (
      <span class="hljs-tag">&lt;&gt;</span>
        {data.map((item) =&gt; {
          const company = item._source || item;
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">ResultItem</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{company._id}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Logo</span>
                <span class="hljs-attr">src</span>=<span class="hljs-string">{company.small_logo_thumb_url}</span>
                <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">company.name</span>} <span class="hljs-attr">logo</span>`}
              /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Info</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">CompanyName</span>&gt;</span>{company.name}<span class="hljs-tag">&lt;/<span class="hljs-name">CompanyName</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">OneLiner</span>&gt;</span>
                  {company.one_liner || company.long_description}
                <span class="hljs-tag">&lt;/<span class="hljs-name">OneLiner</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Meta</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">FaUsers</span> /&gt;</span> {company.team_size}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">FaBuilding</span> /&gt;</span> {company.stage}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">Meta</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Tags</span>&gt;</span>
                  {company.industries &amp;&amp;
                    company.industries.map((ind) =&gt; (
                      <span class="hljs-tag">&lt;<span class="hljs-name">Tag</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{ind}</span>&gt;</span>{ind}<span class="hljs-tag">&lt;/<span class="hljs-name">Tag</span>&gt;</span>
                    ))}
                <span class="hljs-tag">&lt;/<span class="hljs-name">Tags</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                  <span class="hljs-attr">href</span>=<span class="hljs-string">{company.website}</span>
                  <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
                  <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
                &gt;</span>
                  Visit website
                <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">Info</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">ResultItem</span>&gt;</span>
          );
        })}
      <span class="hljs-tag">&lt;/&gt;</span></span>
    )}
    renderNoResults={<span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>No results found<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>}
  /&gt;
&lt;/ResultsContainer&gt;
</code></pre>
<h3 id="heading-customizing-search-queries">Customizing Search Queries</h3>
<p>The <code>transformRequest</code> function customizes the search query before sending it to Elasticsearch. It dynamically adjusts the payload based on whether a semantic query or industry filter (or both) are applied.</p>
<pre><code class="lang-jsx">transformRequest={<span class="hljs-function">(<span class="hljs-params">request</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> body = <span class="hljs-built_in">JSON</span>.parse(request.body);
  <span class="hljs-keyword">const</span> searchQuery = body.query.find(<span class="hljs-function">(<span class="hljs-params">q</span>) =&gt;</span> q.id === <span class="hljs-string">"search"</span>);
  <span class="hljs-keyword">const</span> industriesQuery = body.query.find(<span class="hljs-function">(<span class="hljs-params">q</span>) =&gt;</span> q.id === <span class="hljs-string">"industries"</span>);
  <span class="hljs-keyword">const</span> resultsQueryIndex = body.query.findIndex(<span class="hljs-function">(<span class="hljs-params">q</span>) =&gt;</span> q.id === <span class="hljs-string">"results"</span>);
  <span class="hljs-keyword">const</span> resultsQuery = body.query.find(<span class="hljs-function">(<span class="hljs-params">q</span>) =&gt;</span> q.id === <span class="hljs-string">"results"</span>);

  <span class="hljs-keyword">if</span> (
    (searchQuery?.value &amp;&amp; searchQuery.value !== <span class="hljs-string">""</span>) ||
    (industriesQuery?.value &amp;&amp; industriesQuery.value.length &gt; <span class="hljs-number">0</span>)
  ) {
    <span class="hljs-keyword">if</span> (resultsQuery) {
      resultsQuery.candidates = resultsQuery.size;
      <span class="hljs-keyword">if</span> (searchQuery.value) {
        resultsQuery.value = searchQuery.value;
        resultsQuery.react = { <span class="hljs-attr">and</span>: [<span class="hljs-string">"industries"</span>] };
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (industriesQuery.value) {
        resultsQuery.value = industriesQuery.value.join(<span class="hljs-string">" "</span>);
      }
      <span class="hljs-keyword">if</span> (resultsQuery.dataField) {
        <span class="hljs-keyword">delete</span> resultsQuery.dataField;
      }
      body.query[resultsQueryIndex] = resultsQuery;
    }
  } <span class="hljs-keyword">else</span> {
    body.query.splice(resultsQueryIndex, <span class="hljs-number">1</span>);
  }
  <span class="hljs-keyword">return</span> {
    ...request,
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(body),
  };
}}
</code></pre>
<h2 id="heading-5-code-walkthrough">5.  Code Walkthrough</h2>
<ul>
<li><strong>ReactiveBase:</strong> Connects the UI to your ReactiveSearch/Elasticsearch backend.</li>
<li><strong>SearchBox:</strong> Captures semantic queries from users.</li>
<li><strong>MultiList:</strong> Implements faceting by filtering search results based on industry.</li>
<li><strong>ReactiveList:</strong> Displays the results in a structured and styled list.</li>
<li><strong>transformRequest:</strong> Adjusts the search query dynamically based on active filters.</li>
</ul>
<p>Each component works together to create a hybrid search experience that combines vector search with traditional faceting.</p>
<h2 id="heading-6-live-demo-and-final-thoughts">6. Live Demo and Final Thoughts</h2>
<p>Check out the live demo here: <a target="_blank" href="https://oss-demos.reactivesearch.io/knn">KNN with Facets Demo</a>. You can browse the code for the demo over here: https://github.com/awesome-reactivesearch/opensource-demos/blob/main/src/pages/KNN/knn-with-faceting.jsx.</p>
<p>In this follow-up guide, we enhanced our original vector search UI by incorporating faceting. This approach allows users to refine search results effectively, making the overall search experience more precise and user-friendly. Leveraging the ReactiveSearch UI kit, you can easily combine the power of hybrid search techniques with traditional filters to deliver an unmatched search experience. Stay tuned for more advanced search UI enhancements using ReactiveSearch!</p>
]]></content:encoded></item><item><title><![CDATA[Guide to Building a Vector Search UI Using ReactiveSearch and OpenSearch 2.17]]></title><description><![CDATA[How often have you looked for something, but you're not sure what it’s called? Vector search (aka KNN-search) solves for this problem by allowing you to search for what you mean.
Internally, a vector search engine stores a numerical representation of...]]></description><link>https://blog.reactivesearch.io/guide-to-building-a-vector-search-ui-using-reactivesearch-and-opensearch-2-17-0</link><guid isPermaLink="true">https://blog.reactivesearch.io/guide-to-building-a-vector-search-ui-using-reactivesearch-and-opensearch-2-17-0</guid><category><![CDATA[opensearch]]></category><category><![CDATA[VectorSearch]]></category><category><![CDATA[openai]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[search]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Fri, 04 Oct 2024 14:44:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728052908918/228e14a2-787d-4c33-bf20-a5da3e9049bb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>How often have you looked for something, but you're not sure what it’s called? Vector search (aka KNN-search) solves for this problem by allowing you to search for what you mean.</p>
<p>Internally, a vector search engine stores a numerical representation of each data: be it text, images, or other types of media even, called embeddings. These embeddings capture the underlying meaning and context of the data rather than just the exact words or pixels. When you search using vector search, the engine compares the embedding of your query to the embeddings of all stored items. It finds the results that are most semantically similar, even if they don’t contain the exact words or elements you used in your search.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726914765003/b74be33a-cee7-4045-94b1-296690ce7fc9.png" alt="A simplified vector representation, picture credit:  ODSC - Open Data Science | https://odsc.medium.com/a-gentle-introduction-to-vector-search-3c0511bc6771" class="image--center mx-auto" /></p>
<p>This means you can search with vague descriptions, synonyms, or even related concepts, and still find the right match because vector search understands what you’re looking for, not just the exact terms you use. This makes it an ideal choice for product discovery, recommendation systems, natural-language and voice search use-cases.</p>
<h3 id="heading-the-ingredients">The Ingredients</h3>
<p>In this post, I will show how to build a vector search UI using ReactiveSearch for the search UI, OpenSearch as the vector search engine, and OpenAI embeddings API for creating the numerical vector representation.</p>
<p><a target="_blank" href="https://www.reactivesearch.io">ReactiveSearch</a> offers an API for authoring search endpoints as well as an open-source UI kit for building search UIs with React, Vue, and Flutter.</p>
<p>OpenSearch is a versatile open-source keyword and vector search engine. The latest OpenSearch 2.17.0 release introduces several vector search enhancements,</p>
<p><a target="_blank" href="https://oss-demos.reactivesearch.io/knn"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726915327826/76f160ae-403e-494f-a784-dde3661f2d70.png" alt class="image--center mx-auto" /></a></p>
<p>The vector search UI allows searching from over 5,000 Y-Combinator companies. You can try out the live demo at: <a target="_blank" href="https://oss-demos.reactivesearch.io/knn">https://oss-demos.reactivesearch.io/knn</a>.</p>
<h3 id="heading-create-search-cluster">Create Search Cluster</h3>
<p>We start by creating a search cluster with ReactiveSearch Cloud at <a target="_blank" href="https://reactivesearch.io">https://reactivesearch.io</a>. This will create a deployment of the OpenSearch engine and the ReactiveSearch API service.</p>
<p>If you already have an OpenSearch cluster hosted, you can deploy the ReactiveSearch API service using one of these <a target="_blank" href="https://github.com/appbaseio/reactivesearch-api-docker">docker-compose files</a>.</p>
<h3 id="heading-indexing-data">Indexing Data</h3>
<p>We start by indexing the dataset of over 5,000 Y-Combinator companies into the ReactiveSearch (with OpenSearch engine configured) cluster.</p>
<p>If you already have an OpenSearch index, you can use the KNN re-index utility <a target="_blank" href="https://github.com/appbaseio/ai-scripts/tree/master/knn_reindex">https://github.com/appbaseio/ai-scripts/tree/master/knn_reindex</a> to generate embeddings based on your existing data + mappings and re-index the data. It uses the <code>text-embedding-3-small</code> model from OpenAI for embedding, this can be updated by forking the utility.</p>
<iframe src="https://dejavu.appbase.io/?appname=yc-companies-dataset&amp;url=https://4a0b1df1e215:a48031c3-4816-42e1-b0f4-21dc742c10d0@appbase-demo-ansible-abxiydt-arc.searchbase.io&amp;mode=view&amp;sidebar=false&amp;appswitcher=false" width="100%" height="600px" style="border:none">
</iframe>

<h3 id="heading-authoring-search-pipeline">Authoring Search Pipeline</h3>
<p>Next, I will author a search pipeline with the ReactiveSearch dashboard. A ReactiveSearch pipeline allows stitching the search engine (here, OpenSearch) with additional connectors to enrich search queries, add transformation steps, or enrich search engine results. The pipeline definition is based on a JSON format and these steps can be written in JavaScript. Check out these <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/">how-to guides</a> to see what you can build with pipelines.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"enabled"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"KNN query pipeline"</span>,
    <span class="hljs-attr">"routes"</span>: [
        {
            <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/_knn_search/_reactivesearch"</span>,
            <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>,
            <span class="hljs-attr">"classify"</span>: {
                <span class="hljs-attr">"category"</span>: <span class="hljs-string">"reactivesearch"</span>
            }
        }
    ],
    <span class="hljs-attr">"envs"</span>: {
        <span class="hljs-attr">"index"</span>: <span class="hljs-string">"yc-companies-dataset"</span>
    },
    <span class="hljs-attr">"stages"</span>: [
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"authorize user"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"fetch embeddings"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"openAIEmbeddings"</span>,
            <span class="hljs-attr">"inputs"</span>: {
                <span class="hljs-attr">"apiKey"</span>: <span class="hljs-string">"{{openAIConfig.open_ai_key}}"</span>,
                <span class="hljs-attr">"useWithReactiveSearchQuery"</span>: <span class="hljs-literal">true</span>
            },
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"os_query_body"</span>,
            <span class="hljs-attr">"scriptRef"</span>: <span class="hljs-string">"os_query"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"httpRequest"</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">"HTTP query to OS"</span>,
            <span class="hljs-attr">"inputs"</span>: {
                <span class="hljs-attr">"url"</span>: <span class="hljs-string">"http://localhost:9200/{{index}}/_search"</span>,
                <span class="hljs-attr">"body"</span>: <span class="hljs-string">"{{{osBody}}}"</span>,
                <span class="hljs-attr">"headers"</span>: {
                    <span class="hljs-attr">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
                },
                <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>
            }
        }
    ]
}
</code></pre>
<p>Here, we first get the OpenAI embedding for the incoming search query, then generate the query DSL body using the <code>os_query</code> script (below) and then send this to the OpenSearch engine to return search results.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Parse the request body</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'request body is: '</span>, context.request.body);
    reqBody = <span class="hljs-built_in">JSON</span>.parse(context.request.body);

    <span class="hljs-comment">// Construct the OpenSearch DSL query</span>
    <span class="hljs-keyword">var</span> osBody = <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">query</span>: {
            <span class="hljs-attr">knn</span>: {
                <span class="hljs-attr">vector_data</span>: {
                    <span class="hljs-attr">vector</span>: reqBody.query[<span class="hljs-number">0</span>].queryVector,
                    <span class="hljs-attr">k</span>: <span class="hljs-number">10</span>,
                },
            },
        },
        <span class="hljs-attr">_source</span>: [
            <span class="hljs-string">'name'</span>,
            <span class="hljs-string">'one_liner'</span>,
            <span class="hljs-string">'website'</span>,
            <span class="hljs-string">'small_logo_thumb_url'</span>,
            <span class="hljs-string">'team_size'</span>,
            <span class="hljs-string">'industries'</span>,
            <span class="hljs-string">'regions'</span>,
            <span class="hljs-string">'stage'</span>,
        ],
        <span class="hljs-attr">size</span>: <span class="hljs-number">10</span>,
    });

    <span class="hljs-comment">// Return the updated context with the new esBody</span>
    <span class="hljs-keyword">return</span> { ...context, osBody };
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726919353788/cac7d12c-aaab-40bb-bc24-89d897ac414c.png" alt class="image--center mx-auto" /></p>
<p>The ReactiveSearch dashboard provides an execution environment where I can set the REST API request body and see the live response from the pipeline. It also provides console logs and individual stage changes that can be helpful while developing.</p>
<p>We just configured the pipeline to respond to this API endpoint:</p>
<pre><code class="lang-bash">curl -XPOST https://search-server.com/_knn_search/reactivesearch -d <span class="hljs-string">'{
    "query": [
        {
            "id": "search",
            "value": "Semantic search API",
            "vectorDataField": "vector_data"
        }
    ]
}'</span>
</code></pre>
<p>The pipeline fetches the OpenAI embeddings first for the search input, and then creates the query DSL to run a KNN search query against the OpenSearch engine. The results are then returned back without any modifications.</p>
<h3 id="heading-building-the-search-ui">Building The Search UI</h3>
<p>Now that the REST API is configured, the next and final step is to build a search UI. Here, I’m choosing to build a basic Search UI with a ChatGPT prompt.</p>
<pre><code class="lang-markdown"><span class="hljs-section">### Task</span>

Write a React component named <span class="hljs-code">`KNN`</span> that implements a K-Nearest Neighbors (KNN) based search UI.

<span class="hljs-section">### Server Connection Info</span>

<span class="hljs-bullet">-</span> <span class="hljs-strong">**POST method**</span>: <span class="hljs-code">`https://appbase-demo-ansible-abxiydt-arc.searchbase.io/_knn_search/_reactivesearch`</span>
<span class="hljs-bullet">-</span> <span class="hljs-strong">**API Payload**</span>:

{
  "query": [
<span class="hljs-code">    {
      "id": "search",
      "value": "Semantic search API",
      "vectorDataField": "vector_data"
    }
  ]
}
</span>
Response is returned in Elasticsearch hits format.

<span class="hljs-section">### Component Requirements</span>

React with styled-components based on @emotion

<span class="hljs-section">### Search Functionality</span>

<span class="hljs-bullet">-</span> Implement an input field for search queries.
<span class="hljs-bullet">-</span> Include a button labeled <span class="hljs-strong">**"Try random search input"**</span> to autofill a random query from a predefined list.
<span class="hljs-bullet">-</span> Use <span class="hljs-strong">**Basic Auth**</span> with credentials:
<span class="hljs-bullet">  -</span> Username: <span class="hljs-code">`a03a1cb71321`</span>
<span class="hljs-bullet">  -</span> Password: <span class="hljs-code">`75b6603d-9456-4a5a-af6b-a487b309eb61`</span>

<span class="hljs-section">### State Management</span>

<span class="hljs-bullet">-</span> Use <span class="hljs-code">`useState`</span> to manage:
<span class="hljs-bullet">  -</span> <span class="hljs-code">`searchInput`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`results`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`loading`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`noResults`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`searchTime`</span>
<span class="hljs-bullet">-</span> Use <span class="hljs-code">`useEffect`</span> and <span class="hljs-code">`useRef`</span> to handle side effects and input debouncing.

<span class="hljs-section">### Result Display</span>

<span class="hljs-bullet">-</span> Display search results in a styled list with:
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Company logo**</span>  <span class="hljs-code">`small_logo_thum_url`</span> field
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Company name**</span>  <span class="hljs-code">`name`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**One-liner description**</span> <span class="hljs-code">`one_liner`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Team size and stage**</span> (with icons from <span class="hljs-code">`react-icons/fa`</span>) <span class="hljs-code">`team_size`</span>, <span class="hljs-code">`stage`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Industries**</span> as tags <span class="hljs-code">`industries`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Link**</span> to the company’s website. <span class="hljs-code">`website`</span>

<span class="hljs-section">### Loading and No Results States</span>

<span class="hljs-bullet">-</span> Show a loading message during search.
<span class="hljs-bullet">-</span> Display a message if no results are found.

Please provide the complete code.
</code></pre>
<p>This should generate a close to functional code. Here is a version with some tweaks to the ChatGPT output: <a target="_blank" href="https://github.com/awesome-reactivesearch/opensearch-conf-demo-wrapper/blob/main/src/pages/KNN/index.jsx">KNN/index.jsx</a>.</p>
<p>You can try out the live demo at: <a target="_blank" href="https://oss-demos.reactivesearch.io/knn">https://oss-demos.reactivesearch.io/knn</a>.</p>
<h3 id="heading-summary">Summary</h3>
<p>In this guide, we explored how to build a vector search UI using ReactiveSearch and OpenSearch 2.17. I started by creating a search cluster with ReactiveSearch Cloud, then indexed a dataset of over 5,000 Y-Combinator companies. Next, I authored a search pipeline to handle the search queries and fetch results using OpenAI embeddings. Finally, I built a simple search UI to interact with the search API.</p>
<p>In the next post, we will see how to build a hybrid search with KNN-search along with faceting, where we will use the ReactiveSearch UI kit for building the search UI.</p>
]]></content:encoded></item><item><title><![CDATA[Guide to Building a Vector Search UI Using ReactiveSearch and Elasticsearch 8.15]]></title><description><![CDATA[How often have you looked for something, but you're not sure what it’s called? Vector search (aka KNN-search) solves for this problem by allowing you to search for what you mean.
Internally, a vector search engine stores a numerical representation of...]]></description><link>https://blog.reactivesearch.io/vector-search-ui-with-reactivesearch-and-elasticsearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/vector-search-ui-with-reactivesearch-and-elasticsearch</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[search]]></category><category><![CDATA[VectorSearch]]></category><category><![CDATA[vector embeddings]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Sat, 21 Sep 2024 12:42:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726663516255/f4e02930-a755-462c-8d60-2d99c9391ef6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>How often have you looked for something, but you're not sure what it’s called? Vector search (aka KNN-search) solves for this problem by allowing you to search for what you mean.</p>
<p>Internally, a vector search engine stores a numerical representation of each data: be it text, images, or other types of media even, called embeddings. These embeddings capture the underlying meaning and context of the data rather than just the exact words or pixels. When you search using vector search, the engine compares the embedding of your query to the embeddings of all stored items. It finds the results that are most semantically similar, even if they don’t contain the exact words or elements you used in your search.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726914765003/b74be33a-cee7-4045-94b1-296690ce7fc9.png" alt="A simplified vector representation, picture credit:  ODSC - Open Data Science | https://odsc.medium.com/a-gentle-introduction-to-vector-search-3c0511bc6771" class="image--center mx-auto" /></p>
<p>This means you can search with vague descriptions, synonyms, or even related concepts, and still find the right match because vector search understands what you’re looking for, not just the exact terms you use. This makes it an ideal choice for product discovery, recommendation systems, natural-language and voice search use-cases.</p>
<h3 id="heading-the-ingredients">The Ingredients</h3>
<p>In this post, I will show how to build a vector search UI using ReactiveSearch for the search UI, Elasticsearch as the vector search engine, and OpenAI embeddings API for creating the numerical vector representation.</p>
<p><a target="_blank" href="https://www.reactivesearch.io">ReactiveSearch</a> offers an API for authoring search endpoints as well as an open-source UI kit for building search UIs with React, Vue, and Flutter.</p>
<p>Elasticsearch is a versatile keyword and vector search engine. Particularly of note, in the last year, Elasticsearch has significantly enhanced its vector search capabilities, with improvements in indexing and querying vector fields from versions 7.x to 8.15, including support for approximate nearest neighbor (ANN) search and optimizations for high-dimensional data.</p>
<p><a target="_blank" href="https://oss-demos.reactivesearch.io/knn"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726915327826/76f160ae-403e-494f-a784-dde3661f2d70.png" alt class="image--center mx-auto" /></a></p>
<p>The vector search UI allows searching from over 5,000 Y-Combinator companies. You can try out the live demo at: <a target="_blank" href="https://oss-demos.reactivesearch.io/knn">https://oss-demos.reactivesearch.io/knn</a>.</p>
<h3 id="heading-create-search-cluster">Create Search Cluster</h3>
<p>We start by creating a search cluster with ReactiveSearch Cloud at https://reactivesearch.io. This will create a deployment of the Elasticsearch engine and the ReactiveSearch API service.</p>
<p>If you already have an Elasticsearch cluster hosted, either as OpenSearch or with Elasticsearch Cloud, you can deploy the ReactiveSearch API service using one of these <a target="_blank" href="https://github.com/appbaseio/reactivesearch-api-docker">docker-compose files</a>.</p>
<h3 id="heading-indexing-data">Indexing Data</h3>
<p>We start by indexing the dataset of over 5,000 Y-Combinator companies into the ReactiveSearch (with Elasticsearch engine configured) cluster.</p>
<p>If you already have an Elasticsearch index, you can use the KNN re-index utility <a target="_blank" href="https://github.com/appbaseio/ai-scripts/tree/master/knn_reindex">https://github.com/appbaseio/ai-scripts/tree/master/knn_reindex</a> to generate embeddings based on your existing data + mappings and re-index the data. It uses the <code>text-embedding-3-small</code> model from OpenAI for embedding, this can be updated by forking the utility.</p>
<iframe src="https://dejavu.appbase.io/?appname=yc-companies-dataset&amp;url=https://4a0b1df1e215:a48031c3-4816-42e1-b0f4-21dc742c10d0@appbase-demo-ansible-abxiydt-arc.searchbase.io&amp;mode=view&amp;sidebar=false&amp;appswitcher=false" width="100%" height="600px" style="border:none">
</iframe>

<h3 id="heading-authoring-search-pipeline">Authoring Search Pipeline</h3>
<p>Next, I will author a search pipeline with the ReactiveSearch dashboard. A ReactiveSearch pipeline allows stitching the search engine (here, Elasticsearch) with additional connectors to enrich search queries, add transformation steps, or enrich search engine results. The pipeline definition is based on a JSON format and these steps can be written in JavaScript. Check out these <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/">how-to guides</a> to see what you can build with pipelines.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"enabled"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"KNN query pipeline"</span>,
    <span class="hljs-attr">"routes"</span>: [
        {
            <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/_knn_search/_reactivesearch"</span>,
            <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>,
            <span class="hljs-attr">"classify"</span>: {
                <span class="hljs-attr">"category"</span>: <span class="hljs-string">"reactivesearch"</span>
            }
        }
    ],
    <span class="hljs-attr">"envs"</span>: {
        <span class="hljs-attr">"index"</span>: <span class="hljs-string">"yc-companies-dataset"</span>
    },
    <span class="hljs-attr">"stages"</span>: [
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"authorize user"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"fetch embeddings"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"openAIEmbeddings"</span>,
            <span class="hljs-attr">"inputs"</span>: {
                <span class="hljs-attr">"apiKey"</span>: <span class="hljs-string">"{{openAIConfig.open_ai_key}}"</span>,
                <span class="hljs-attr">"useWithReactiveSearchQuery"</span>: <span class="hljs-literal">true</span>
            },
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"es_query_body"</span>,
            <span class="hljs-attr">"scriptRef"</span>: <span class="hljs-string">"es_query"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"httpRequest"</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">"HTTP query to ES"</span>,
            <span class="hljs-attr">"inputs"</span>: {
                <span class="hljs-attr">"url"</span>: <span class="hljs-string">"http://localhost:9200/{{index}}/_search"</span>,
                <span class="hljs-attr">"body"</span>: <span class="hljs-string">"{{{esBody}}}"</span>,
                <span class="hljs-attr">"headers"</span>: {
                    <span class="hljs-attr">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
                },
                <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>
            }
        }
    ]
}
</code></pre>
<p>Here, we first get the OpenAI embedding for the incoming search query, then generate the query DSL body using the <code>es_query</code> script (below) and then send this to the Elasticsearch engine to return search results.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// es_query.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params"></span>) </span>{
    reqBody = <span class="hljs-built_in">JSON</span>.parse(context.request.body);
    <span class="hljs-keyword">var</span> esBody = <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">knn</span>: {
            <span class="hljs-attr">field</span>: <span class="hljs-string">'vector_data'</span>,
            <span class="hljs-attr">query_vector</span>: reqBody.query[<span class="hljs-number">0</span>].queryVector,
            <span class="hljs-attr">k</span>: <span class="hljs-number">10</span>,
            <span class="hljs-attr">num_candidates</span>: <span class="hljs-number">100</span>,
        },
        <span class="hljs-attr">_source</span>: [
            <span class="hljs-string">'name'</span>,
            <span class="hljs-string">'one_liner'</span>,
            <span class="hljs-string">'website'</span>,
            <span class="hljs-string">'small_logo_thumb_url'</span>,
            <span class="hljs-string">'team_size'</span>,
            <span class="hljs-string">'industries'</span>,
            <span class="hljs-string">'regions'</span>,
            <span class="hljs-string">'stage'</span>,
        ],
    });
    <span class="hljs-comment">// sets esBody at the top-level in the context,</span>
    <span class="hljs-keyword">return</span> { ...context, esBody };
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726919353788/cac7d12c-aaab-40bb-bc24-89d897ac414c.png" alt class="image--center mx-auto" /></p>
<p>The ReactiveSearch dashboard provides an execution environment where I can set the REST API request body and see the live response from the pipeline. It also provides console logs and individual stage changes that can be helpful while developing.</p>
<p>We just configured the pipeline to respond to this API endpoint:</p>
<pre><code class="lang-bash">curl -XPOST https://search-server.com/_knn_search/reactivesearch -d <span class="hljs-string">'{
    "query": [
        {
            "id": "search",
            "value": "Semantic search API",
            "vectorDataField": "vector_data"
        }
    ]
}'</span>
</code></pre>
<p>The pipeline fetches the OpenAI embeddings first for the search input, and then creates the query DSL to run a KNN search query against the Elasticsearch engine. The results are then returned back without any modifications.</p>
<h3 id="heading-building-the-search-ui">Building The Search UI</h3>
<p>Now that the REST API is configured, the next and final step is to build a search UI. Here, I’m choosing to build a basic Search UI with a ChatGPT prompt.</p>
<pre><code class="lang-markdown"><span class="hljs-section">### Task</span>

Write a React component named <span class="hljs-code">`KNN`</span> that implements a K-Nearest Neighbors (KNN) based search UI.

<span class="hljs-section">### Server Connection Info</span>

<span class="hljs-bullet">-</span> <span class="hljs-strong">**POST method**</span>: <span class="hljs-code">`https://appbase-demo-ansible-abxiydt-arc.searchbase.io/_knn_search/_reactivesearch`</span>
<span class="hljs-bullet">-</span> <span class="hljs-strong">**API Payload**</span>:

{
  "query": [
<span class="hljs-code">    {
      "id": "search",
      "value": "Semantic search API",
      "vectorDataField": "vector_data"
    }
  ]
}
</span>
Response is returned in Elasticsearch hits format.

<span class="hljs-section">### Component Requirements</span>

React with styled-components based on @emotion

<span class="hljs-section">### Search Functionality</span>

<span class="hljs-bullet">-</span> Implement an input field for search queries.
<span class="hljs-bullet">-</span> Include a button labeled <span class="hljs-strong">**"Try random search input"**</span> to autofill a random query from a predefined list.
<span class="hljs-bullet">-</span> Use <span class="hljs-strong">**Basic Auth**</span> with credentials:
<span class="hljs-bullet">  -</span> Username: <span class="hljs-code">`a03a1cb71321`</span>
<span class="hljs-bullet">  -</span> Password: <span class="hljs-code">`75b6603d-9456-4a5a-af6b-a487b309eb61`</span>

<span class="hljs-section">### State Management</span>

<span class="hljs-bullet">-</span> Use <span class="hljs-code">`useState`</span> to manage:
<span class="hljs-bullet">  -</span> <span class="hljs-code">`searchInput`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`results`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`loading`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`noResults`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-code">`searchTime`</span>
<span class="hljs-bullet">-</span> Use <span class="hljs-code">`useEffect`</span> and <span class="hljs-code">`useRef`</span> to handle side effects and input debouncing.

<span class="hljs-section">### Result Display</span>

<span class="hljs-bullet">-</span> Display search results in a styled list with:
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Company logo**</span>  <span class="hljs-code">`small_logo_thum_url`</span> field
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Company name**</span>  <span class="hljs-code">`name`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**One-liner description**</span> <span class="hljs-code">`one_liner`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Team size and stage**</span> (with icons from <span class="hljs-code">`react-icons/fa`</span>) <span class="hljs-code">`team_size`</span>, <span class="hljs-code">`stage`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Industries**</span> as tags <span class="hljs-code">`industries`</span>
<span class="hljs-bullet">  -</span> <span class="hljs-strong">**Link**</span> to the company’s website. <span class="hljs-code">`website`</span>

<span class="hljs-section">### Loading and No Results States</span>

<span class="hljs-bullet">-</span> Show a loading message during search.
<span class="hljs-bullet">-</span> Display a message if no results are found.

Please provide the complete code.
</code></pre>
<p>This should generate a close to functional code. Here is a version with some tweaks to the ChatGPT output: <a target="_blank" href="https://github.com/awesome-reactivesearch/opensearch-conf-demo-wrapper/blob/main/src/pages/KNN/index.jsx">KNN/index.jsx</a>.</p>
<p>You can try out the live demo at: <a target="_blank" href="https://oss-demos.reactivesearch.io/knn">https://oss-demos.reactivesearch.io/knn</a>.</p>
<h3 id="heading-summary"><strong>Summary</strong></h3>
<p>In this guide, we explored how to build a vector search UI using ReactiveSearch and Elasticsearch 8.15. I started by creating a search cluster with ReactiveSearch Cloud, then indexed a dataset of over 5,000 Y-Combinator companies. Next, I authored a search pipeline to handle the search queries and fetch results using OpenAI embeddings. Finally, I built a simple search UI to interact with the search API.</p>
<p>In the next post, we will see how to build a hybrid search with KNN-search along with faceting, where we will use the ReactiveSearch UI kit for building the search UI.</p>
]]></content:encoded></item><item><title><![CDATA[Building Search UIs with Flutter Searchbox 4.0]]></title><description><![CDATA[Introduction
Search functionality is a vital component of modern applications, significantly enhancing user experience by providing quick access to relevant information. As Flutter continues to gain popularity for building cross-platform apps, integr...]]></description><link>https://blog.reactivesearch.io/building-search-uis-with-flutter-searchbox-40</link><guid isPermaLink="true">https://blog.reactivesearch.io/building-search-uis-with-flutter-searchbox-40</guid><category><![CDATA[Flutter]]></category><category><![CDATA[search]]></category><category><![CDATA[opensearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Thu, 08 Aug 2024 11:59:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727184349015/a42d6a82-42ab-43af-be2c-bff995de3ace.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h4 id="heading-introduction">Introduction</h4>
<p>Search functionality is a vital component of modern applications, significantly enhancing user experience by providing quick access to relevant information. As Flutter continues to gain popularity for building cross-platform apps, integrating a robust search interface has become increasingly important. Enter <a target="_blank" href="https://github.com/appbaseio/flutter-searchbox"><strong>Flutter Searchbox</strong></a> 4.0, a package that offers a declarative API to seamlessly connect your Flutter app with Elasticsearch or OpenSearch, enabling the creation of powerful search UIs.</p>
<p>In this post, we’ll guide you through setting up a basic search interface using Flutter Searchbox 4.0, highlighting its key features and providing a practical example.</p>
<h4 id="heading-understanding-flutter-searchbox-40">Understanding Flutter Searchbox 4.0</h4>
<p>Flutter Searchbox is designed to integrate seamlessly with ReactiveSearch, making it easier to query Elasticsearch and OpenSearch. It allows developers to build responsive and dynamic search UIs with minimal code, leveraging pre-built widgets for autosuggestions, results rendering, facets and maps.</p>
<h4 id="heading-installation">Installation</h4>
<p>Before diving into coding, let’s set up the necessary dependencies.</p>
<ol>
<li><p><strong>Depend on it</strong><br /> Add the following dependencies to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">codedependencies:</span>
   <span class="hljs-attr">flutter_searchbox:</span> <span class="hljs-string">^4.0.0</span>
   <span class="hljs-attr">searchbase:</span> <span class="hljs-string">^4.0.0</span>
</code></pre>
</li>
<li><p><strong>Install it</strong><br /> Run the following command to install the packages:</p>
<pre><code class="lang-bash"> flutter pub get
</code></pre>
</li>
</ol>
<h4 id="heading-basic-usage">Basic Usage</h4>
<p>Now that we have the dependencies installed, let’s build a simple search UI.</p>
<h5 id="heading-a-simple-example">A Simple Example</h5>
<p>The following example demonstrates how to create a search interface with an autosuggestion search widget and a custom result widget.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:searchbase/searchbase.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_searchbox/flutter_searchbox.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(FlutterSearchBoxApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FlutterSearchBoxApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">final</span> searchbaseInstance = SearchBase(
    <span class="hljs-string">'good-books-ds'</span>,
    <span class="hljs-string">'https://appbase-demo-ansible-abxiydt-arc.searchbase.io'</span>,
    <span class="hljs-string">'a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61'</span>,
    appbaseConfig: AppbaseSettings(
      recordAnalytics: <span class="hljs-keyword">true</span>,
      userId: <span class="hljs-string">'test@dev'</span>,
    ),
  );

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> SearchBaseProvider(
      searchbase: searchbaseInstance,
      child: MaterialApp(
        title: <span class="hljs-string">"SearchBox Demo"</span>,
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: HomePage(),
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(<span class="hljs-string">'SearchBox Demo'</span>),
        actions: [
          IconButton(
            icon: Icon(Icons.search),
            onPressed: () {
              showSearch(
                context: context,
                delegate: SearchBox(
                  id: <span class="hljs-string">'search-widget'</span>,
                  enableRecentSearches: <span class="hljs-keyword">true</span>,
                  enablePopularSuggestions: <span class="hljs-keyword">true</span>,
                  showAutoFill: <span class="hljs-keyword">true</span>,
                  maxPopularSuggestions: <span class="hljs-number">3</span>,
                  size: <span class="hljs-number">10</span>,
                  dataField: [
                    {<span class="hljs-string">'field'</span>: <span class="hljs-string">'original_title'</span>, <span class="hljs-string">'weight'</span>: <span class="hljs-number">1</span>},
                    {<span class="hljs-string">'field'</span>: <span class="hljs-string">'original_title.search'</span>, <span class="hljs-string">'weight'</span>: <span class="hljs-number">3</span>},
                  ],
                ),
              );
            },
          ),
        ],
      ),
      body: Center(
        child: SearchWidgetConnector(
          id: <span class="hljs-string">'result-widget'</span>,
          dataField: <span class="hljs-string">'original_title'</span>,
          react: {
            <span class="hljs-string">'and'</span>: [<span class="hljs-string">'search-widget'</span>],
          },
          size: <span class="hljs-number">10</span>,
          triggerQueryOnInit: <span class="hljs-keyword">true</span>,
          preserveResults: <span class="hljs-keyword">true</span>,
          builder: (context, searchController) =&gt; ResultsWidget(searchController),
        ),
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResultsWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">final</span> SearchController searchController;
  ResultsWidget(<span class="hljs-keyword">this</span>.searchController);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Column(
      children: [
        Card(
          child: Align(
            alignment: Alignment.centerLeft,
            child: Container(
              color: Colors.white,
              height: <span class="hljs-number">20</span>,
              child: Text(
                <span class="hljs-string">'<span class="hljs-subst">${searchController.results.numberOfResults}</span> results found in <span class="hljs-subst">${searchController.results.time.toString()}</span> ms'</span>,
              ),
            ),
          ),
        ),
        Expanded(
          child: ListView.builder(
            itemCount: searchController.results.data.length + <span class="hljs-number">1</span>,
            itemBuilder: (context, index) {
              WidgetsBinding.instance.addPostFrameCallback((_) {
                <span class="hljs-keyword">var</span> offset = (searchController.from ?? <span class="hljs-number">0</span>) + searchController.size;
                <span class="hljs-keyword">if</span> (index == offset - <span class="hljs-number">1</span> &amp;&amp; searchController.results.numberOfResults &gt; offset) {
                  searchController.setFrom(offset, options: Options(triggerDefaultQuery: <span class="hljs-keyword">true</span>));
                }
              });

              <span class="hljs-keyword">return</span> index &lt; searchController.results.data.length
                ? Container(
                    margin: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">0.5</span>),
                    padding: <span class="hljs-keyword">const</span> EdgeInsets.fromLTRB(<span class="hljs-number">0</span>, <span class="hljs-number">15</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>),
                    decoration: BoxDecoration(border: Border.all(color: Colors.black26)),
                    height: <span class="hljs-number">200</span>,
                    child: Row(
                      children: [
                        Expanded(
                          flex: <span class="hljs-number">3</span>,
                          child: Card(
                            semanticContainer: <span class="hljs-keyword">true</span>,
                            clipBehavior: Clip.antiAliasWithSaveLayer,
                            child: Image.network(
                              searchController.results.data[index][<span class="hljs-string">"image_medium"</span>],
                              fit: BoxFit.fill,
                            ),
                            elevation: <span class="hljs-number">5</span>,
                            margin: EdgeInsets.all(<span class="hljs-number">10</span>),
                          ),
                        ),
                        Expanded(
                          flex: <span class="hljs-number">7</span>,
                          child: Column(
                            children: [
                              ListTile(
                                title: Text(
                                  searchController.results.data[index][<span class="hljs-string">"original_title"</span>],
                                  style: TextStyle(fontSize: <span class="hljs-number">20.0</span>),
                                ),
                                subtitle: Text(
                                  <span class="hljs-string">'By: <span class="hljs-subst">${searchController.results.data[index][<span class="hljs-string">"authors"</span>]}</span>'</span>,
                                  style: TextStyle(fontSize: <span class="hljs-number">15.0</span>),
                                ),
                              ),
                              Row(
                                children: [
                                  Padding(
                                    padding: <span class="hljs-keyword">const</span> EdgeInsets.fromLTRB(<span class="hljs-number">10</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>),
                                    child: Text(
                                      <span class="hljs-string">'(<span class="hljs-subst">${searchController.results.data[index][<span class="hljs-string">"average_rating"</span>]}</span> avg)'</span>,
                                      style: TextStyle(fontSize: <span class="hljs-number">12.0</span>),
                                    ),
                                  ),
                                ],
                              ),
                              Row(
                                children: [
                                  Padding(
                                    padding: <span class="hljs-keyword">const</span> EdgeInsets.fromLTRB(<span class="hljs-number">27</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>),
                                    child: Text(
                                      <span class="hljs-string">'Pub: <span class="hljs-subst">${searchController.results.data[index][<span class="hljs-string">"original_publication_year"</span>]}</span>'</span>,
                                      style: TextStyle(fontSize: <span class="hljs-number">12.0</span>),
                                    ),
                                  )
                                ],
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  )
                : searchController.requestPending
                  ? Center(child: CircularProgressIndicator())
                  : ListTile(
                      title: Center(
                        child: RichText(
                          text: TextSpan(
                            text: searchController.results.data.length &gt; <span class="hljs-number">0</span>
                              ? <span class="hljs-string">"No more results"</span>
                              : <span class="hljs-string">'No results found'</span>,
                            style: TextStyle(color: Colors.black54, fontSize: <span class="hljs-number">20</span>, fontWeight: FontWeight.bold),
                          ),
                        ),
                      ),
                    );
            },
          ),
        ),
      ],
    );
  }
}
</code></pre>
<p>This code sets up a basic search interface where users can search for books, get autosuggestions, and view detailed results, including images, titles, authors, and publication years.</p>
<p><img src="https://raw.githubusercontent.com/appbaseio/flutter-assets/master/basic.gif" alt="Flutter Search Example UI" class="image--center mx-auto" /></p>
<h4 id="heading-customizing-the-search-experience">Customizing the Search Experience</h4>
<p>Flutter Searchbox offers flexibility in customizing search experiences. You can tailor search widgets, adjust search parameters, and integrate additional features like filters and facets to refine results based on user preferences.</p>
<h4 id="heading-optimizing-performance-and-user-experience">Optimizing Performance and User Experience</h4>
<p>Optimizing your search interface is crucial for delivering a fast and responsive user experience. Consider implementing caching strategies, managing large datasets efficiently, and ensuring the UI remains smooth even under heavy search loads.</p>
<h4 id="heading-summary">Summary</h4>
<p>Building search UIs with Flutter Searchbox 4.0 is efficient and customizable, offering a rich set of features to create a seamless search experience. By following the steps in this guide, you can add powerful search capabilities into your Flutter app.</p>
<p>For more examples with facets, custom Search UI, and maps, explore the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/flutter-searchbox/quickstart/">Flutter Searchbox documentation</a> and start building search-powered apps today.</p>
]]></content:encoded></item><item><title><![CDATA[ReactiveSearch UI kit for Vue 3 - Build intelligent search UIs]]></title><description><![CDATA[ReactiveSearch for Vue 3 is a major upgrade to the UI kit and comes with several enhancements for building intelligent user interfaces and search experiences.
Watch the 1-min introduction video below: 👇
https://youtu.be/kvm-H-1iLms?si=fObbwloJ1Tiba5...]]></description><link>https://blog.reactivesearch.io/reactivesearch-ui-kit-for-vue-3-build-intelligent-search-uis</link><guid isPermaLink="true">https://blog.reactivesearch.io/reactivesearch-ui-kit-for-vue-3-build-intelligent-search-uis</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[openai]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Mon, 18 Dec 2023 14:12:29 GMT</pubDate><content:encoded><![CDATA[<p>ReactiveSearch for Vue 3 is a major upgrade to the UI kit and comes with several enhancements for building intelligent user interfaces and search experiences.</p>
<p>Watch the 1-min introduction video below: 👇</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/kvm-H-1iLms?si=fObbwloJ1Tiba5F6">https://youtu.be/kvm-H-1iLms?si=fObbwloJ1Tiba5F6</a></div>
<p> </p>
<h2 id="heading-new-components">New Components</h2>
<h3 id="heading-a-i-answer">a-i-answer</h3>
<p>The <mark>a-i-answer</mark> UI component enables building a RAG (retrieval augmented generation) based chat experience with AI answering support. See a demo in action over <a target="_blank" href="https://reactivesearch-vue.vercel.app/?path=/story/search-components-aianswer--basic">here</a>. Or read the component's documentation reference over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/vue/search/aianswer/">here</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702907540117/17f69bf1-4c01-4e4d-b644-d3aa6101393a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-search-box">search-box</h3>
<p>The <mark>search-box</mark> UI component is enhanced to support AI Answering, showing FAQs as suggestions, and showing curated suggestions with custom actions. See all of these in action with an extensive search showcase below. Read the component documentation over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/vue/search/searchbox/">here</a>.</p>
<iframe src="https://searchbox-showcase.vercel.app/?library=vue" style="width:100%;height:600px"></iframe>

<h3 id="heading-secure-api">Secure API</h3>
<p>ReactiveSearch Vue v3 uses the ReactiveSearch API to express the search intent. This requires the use of a ReactiveSearch server with two deployment choices: a <a target="_blank" href="https://www.reactivesearch.io/pricing">search cluster</a> and a <a target="_blank" href="https://www.reactivesearch.io/pricing/serverless-search">serverless search</a> mode.</p>
<h3 id="heading-expanded-search-engine-support">Expanded Search Engine Support</h3>
<p>ReactiveSearch UI kit supports Elasticsearch, OpenSearch, OpenAI, Solr and MongoDB Atlas Search as the choices for backend search engines.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Search Engine</td><td>Search Cluster Support</td><td>Serverless Search support</td></tr>
</thead>
<tbody>
<tr>
<td>Elasticsearch</td><td>Yes, fully hosted</td><td>Yes (user connects to their Elasticsearch)</td></tr>
<tr>
<td>OpenSearch</td><td>Yes, fully hosted</td><td>Yes (built-in)</td></tr>
<tr>
<td>OpenAI</td><td>Yes, API support and via pipelines</td><td>Yes, API support via pipelines</td></tr>
<tr>
<td>Solr [Preview]</td><td>-</td><td>Yes (user connects to their Solr cloud cluster)</td></tr>
<tr>
<td>MongoDB Atlas Search [Preview]</td><td>-</td><td>Yes (user connects to their MongoDB Atlas Search cloud)</td></tr>
</tbody>
</table>
</div><p>Elasticsearch and OpenSearch are supported completely. OpenAI is supported with both ReactiveSearch API and with pipelines. Solr and MongoDB Atlas search supports are in preview, we're happy to provide additional support to get you up and running with these engines.</p>
<hr />
<p>Check out the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/vue/overview/quickstart/">Vue developer quickstart guide</a> and <mark>star the project</mark> at <a target="_blank" href="https://github.com/appbaseio/reactivesearch">https://github.com/appbaseio/reactivesearch</a>.</p>
<p>You can <a target="_blank" href="https://blog.reactivesearch.io/reactivesearch-for-react-40-ui-components-for-building-intelligent-uis">check out this post</a> for the ReactiveSearch React v4 release.</p>
]]></content:encoded></item><item><title><![CDATA[ReactiveSearch for React 4.0 - UI components for building intelligent UIs]]></title><description><![CDATA[ReactiveSearch for React 4.0 is a major update for the React UI kit and provides several enhancements for building AI native user interfaces and search experiences.
Watch the 1-min introduction video below: 👇
https://youtu.be/6vDYb8jDs_o
 
New Compo...]]></description><link>https://blog.reactivesearch.io/reactivesearch-for-react-40-ui-components-for-building-intelligent-uis</link><guid isPermaLink="true">https://blog.reactivesearch.io/reactivesearch-for-react-40-ui-components-for-building-intelligent-uis</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[openai]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Wed, 22 Nov 2023 10:47:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1700931102878/ca723676-18e2-42f3-be19-646ebe44b575.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ReactiveSearch for React 4.0 is a major update for the React UI kit and provides several enhancements for building AI native user interfaces and search experiences.</p>
<p>Watch the 1-min introduction video below: 👇</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/6vDYb8jDs_o">https://youtu.be/6vDYb8jDs_o</a></div>
<p> </p>
<h2 id="heading-new-components">New Components</h2>
<h3 id="heading-ai-answer">AI Answer</h3>
<p>The AIAnswer UI component enables building a RAG (retrieval augmented generation) based chat experience with AI answering support. See a demo in action over <a target="_blank" href="https://opensource.appbase.io/playground/?path=/story/search-components-aianswer--basic">here</a>. Or read the component's documentation reference over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/aianswer/">here</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700647761956/7a0289f6-7622-4813-862e-8f4e7a9d9797.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-searchbox">SearchBox</h3>
<p>The SearchBox component is enhanced to support AI Answering, image searching, showing FAQs as suggestions, and showing curated suggestions with custom actions. See all of these in action with an extensive search showcase below. Read the component documentation over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/">here</a>.</p>
<iframe src="https://searchbox-showcase.vercel.app/" style="width:100%;height:600px"></iframe>

<h3 id="heading-reactivecharts">ReactiveCharts</h3>
<p>Embed reactive charts in your search interface. With 5 built-in chart types: <code>pie</code>, <code>bar</code>, <code>scatter</code>, <code>line</code> and <code>histogram</code> supported and use <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/reactivechart/"><code>ReactiveChart</code></a> to support any chart UI using Apache E-charts.</p>
<h3 id="heading-simplified-ssr">Simplified SSR</h3>
<p>With ReactiveSearch v4, configuring SSR is an intuitive experience by removing the requirement of providing the component configuration.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700648711413/fe24d781-61cd-4f06-904e-05a8abefc6c7.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-secure-api">Secure API</h3>
<p>ReactiveSearch v4 uses the ReactiveSearch API introduced in v3 to express the search intent. This requires the use of a ReactiveSearch server with two initial deployment choices: a <a target="_blank" href="https://www.reactivesearch.io/pricing">search cluster</a> and a <a target="_blank" href="https://www.reactivesearch.io/pricing/serverless-search">serverless search</a> mode.</p>
<h3 id="heading-expanded-search-engine-support">Expanded Search Engine Support</h3>
<p>ReactiveSearch UI kit supports Elasticsearch, OpenSearch, OpenAI, Solr and MongoDB Atlas Search as the choices for backend search engines.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Search Engine</td><td>Search Cluster Support</td><td>Serverless Search support</td></tr>
</thead>
<tbody>
<tr>
<td>Elasticsearch</td><td>Yes, fully hosted</td><td>Yes (user connects to their Elasticsearch)</td></tr>
<tr>
<td>OpenSearch</td><td>Yes, fully hosted</td><td>Yes (built-in)</td></tr>
<tr>
<td>OpenAI</td><td>Yes, API support and via pipelines</td><td>Yes, API support via pipelines</td></tr>
<tr>
<td>Solr [Preview]</td><td>-</td><td>Yes (user connects to their Solr cloud cluster)</td></tr>
<tr>
<td>MongoDB Atlas Search [Preview]</td><td>-</td><td>Yes (user connects to their MongoDB Atlas Search cloud)</td></tr>
</tbody>
</table>
</div><p>Elasticsearch and OpenSearch are supported completely. OpenAI is supported with both ReactiveSearch API and with pipelines. Solr and MongoDB Atlas search supports are in preview, we're happy to provide additional support to get you up and running with these engines.</p>
<hr />
<p>I'm excited to see what you build with ReactiveSearch v4! You can check out the <a target="_blank" href="https://bit.ly/reactive-v4">developer quickstart guide</a> and <mark>star the project</mark> at <a target="_blank" href="https://github.com/appbaseio/reactivesearch">https://github.com/appbaseio/reactivesearch</a>.</p>
<p>You can <a target="_blank" href="https://blog.reactivesearch.io/reactivesearch-ui-kit-for-vue-3-build-intelligent-search-uis">check out this post</a> for the ReactiveSearch Vue v3 release.</p>
]]></content:encoded></item><item><title><![CDATA[Build a Context-Aware Site Search with AI integration]]></title><description><![CDATA[It's 2023 and business search engines are innovating alongside top consumer search engines. In this post, we will be using ReactiveSearch's versatile stack for integrating AI and bringing context awareness to site search.

Understanding ReactiveSearc...]]></description><link>https://blog.reactivesearch.io/context-aware-site-search-with-ai</link><guid isPermaLink="true">https://blog.reactivesearch.io/context-aware-site-search-with-ai</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[openai]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[Search Engines]]></category><category><![CDATA[opensearch]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Wed, 06 Sep 2023 16:32:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694013865774/e89b4803-c9a6-4b72-b5c5-3e1b09096441.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It's 2023 and business search engines are innovating alongside top consumer search engines. In this post, we will be using ReactiveSearch's versatile stack for integrating AI and bringing context awareness to site search.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693877572119/e556bf46-2b53-4413-9275-9c5065fb621a.jpeg" alt="ReactiveSearch platform" class="image--center mx-auto" /></p>
<h2 id="heading-understanding-reactivesearch">Understanding ReactiveSearch</h2>
<p>With native connectors for Elasticsearch, OpenSearch, Solr and OpenAI, ReactiveSearch enables businesses to choose an incremental search improvement and adoption strategy over betting on a completely new search solution.</p>
<h3 id="heading-how-it-works">How it works</h3>
<p>Connect search engine index, LLM such as OpenAI's ChatGPT, or integrate a HTTP endpoint as a search data source.</p>
<p>Then, author a pipeline that takes a HTTP request as input, modifies the request by either using ReactiveSearch's pre-built stages or by writing JavaScript, queries search sources via connectors, and then transforms, re-orders and optionally enriches the response which is then served to the client. This orchestration happens in milliseconds, our benchmarks mark a typical pipeline doing the above adding a <strong>~10ms</strong> overhead and can scale up to <strong>300</strong> QPS (that's about ~1MM requests in an hour). V8 engine isolates make a significant contribution as the choice of environment for executing JavaScript ⚡️ fast.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694014144301/a689ed80-e0d3-4986-82fd-e7fcbf2ab8af.jpeg" alt="Pipeline Illustration" class="image--center mx-auto" /></p>
<p>As the above image illustrates, the orchestration of the search pipeline is written as a YAML (or JSON) and the stages each represent a middleware. Here, the <code>authorization</code>, <code>reactiveSearchQuery</code> and <code>elasticsearchQuery</code> are pre-built stages and the user is writing a JavaScript for <code>google_knowledge_graph</code> and <code>merge_response</code> stages, to make a fetch request to Google's Knowledge Graph API and to merge the search engine and KG responses before serving it to the client. Stages that are I/O heavy and independent can be executed asynchronously as is the case here for the KG API call and the Elasticsearch Query stages.</p>
<p>Pipelines come with starter templates for over 10 use cases today and the authoring interface is via Monaco editor (what powers VS Code) with autocompletion and usage tooltip support to help you with the syntax. The JavaScript syntax supported by V8 is very close to the browser JavaScript runtimes -- we will illuminate the usage of all of this with a use-case of building an E-Commerce search pipeline.</p>
<h2 id="heading-crafting-a-context-aware-e-commerce-search"><strong>Crafting a Context-Aware E-Commerce Search</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694014178944/507567ae-a01c-451a-bb78-657b59b59a15.png" alt="Context-Aware Search" class="image--center mx-auto" /></p>
<p>A context-aware E-Commerce search should:</p>
<ul>
<li>Display <strong>Recent</strong> and <strong>Popular</strong> suggestions when no query is entered.</li>
<li>Provide relevant suggestions during active user input.</li>
<li>Offer AI-generated answers to user questions.</li>
<li>Handle typos during product searches.</li>
</ul>
<p>To visualize the end result, refer to the embedded demo below.</p>
<iframe src="https://demo.arcade.software/LR8iySOOLYYUe3iwgfry?embed" style="position:relative;top:0;left:0;width:100%;height:480px;color-scheme:light"></iframe>

<h3 id="heading-search-index">Search Index</h3>
<p>Before we build the search, we will need to index the data. For this use case, we've ingested 100,000 products from different categories through the Best Buy developer API. You likely already have a search index configured in Elasticsearch, OpenSearch or Solr, so we will skip over this part.</p>
<p>We will start with the ReactiveSearch dashboard and take a closer look at both the search index and the <strong>Browse Data</strong> functionality.</p>
<iframe src="https://demo.arcade.software/dOPtIsHS2MuvWIi1ZgyA?embed" style="position:relative;top:0;left:0;width:100%;height:480px;color-scheme:light"></iframe>

<p>At this point, you probably want to <a target="_blank" href="https://dashboard.reactivesearch.io/signup">get a free 14-day evaluation ↗️</a> of the ReactiveSearch platform. Choose <strong>Search Cluster</strong> for a green field project, or choose <strong>Serverless Search</strong> if you're connecting to an existing search index.</p>
<h3 id="heading-building-the-search-pipeline">Building the Search Pipeline</h3>
<p>Let's take a closer look at the process of building a search pipeline. We will start out with a pre-built pipeline template, orchestrate the pipeline stages with JSON and then write some JavaScript for building the context-aware search query.</p>
<iframe src="https://demo.arcade.software/NwNMMVkpqR1cYkkf2PGA?embed" style="position:relative;top:0;left:0;width:100%;height:480px;color-scheme:light"></iframe>

<p>Since we intend to use the no-code UI builder for building the search UI in the following step, we will assume the request payload to be using the <a target="_blank" href="https://docs.reactivesearch.io/docs/search/reactivesearch-api/">ReactiveSearch API</a> - a declarative API for capturing search intent. Pipelines by themselves make no assumptions of the use of ReactiveSearch API, though it certainly helps with being able to use the pre-built stages as well as the no-code UI builder.</p>
<p>The orchestration for the pipeline that we defined is as follows:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"enabled"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Best Buy Search Pipeline"</span>,
    <span class="hljs-attr">"routes"</span>: [
        {
            <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/best-buy-set-pipeline/_reactivesearch"</span>,
            <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>,
            <span class="hljs-attr">"classify"</span>: {
                <span class="hljs-attr">"category"</span>: <span class="hljs-string">"reactivesearch"</span>
            }
        }
    ],
    <span class="hljs-attr">"envs"</span>: {
        <span class="hljs-attr">"index"</span>: [
            <span class="hljs-string">"best-buy-set-2023"</span>
        ]
    },
    <span class="hljs-attr">"stages"</span>: [
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"auth"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"generateRequest"</span>,
            <span class="hljs-attr">"scriptRef"</span>: <span class="hljs-string">"generateRequest"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">true</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"query"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"reactivesearchQuery"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"es_query"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"elasticsearchQuery"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"typo check"</span>,
            <span class="hljs-attr">"scriptRef"</span>: <span class="hljs-string">"checkTypo"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"researchQuery"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"reactivesearchQuery"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">"trigger"</span>: {
                <span class="hljs-attr">"expression"</span>: <span class="hljs-string">"context.envs.research == true"</span>
            }
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"research_es_query"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"elasticsearchQuery"</span>,
            <span class="hljs-attr">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">"trigger"</span>: {
                <span class="hljs-attr">"expression"</span>: <span class="hljs-string">"context.envs.research == true"</span>
            }
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"answerAI"</span>,
            <span class="hljs-attr">"use"</span>: <span class="hljs-string">"AIAnswer"</span>,
            <span class="hljs-attr">"inputs"</span>: {
                <span class="hljs-attr">"topDocsForContext"</span>: <span class="hljs-number">3</span>,
                <span class="hljs-attr">"docTemplate"</span>: <span class="hljs-string">"${source.name}"</span>,
                <span class="hljs-attr">"queryTemplate"</span>: <span class="hljs-string">"Can you tell me about: ${value}"</span>,
                <span class="hljs-attr">"apiKey"</span>: <span class="hljs-string">"{{ OPENAI_API_KEY }}"</span>
            }
        }
    ]
}
</code></pre>
<p>Our first custom script takes the browser query and modifies it to set the search and suggestion queries to use based on the search intent, which is detected based on the presence/absence of user input as well as the specificity of it.</p>
<p>The second custom script for applying typo tolerance is triggered when no hits are returned by the original query.</p>
<p>Below is a template for how a JavaScript stage should be defined.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// your function handler should always be named as handleRequest()</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Accessible variables within the function: context,</span>
    <span class="hljs-comment">// e.g. 1. context.envs contains the envs set by the pipeline and dynamically at runtime</span>
    <span class="hljs-comment">//      2. JSON.parse(context.request.body) provides the JSON of the request body - useful for changes to request body</span>
    <span class="hljs-comment">//      3. JSON.parse(context.response.body) provides the JSON of the request body - useful for enriching the response body</span>
    <span class="hljs-comment">// The function expects a return value of the context if you're making changes to the request or response body or setting a variable</span>
    <span class="hljs-comment">// e.g. return { ...context, myVar: myVar }</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'request body is: '</span>, context.request.body);
    <span class="hljs-keyword">var</span> myVar = <span class="hljs-string">'test'</span>;
    <span class="hljs-comment">// sets myVar at the top-level in the context,</span>
    <span class="hljs-keyword">return</span> { ...context, myVar };
}
</code></pre>
<p>As this template shows, a custom JavaScript stage will define a <code>function handleRequest() {</code> handler that may read the request context, modify it, set a new variable in the context or add console logs.</p>
<p><a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/">Read the pipeline docs</a> for concepts and how-to guides.</p>
<h3 id="heading-building-the-search-ui">Building the search UI</h3>
<p>Now that we have the search pipeline configured, let's consume it with a search UI. For this use case, we will choose to do this with the no-code <strong>UI builder</strong>. <a target="_blank" href="https://www.reactivesearch.io/product/search-ui">ReactiveSearch UI kit</a> is also a good choice for building a React, Vue or Flutter based search UI.</p>
<iframe src="https://demo.arcade.software/wRjrHtmRRgHJQir56AcO?embed" style="position:relative;top:0;left:0;width:100%;height:480px;color-scheme:light"></iframe>

<h2 id="heading-summary">Summary</h2>
<p>ReactiveSearch offers a versatile stack for incrementally adopting AI to an existing business search offering an alternative to betting on new solutions. It does this by providing connectors to Elasticsearch, OpenSearch, Solr and OpenAI. In this post, we went over setting up the search index, browsing the data, building the search pipeline for context-aware search, and finally building the search UI.</p>
<p><a target="_blank" href="https://www.reactivesearch.io/how-to/build-search-ui">Browse other interactive use-cases of ReactiveSearch over here ↗️</a></p>
<p><a target="_blank" href="https://dashboard.reactivesearch.io/signup">Get a free 14-day evaluation ↗️</a> of the ReactiveSearch platform</p>
]]></content:encoded></item><item><title><![CDATA[Top 10 Applications of SearchBox]]></title><description><![CDATA[At ReactiveSearch, we're big fans of search user experience, and with the emerging use cases around web search and generative AI, it's time to re-think the SearchBox (and Search UIs as a whole) user experience that our customers can use.
We've curate...]]></description><link>https://blog.reactivesearch.io/top-10-applications-of-searchbox</link><guid isPermaLink="true">https://blog.reactivesearch.io/top-10-applications-of-searchbox</guid><category><![CDATA[Search Engines]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[React]]></category><category><![CDATA[Vue.js]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Fri, 07 Jul 2023 15:50:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1688744203917/fa8df375-667d-412f-a331-fb69dc11e1f7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>At ReactiveSearch, we're big fans of search user experience, and with the emerging use cases around web search and generative AI, it's time to re-think the SearchBox (and Search UIs as a whole) user experience that our customers can use.</p>
<p>We've curated the top 10 applications of SearchBox along with interactive consumer-grade search demos in React and Vue.</p>
<h3 id="heading-1-voice-search">1. Voice Search</h3>
<p>This example demonstrates how to enable voice search by setting the <code>showVoiceSearch</code> prop to true. This adds a microphone icon to the search box, allowing users to search using their voice instead of typing. This can be especially useful for users on mobile devices or for those with accessibility needs.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithVoiceSearch?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithVoiceSearch?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for Voice Search</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/voice-search?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/voice-search?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for Voice Search</p>
<h3 id="heading-2-auto-suggestions"><strong>2. Auto suggestions</strong></h3>
<p>This example demonstrates how to enable auto-suggestions. When the <code>autosuggest</code> prop is set to <code>true</code>, the SearchBox will query the search index for suggestions based on the user's input.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithAutosuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithAutosuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for Auto suggestions</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/auto-suggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/auto-suggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for Auto suggestions</p>
<h3 id="heading-3-auto-suggestions-popular-recent-suggestions">3. Auto Suggestions + Popular + Recent Suggestions</h3>
<p>This example shows how to enable popular and recent suggestions along with auto-suggestions based on a search index. Both popular and recent suggestions are powered by ReactiveSearch analytics captured from end-users and offer search time configurations for setting index, and minimum thresholds for characters, hits and # of times a query is searched.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithPopularRecentSuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithPopularRecentSuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/simple-search?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/simple-search?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo</p>
<h3 id="heading-4-instant-search">4. Instant Search</h3>
<p>This example demonstrates how to enable instant search by setting the <code>autosuggest</code> prop to <code>false</code>. With instant search, users can instantly see the effect of searching at each keystroke on the facets and results.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithInstantSearch?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithInstantSearch?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for Instant Search</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/instant-search?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/instant-search?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for Instant Search</p>
<h3 id="heading-5-instant-search-suggestions-as-pills">5. Instant Search + Suggestions As Pills</h3>
<p>There are times when you want to get the best of both worlds: Display results instantly along with showing possible autocomplete extensions to the user. This example uses controlled behavior with a custom rendering of suggestions to achieve this.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/s/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithPillSuggestions?from-embed">https://codesandbox.io/s/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithPillSuggestions?from-embed</a></div>
<p> </p>
<p>React code + demo</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/s/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/pill-suggestions?from-embed">https://codesandbox.io/s/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/pill-suggestions?from-embed</a></div>
<p> </p>
<p>Vue code + demo</p>
<h3 id="heading-6-keyboard-shortcut-to-focus">6. Keyboard Shortcut to Focus</h3>
<p>Configure familiar keyboard shortcuts and keyboard accessibility for your SaaS power users.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithKeyboardShortcuts?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithKeyboardShortcuts?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for keyboard shortcuts</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/focus-shortcuts?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;view=preview">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/focus-shortcuts?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;view=preview</a></div>
<p> </p>
<p>Vue code + demo for keyboard shortcuts</p>
<h3 id="heading-7-featured-suggestions">7. Featured Suggestions</h3>
<p>Featured (aka curated) suggestions that show up as default suggestions, and provide for rich interactive behavior such as custom styling, navigate on click and more. One can set featured suggestions using the point-and-click interface of the ReactiveSearch dashboard and then display them alongside auto-suggestions, and popular or recent suggestions.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithFeaturedSuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/SearchBoxWithFeaturedSuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for Featured Suggestions</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-000/packages/vue/examples/search-showcase/featured-suggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-000/packages/vue/examples/search-showcase/featured-suggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for Featured Suggestions</p>
<h3 id="heading-8-question-answering-with-aianswer">8. Question Answering with AIAnswer</h3>
<p>This is a simple AI question answering UI that uses the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/aianswer/">AIAnswer component</a> along with SearchBox and ReactiveList.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/AIAnswer?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Freact-showcase-examples/packages/web/examples/AIAnswer?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for AIAnswer</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/ai-answer-simple?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/ai-answer-simple?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for AIAnswer</p>
<h3 id="heading-9-question-answering-with-follow-up-questions">9. Question Answering with follow-up questions</h3>
<p>Same same as question answering with AIAnswer component but also allow your users to ask follow-up questions. AI Answer component supports custom rendering allowing you as a developer to provide a rich user experience while leveraging ReactiveSearch's query structure.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/awesome-reactivesearch/qna-rick-and-morty/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/awesome-reactivesearch/qna-rick-and-morty/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + demo for question answering with follow-up questions support</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/movie-search-ai-demo?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/movie-search-ai-demo?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for question answering with follow-up questions support</p>
<p><mark>The SearchBox component auto-adjusts its height to up to three rows to allow for a question.</mark></p>
<h3 id="heading-10-ai-answering-integrated-into-searchbox">10. AI Answering integrated into SearchBox</h3>
<p>SearchBox as a Swiss army knife where your users get an answer along with suggestions. Props allow configuring how this can work seamlessly.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/awesome-reactivesearch/ask-reactivesearch/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/awesome-reactivesearch/ask-reactivesearch/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>React code + example for SearchBox showing an AI Answer</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/searchbox-inline-ai-response?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/feat%2Fjust-for-csb-001/packages/vue/examples/search-showcase/searchbox-inline-ai-response?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Vue code + demo for SearchBox showing an AI Answer</p>
<h2 id="heading-summary">Summary</h2>
<p>In this post, we just showcased the top 10 applications of SearchBox UI to create an engaging user experience.</p>
<p><a target="_blank" href="https://github.com/appbaseio/reactivesearch">SearchBox and ReactiveSearch UIs are open-source (Apache 2.0 licensed)</a> and designed to work with a ReactiveSearch cloud instance.</p>
<blockquote>
<p>ReactiveSearch cloud is a reverse proxy for your favorite search engines and provides a no-code control plane to author search pipelines (connect your favorite search engines, use pre-built stages or add JavaScript processing stages), build and ship UIs and get insights into your search.</p>
</blockquote>
<p><a target="_blank" href="https://dashboard.reactivesearch.io/signup?utm_source=blog&amp;utm_medium=blog&amp;utm_campaign=reactivesearchio">Sign up for a free 14-day trial of ReactiveSearch cloud</a></p>
<p>Stay tuned for more updates and features in the ReactiveSearch UI library. Happy shipping!</p>
]]></content:encoded></item><item><title><![CDATA[Supercharge Customer Support with Fact-Based Question Answering with ChatGPT and Elasticsearch]]></title><description><![CDATA[ChatGPT, powered by OpenAI's advanced GPT-3.5 and GPT-4 models, has been making waves in the tech industry. This language model, trained on a vast corpus of data, has demonstrated its utility in answering questions and providing information across a ...]]></description><link>https://blog.reactivesearch.io/question-answering-with-chatgpt-and-elasticsearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/question-answering-with-chatgpt-and-elasticsearch</guid><category><![CDATA[chatgpt]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[opensearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Wed, 31 May 2023 18:19:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1685541982701/5e0d0c57-8786-4adb-8391-b14fb01ca9b1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ChatGPT, powered by OpenAI's advanced GPT-3.5 and GPT-4 models, has been making waves in the tech industry. This language model, trained on a vast corpus of data, has demonstrated its utility in answering questions and providing information across a broad spectrum of topics, including technical documentation.</p>
<p>In this article, I explore how ChatGPT can be used for documentation and knowledge base search in user and customer support scenarios, allowing users to find answers to their questions in a conversational way. However, it's important to note that ChatGPT has its limitations, especially regarding its knowledge cutoff. It can't access data beyond 2021, so any changes or updates to your documentation after this date won't be reflected in its responses.</p>
<p>This limitation means that certain queries related to your documentation may result in outdated or incorrect answers, as ChatGPT doesn't have up-to-date information beyond its cutoff date of September 2021. In this article, I'll show you how to overcome this limitation of ChatGPT by integrating context from a search engine to ensure more accurate responses.</p>
<p>Here's the application I'm going to build:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/ask-reactivesearch/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-ltm-qa">LTM - QA</h2>
<p>ChatGPT works as a GQA (Generative Question Answering) system. A basic GQA system only requires a user text query and a large language model (LLM).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680753794101/71dbbbcd-9517-43f8-bc06-a6202b813ffc.png" alt class="image--center mx-auto" /></p>
<p>I enhance GQA with LTM-QA, which stands for Long-Term Memory Question Answering. LTM-QA allows machines to store information for a long time and recall it when needed. This improves both precision and recall - a win-win situation.</p>
<p>The innovative idea here is that I use BM-25 (TF/IDF) relevance instead of requiring data vectorization, although this is also possible - a post on the same is forthcoming. Elasticsearch and OpenSearch (the Apache 2.0 derivative of the former) are the go-to search engines when it comes to relevant text search with filtering capabilities. If your search is used by users who are familiar with the data, then BM-25-based relevance offers instant results at scale and query remixing capabilities that are very nascent with vector search today.</p>
<p>The key idea is to combine the power of ChatGPT and state-of-the-art search engines such as ElasticSearch and OpenSearch to create a robust search experience that's relevant, accurate, concise, and evolves as per the users' prompts.</p>
<blockquote>
<p><strong>🤔 Elasticsearch and OpenSearch are the standards (10x adoption, work at scale, and have good Dx) where developers already have their long-term search data, and they support BM-25 and vector search-based relevance models.</strong></p>
</blockquote>
<h2 id="heading-reactivesearch-is-a-cloudflare-for-search-engines"><strong>ReactiveSearch is a CloudFlare for Search Engines</strong></h2>
<p><a target="_blank" href="http://ReactiveSearch.io"><strong>ReactiveSearch.io</strong></a> offers a supercharged search experience for creating the most demanding app search experiences with a no-code UI builder and search relevance control plane, AI Answering support (which I will be using here), pipelines to create an extendible search backend, caching, and search insights.</p>
<p>There are additional open-source and hosted tools that simplify the process of building search experiences. You can:</p>
<ul>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/data/import/">Import your data</a> from various sources via the dashboard or CLI or REST APIs,</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/search/relevancy/">Test search relevancy visually</a>,</p>
</li>
<li><p>Build production-grade search UIs using:</p>
<ol>
<li><p><a target="_blank" href="https://docs.appbase.io/docs/reactivesearch/gettingstarted/">UI component libraries</a> that are available for React, Vue, React Native, Flutter, and vanilla JavaScript,</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/api/rest/overview/">A declarative REST API</a> or</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/ui-builder/search/">NoCode search UI builder</a>.</p>
</li>
</ol>
</li>
<li><p>Get out-of-the-box <a target="_blank" href="https://docs.reactivesearch.io/docs/analytics/overview/">actionable analytics</a> on top searches, no-result searches, slow queries, and more,</p>
</li>
<li><p>Get improved search performance and throughput with <a target="_blank" href="https://docs.reactivesearch.io/docs/speed/cache-management/">application layer caching</a>,</p>
</li>
<li><p>Build access-controlled search experiences with built-in Basic Auth or JWT-based authentication, read/write access keys with granular ACLs, field level security, IP-based rate limits, and time to live - <a target="_blank" href="https://docs.reactivesearch.io/docs/security/credentials/">read more over here</a>.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680754354498/1448d04f-2dd6-4b2a-a5a1-5e59cabb39d0.png" alt="ReactiveSearch overview diagram" class="image--center mx-auto" /></p>
<blockquote>
<p>With recent upgrades, ReactiveSearch supports all major search engine backends like OpenSearch, Mongo Atlas, Solr, OpenAI</p>
<p>Visit the website <a target="_blank" href="https://www.reactivesearch.io/">here</a>.</p>
</blockquote>
<h2 id="heading-data-prep">Data Prep</h2>
<p>As Elasticsearch works with JSON natively, I will create a search index using the JSON data of ReactiveSearch's documentation pages.</p>
<p>A document looks as below:</p>
<pre><code class="lang-json">{
    meta_title: <span class="hljs-string">"ReactiveComponent"</span>,
    meta_description: <span class="hljs-string">"ReactiveComponent lets you connect any React UI component with an Elasticsearch query or an aggregation seamlessly."</span>,
    keywords: [<span class="hljs-string">"react"</span>, <span class="hljs-string">"web"</span>, <span class="hljs-string">"reactivesearch"</span>, <span class="hljs-string">"ui"</span>],
    heading: <span class="hljs-string">"Usage"</span>,
    tokens: <span class="hljs-string">"ReactiveComponent lets you connect any React UI component with an Elasticsearch query or an aggregation seamlessly. It can be used as a standalone component to integrate Elasticsearch queries into your frontend UI declaratively. It can also be used in conjunction with other ReactiveSearch components. Read more here."</span>
}
</code></pre>
<p>The indexing structure I've decided to use here treats each section of a page as a separate document. The following image shows the fields mapped to the documentation page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680756244531/2e356718-5f3f-43be-bce7-a352bcf715fa.png" alt class="image--center mx-auto" /></p>
<p>For your dataset, you can index using the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/importing/#importing-custom-data"><strong>ReactiveSearch dashboard's importer</strong></a> interface (which supports JSON, JSONL, and CSV formats) or with the <a target="_blank" href="https://docs.reactivesearch.io/docs/data/import/#rest-api"><strong>REST API</strong></a>.</p>
<h2 id="heading-building-the-search-ui"><strong>Building the Search UI</strong></h2>
<p>All the code I'm going to share is located inside the <a target="_blank" href="https://github.com/awesome-reactivesearch/ask-reactivesearch"><strong>ask-reactivesearch</strong></a> repo. If you're new to it, I recommend going through the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/quickstart/"><strong>quick start guide</strong></a> of the ReactiveSearch UI library. I'll also introduce a new component called <code>AIAnswer</code> which creates an experience similar to ChatGPT.</p>
<h3 id="heading-configuring-reactivebase">Configuring ReactiveBase</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.jsx</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">app</span>=<span class="hljs-string">"reactivesearch_docs_v2"</span>
      <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
      <span class="hljs-attr">theme</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">typography:</span> {
          <span class="hljs-attr">fontFamily:</span> "<span class="hljs-attr">monospace</span>",
          <span class="hljs-attr">fontSize:</span> "<span class="hljs-attr">16px</span>",
        },
      }}
      <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">true</span>
      }}
    &gt;</span>
      {/*All components go here*/}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Main</span> /&gt;</span></span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>You can read about the props we're setting in more detail in the documentation reference over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/reactivebase/#props">here</a>.</p>
<h3 id="heading-adding-searchbox-component">Adding SearchBox Component</h3>
<pre><code class="lang-javascript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span> {<span class="hljs-attr">...config</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"search"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">{[</span>
          {
            <span class="hljs-attr">field:</span> "<span class="hljs-attr">keywords</span>",
            <span class="hljs-attr">weight:</span> <span class="hljs-attr">4</span>,
          },
          {
            <span class="hljs-attr">field:</span> "<span class="hljs-attr">heading</span>",
            <span class="hljs-attr">weight:</span> <span class="hljs-attr">1</span>,
          },
          {
            <span class="hljs-attr">field:</span> "<span class="hljs-attr">tokens</span>",
            <span class="hljs-attr">weight:</span> <span class="hljs-attr">1</span>,
          },
          {
            <span class="hljs-attr">field:</span> "<span class="hljs-attr">meta_title</span>",
            <span class="hljs-attr">weight:</span> <span class="hljs-attr">2</span>,
          },
        ]}
        <span class="hljs-attr">distinctField</span>=<span class="hljs-string">"meta_title.keyword"</span>
        <span class="hljs-attr">highlight</span>=<span class="hljs-string">{false}</span>
        <span class="hljs-attr">size</span>=<span class="hljs-string">{5}</span>
        <span class="hljs-attr">autosuggest</span>=<span class="hljs-string">{true}</span>
        <span class="hljs-attr">URLParams</span>
        <span class="hljs-attr">showClear</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>I'm going to create a <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/"><code>SearchBox</code></a> component with a specific configuration. I've set up the <code>dataField</code> property to prioritize <code>keywords</code>, <code>heading</code>, <code>tokens</code> and <code>meta_title</code>. I figured out the best weights for these fields through a bit of trial and error.</p>
<p>I also want to show some FAQs, so I'll create custom suggestions. You can check out the code for the <a target="_blank" href="https://github.com/awesome-reactivesearch/ask-reactivesearch/blob/step-1/src/App.jsx#L79"><strong>render function on Github</strong></a> and see the result in the Codesandbox below.</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/ask-reactivesearch/tree/step-1/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h3 id="heading-rendering-ai-response">Rendering AI Response</h3>
<p>There's one last step I want to share with you. It's about choosing to get an AI response for your search query right inside the SearchBox component. All I have to do is set <code>enableAI</code> to true, and just like that, I have a working AI searchbox. Now, since I'm creating suggestions myself, I also choose to render the AI response. The response and other properties come as parameters of my render function.</p>
<p>Adding an AI config is completely up to you. If you're interested in fine-tuning the precision of your AI response, it can be quite handy. You can find more details in the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/#aiconfig">documentation</a>.</p>
<pre><code class="lang-javascript">&lt;SearchBox
  enableAI
  AIConfig={{
    <span class="hljs-attr">docTemplate</span>:
      <span class="hljs-string">"title is '${source.title}', page content is '${source.tokens}', URL is https://docs.reactivesearch.io${source.url}"</span>,
    <span class="hljs-attr">queryTemplate</span>:
      <span class="hljs-string">"Answer the query: '${value}', cite URL in your answer from the context"</span>,
    <span class="hljs-attr">topDocsForContext</span>: <span class="hljs-number">3</span>,
  }}
  AIUIConfig={{
    <span class="hljs-attr">askButton</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">renderAskButton</span>: <span class="hljs-function">(<span class="hljs-params">clickHandler</span>) =&gt;</span> {
      <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ask-ai-button"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{clickHandler}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"button-emoji"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSvg</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"button-text"</span>&gt;</span>{"Ask AI"}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
      );
    },
  }}
  render={<span class="hljs-function">(<span class="hljs-params">{
          downshiftProps: {
            isOpen,
            getItemProps,
            highlightedIndex,
            selectedItem,
          },
          AIData: { answer: aiAnswer, isAILoading },
          data,
          loading,
        }</span>) =&gt;</span> {
    <span class="hljs-comment">/* Rendering SearchBox with JSX */</span>
  }}
/&gt;;
</code></pre>
<p>The final app appears as follows:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/ask-reactivesearch/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<p>Try searching for your ReactiveSearch questions and share your experience.</p>
<h2 id="heading-summary">Summary</h2>
<p>This blog post discusses the integration of ChatGPT and Elasticsearch for fact-based question-answering for documentation. It begins by introducing ChatGPT, a language model developed by OpenAI, and its application in answering questions and providing information on a wide range of topics, including technical documentation. The post then delves into the limitations of ChatGPT, particularly its inability to access data beyond 2021, and proposes a solution to this issue by integrating it with Elasticsearch, a search engine that provides context for more accurate responses.</p>
<p>The post provides a detailed walkthrough of building an application that leverages ChatGPT and Elasticsearch. It introduces LTM-QA (Long-Term Memory Question Answering), a system that enhances GQA (Generative Question Answering) by storing information for extended periods and recalling it as needed. The integration of BM-25 (TF/IDF) relevance with Elasticsearch and OpenSearch is also discussed.</p>
<p>The <a target="_blank" href="https://github.com/awesome-reactivesearch/ask-reactivesearch">source code</a> for the application is open-source, and you can adapt it to build your own AI answering search UI with ReactiveSearch.</p>
]]></content:encoded></item><item><title><![CDATA[AI Answer Capabilities with ChatGPT and Elasticsearch]]></title><description><![CDATA[Overview
Learn how to build an AI Answer interface for your own domain with this follow-along post - where we use ElasticSearch and ChatGPT together to build an AI Answering system with high precision (no hallucinations), is always based on facts fro...]]></description><link>https://blog.reactivesearch.io/ai-answer-capabilities-with-chatgpt-and-elasticsearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/ai-answer-capabilities-with-chatgpt-and-elasticsearch</guid><category><![CDATA[chatgpt]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[opensearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Shubham Shah]]></dc:creator><pubDate>Wed, 10 May 2023 11:50:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1683884307787/03c7e441-3612-4051-80e8-097b891c6ab3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p>Learn how to build an AI Answer interface for your own domain with this follow-along post - where we use ElasticSearch and ChatGPT together to build an AI Answering system with high precision (no hallucinations), is always based on facts from your domain (instead of outdated facts it may have) and leverages Elasticsearch as a search engine effectively to filter and show results along-side the AI Answer.</p>
<p>To keep the user interface configurable for your use-case, we will use the ReactiveSearch UI kit's (Apache 2.0 licensed) <code>AIAnswer</code> component here. This is available for both React and Vue today.</p>
<h2 id="heading-ltm-qa">LTM - QA</h2>
<p>ChatGPT is a GQA (Generative Question Answering) system. The most <em>straightforward</em> GQA system requires nothing more than a user text query and a large language model (LLM).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680753794101/71dbbbcd-9517-43f8-bc06-a6202b813ffc.png" alt class="image--center mx-auto" /></p>
<p>We enhance GQA with LTM-QA, which stands for Long-Term Memory Question Answering. LTM-QA enables machines to store information for an extended period and recall it as needed. This improves both precision and recall - so a win-win.</p>
<p>The novel idea here is that we make use of BM-25 (TF/IDF) relevance instead of requiring vectorization of data, although this is also possible - a post on the same is coming soon. Elasticsearch and OpenSearch (which is the Apache 2.0 derivative of the former) are the search engines to beat when it comes to relevant text search with filtering capabilities. If your search is used by users who know the data well, then BM-25 based relevance offers instant results at scale and query remixing capabilities that are very nascent with vector search today.</p>
<p>The simple idea is to combine the power of ChatGPT and standard search engine tools like <code>ElasticSearch</code> and <code>OpenSearch</code> to finally come up with a powerful search experience that's relevant, accurate, to the point, and evolving as per the users' prompts.</p>
<blockquote>
<p>🤔 Elasticsearch and OpenSearch are the standards (10x adoption, work at scale, and have good Dx) where developers already likely have their long-term search data, also they both support BM-25 and vector search.</p>
</blockquote>
<h2 id="heading-reactivesearch-is-the-cloudflare-for-search-engines">ReactiveSearch is the CloudFlare for Search Engines</h2>
<p><a target="_blank" href="http://ReactiveSearch.io">ReactiveSearch.io</a> provides a supercharged search experience for creating the most demanding app search experiences with a no-code UI builder and search relevance control plane, AI Answering support (what we will be using over here), pipelines to create an extendible search backend, caching and search insights.</p>
<p>There are additional open-source and hosted tools that simplify the process of building search experiences. You can:</p>
<ul>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/data/import/">Import your data</a> from various sources via the dashboard or CLI or REST APIs,</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/search/relevancy/">Test search relevancy visually</a>,</p>
</li>
<li><p>Build production-grade search UIs using:</p>
<ol>
<li><p><a target="_blank" href="https://docs.appbase.io/docs/reactivesearch/gettingstarted/">UI component libraries</a> that are available for React, Vue, React Native, Flutter, and vanilla JavaScript,</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/api/rest/overview/">A declarative REST API</a> or</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/ui-builder/search/">NoCode search UI builder</a>.</p>
</li>
</ol>
</li>
<li><p>Get out-of-the-box <a target="_blank" href="https://docs.reactivesearch.io/docs/analytics/overview/">actionable analytics</a> on top searches, no-result searches, slow queries, and more,</p>
</li>
<li><p>Get improved search performance and throughput with <a target="_blank" href="https://docs.reactivesearch.io/docs/speed/cache-management/">application layer caching</a>,</p>
</li>
<li><p>Build access-controlled search experiences with built-in Basic Auth or JWT-based authentication, read/write access keys with granular ACLs, field level security, IP-based rate limits, and time to live - <a target="_blank" href="https://docs.reactivesearch.io/docs/security/credentials/">read more over here</a>.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680754354498/1448d04f-2dd6-4b2a-a5a1-5e59cabb39d0.png" alt="ReactiveSearch overview diagram" class="image--center mx-auto" /></p>
<h2 id="heading-dataset-and-relevant-questions">Dataset and relevant questions</h2>
<p>To make a great UI we should have a good dataset. We would be using a dataset of dialogues from the popular TV Show "Rick and Morty". We would also need an index to store the data.</p>
<p>You can set up and install an Elasticsearch server by following the <a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html"><strong>official installation guide</strong></a>, or you can create a free account at <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a> which provides Elasticsearch hosting as a service and is easy to use. For simplicity, we will be using <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a> service to get started.</p>
<p>I’ve already created an index with the dataset. You can check out the dataset from above <a target="_blank" href="https://dejavu.appbase.io/?appname=rick-and-morty&amp;url=https%3A%2F%2F2ca5a5864025%3A12187bc6-44bc-4b3a-9683-80c18cff6312%40appbase-demo-ansible-abxiydt-arc.searchbase.io&amp;mode=edit"><strong>over here in the data browser tool Dejavu</strong></a>, which is built by <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a>.</p>
<p>We also have a long list of questions in JSON format with the following structure. Each question is linked to an episode, which is useful for finding documents in the above index for a particular episode.</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"question"</span>: <span class="hljs-string">"How many Ricks and Mortys are there?"</span>,
    <span class="hljs-attr">"episode"</span>: <span class="hljs-string">"A Rickle in Time"</span>
  },
  {
    <span class="hljs-attr">"question"</span>: <span class="hljs-string">"What's the most bizarre time paradox Rick and Morty encounter?"</span>,
    <span class="hljs-attr">"episode"</span>: <span class="hljs-string">"A Rickle in Time"</span>
  },
  {
    <span class="hljs-attr">"question"</span>: <span class="hljs-string">"What's the funniest time-related mishap?"</span>,
    <span class="hljs-attr">"episode"</span>: <span class="hljs-string">"A Rickle in Time"</span>
  },
]
</code></pre>
<p>You can also have a separate index for the questions if you have a large list which makes the search slow.</p>
<h2 id="heading-building-search-ui">Building Search UI</h2>
<p>All the code we are going to show is present inside the <a target="_blank" href="https://github.com/awesome-reactivesearch/qna-rick-and-morty">qna-rick-and-morty</a> repository. We also recommend going through the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/quickstart/">quick start guide</a> of the Reactivesearch UI library to become familiar with it. The guide will not only show you how to set up a boilerplate React app, but also how to set up the <code>@appbaseio/reactivesearch</code> library. We will primarily modify the <code>src/App.jsx</code> file, which will also introduce you to <code>ReactiveBase</code> and <code>SearchBox</code> component that we will be using in this tutorial. Additionally, we will introduce a new component called <code>AIAnswer</code>, which creates a ChatGPT-like search experience.</p>
<h3 id="heading-configuring-reactivebase">Configuring ReactiveBase:</h3>
<p>We would want to connect to <code>rick-and-morty</code> index. For that we configure <code>ReactiveBase</code> component as follows:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.jsx</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">app</span>=<span class="hljs-string">"reactivesearch_docs_v2"</span>
      <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
      <span class="hljs-attr">theme</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">typography:</span> {
          <span class="hljs-attr">fontFamily:</span> "<span class="hljs-attr">monospace</span>",
          <span class="hljs-attr">fontSize:</span> "<span class="hljs-attr">16px</span>",
        },
      }}
      <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">false</span>,
        <span class="hljs-attr">userId:</span> "<span class="hljs-attr">jon</span>",
      }}
    &gt;</span>
      {/*All components go here*/}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Main</span> /&gt;</span></span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-adding-searchbox">Adding SearchBox:</h3>
<p>We would make a <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/"><code>SearchBox</code></a> component with the following config. You would be familiar with the props from the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/quickstart/">quick start guide</a>. All the props are also listed in the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/">documentation</a>.</p>
<pre><code class="lang-javascript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      {<span class="hljs-attr">...config</span>}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">{[</span>"<span class="hljs-attr">episode_name</span>", "<span class="hljs-attr">name</span>", "<span class="hljs-attr">line</span>"]}
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">{SEARCH_COMPONENT_ID}</span>
        <span class="hljs-attr">size</span>=<span class="hljs-string">{5}</span>
        <span class="hljs-attr">autosuggest</span>=<span class="hljs-string">{true}</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>To search through the list of questions and show suggestions when the user types, we will be using a library called <code>fuse.js</code>. Fuse.js is a lightweight fuzzy search library that is appropriate for the size of our data. If the data were larger, we would have created an index for better performance.</p>
<p>You need to install it first. The latest version at the time of writing is <code>v6.6.2</code>.</p>
<pre><code class="lang-javascript">yarn add fuse.js
</code></pre>
<p>You can look at the documentation for Fuse.js though the usage is quite simple. You just have to pass data to <code>Fuse</code> instance and then call the search method passing your <code>searchQuery</code>. Note that we append the episode name in the question itself which would be used to get the documents filtered by the episode.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.js</span>
<span class="hljs-comment">// questions: [{question, episode}]</span>
<span class="hljs-keyword">const</span> fuse = <span class="hljs-keyword">new</span> Fuse(
  questions.map(<span class="hljs-function">(<span class="hljs-params">q</span>) =&gt;</span> ({
    ...q,
    <span class="hljs-attr">question</span>: q.episode
      ? <span class="hljs-string">`<span class="hljs-subst">${q.question.replace(<span class="hljs-regexp">/\?$/</span>, <span class="hljs-string">""</span>)}</span> in "<span class="hljs-subst">${q.episode}</span>"?`</span>
      : q.question,
  })),
  { <span class="hljs-attr">keys</span>: [<span class="hljs-string">"question"</span>] }
);
<span class="hljs-keyword">const</span> filteredData = fuse.search(searchQuery).map(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> ({
  <span class="hljs-attr">value</span>: res.item.question,
  <span class="hljs-attr">episode</span>: res.item.episode,
  <span class="hljs-attr">idx</span>: res.refIndex,
}));
</code></pre>
<p>We would then render this <code>filteredData</code> using the custom render.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span> {<span class="hljs-attr">...configProps</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
        {<span class="hljs-attr">...previousProps</span>}
        <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span>
          <span class="hljs-attr">downshiftProps:</span> {
            <span class="hljs-attr">isOpen</span>,
            <span class="hljs-attr">getItemProps</span>,
            <span class="hljs-attr">highlightedIndex</span>,
            <span class="hljs-attr">selectedItem</span>,
          },
          <span class="hljs-attr">data</span>,
          <span class="hljs-attr">value:</span> <span class="hljs-attr">searchQuery</span>,
        }) =&gt;</span> {
          const fuse = new Fuse(
            questions.map((q) =&gt; ({
              ...q,
              question: q.episode
                ? `${q.question.replace(/\?$/, "")} in "${q.episode}"?`
                : q.question,
            })),
            { keys: ["question"] }
          );
          const filteredData = fuse.search(searchQuery).map((res) =&gt; ({
            value: res.item.question,
            episode: res.item.episode,
            idx: res.refIndex,
          }));

          return isOpen ? (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">styles.suggestions</span>}`}&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                {filteredData.length ? (
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">bg-gray</span> <span class="hljs-attr">p-2</span> <span class="hljs-attr">m-0</span> ${<span class="hljs-attr">styles.suggestionHeading</span>}`}&gt;</span>
                    Try below suggestions
                  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                ) : null}
                {filteredData.length ? (
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    {filteredData.map((item, index) =&gt;
                      index <span class="hljs-tag">&lt; <span class="hljs-attr">6</span> ? (
                        &lt;<span class="hljs-attr">div</span>
                          /* <span class="hljs-attr">eslint-disable-next-line</span> <span class="hljs-attr">react</span>/<span class="hljs-attr">no-array-index-key</span> */
                          <span class="hljs-attr">key</span>=<span class="hljs-string">{item.idx}</span>
                          {<span class="hljs-attr">...getItemProps</span>({
                            <span class="hljs-attr">item</span>,
                          })}
                          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${
                            <span class="hljs-attr">highlightedIndex</span> === <span class="hljs-string">index</span>
                              ? <span class="hljs-attr">styles.activeSuggestion</span>
                              <span class="hljs-attr">:</span> <span class="hljs-attr">styles.suggestion</span>
                          } 
                                  ${
                                    <span class="hljs-attr">selectedItem</span> &amp;&amp;
                                    <span class="hljs-attr">selectedItem.value</span> === <span class="hljs-string">item.value</span>
                                      ? <span class="hljs-attr">styles.selectedSuggestion</span>
                                      <span class="hljs-attr">:</span> ""
                                  }
                                  `}
                        &gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"clipText"</span>&gt;</span>{item.value}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      ) : null
                    )}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                ) : null}
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          ) : null;
        }}
      /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-adding-the-aianswer-component">Adding the AIAnswer Component</h3>
<p><code>AIAnswer</code> is an AI-driven answer UI component that interacts with a dataset to provide context-aware and relevant answers based on user inputs. It employs machine learning to comprehend user questions and retrieve the most pertinent information. The component can be used to supply answers to common questions related to a specific topic, offer support and help by addressing user queries, and create a knowledge base or FAQ section for your website.</p>
<p>Learn more about the AIAnswer component over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/aianswer/">here</a>.</p>
<p>The AIAnswer component adheres to the same principles as other ReactiveSearch components. It has a <code>react</code> prop that lists dependencies. We will add the <code>SearchBox</code> component as a dependency so that when we hit Enter and the SearchBox triggers a query, the AIAnswer component will display the relevant answer.</p>
<p>There is an additional prop called <code>AIConfig</code>, which allows configuring the default prompt and parameters sent to ChatGPT. Referring to the diagram in the "Let's Visualize" section, we feed documents from Elasticsearch to ChatGPT. These documents are the top results found by performing a search (the suggestions seen in the <code>SearchBox</code> component). The format is determined by <code>docTemplate</code> prop, where we can specify any field from the search index (<code>rick-and-morty</code> in this case, this would vary based on your index). <code>topDocsForContext</code> dictates how many documents to feed to ChatGPT. <code>queryTemplate</code> is the format for the actual question fed to ChatGPT after the document queries, which is formed from the value provided in the SearchBox.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      {<span class="hljs-attr">...configProps</span>}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">{SEARCH_COMPONENT_ID}</span>
        {<span class="hljs-attr">...otherProps</span>}
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"answer-component"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">px-5</span> <span class="hljs-attr">pt-5</span> ${<span class="hljs-attr">styles.answer</span>}`}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">AIAnswer</span>
          <span class="hljs-attr">componentId</span>=<span class="hljs-string">{AI_ANSWER_COMPONENT_ID}</span>
          <span class="hljs-attr">showVoiceInput</span>
          <span class="hljs-attr">showIcon</span>
          <span class="hljs-attr">react</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">and:</span> <span class="hljs-attr">SEARCH_COMPONENT_ID</span> }}
          <span class="hljs-attr">AIConfig</span>=<span class="hljs-string">{{</span>
            <span class="hljs-attr">docTemplate:</span> "'${<span class="hljs-attr">source.name</span>}', <span class="hljs-attr">says</span> '${<span class="hljs-attr">source.line</span>}'",
            <span class="hljs-attr">queryTemplate:</span> "${<span class="hljs-attr">value</span>}",
            <span class="hljs-attr">topDocsForContext:</span> <span class="hljs-attr">15</span>,
          }}
          <span class="hljs-attr">enterButton</span>=<span class="hljs-string">{true}</span>
          <span class="hljs-attr">showInput</span>=<span class="hljs-string">{false}</span>
        /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>In our case, we also want to show the documents that were used to generate the AI answer in the UI itself, so we know the answers come from the search index and the AI isn't hallucinating or replying from out-of-domain facts. We store the data in the state using the <code>onData</code> handler.</p>
<pre><code class="lang-javascript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [feedData, setFeedData] = useState({ <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">hits</span>: [] });
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      {<span class="hljs-attr">...configProps</span>}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">{SEARCH_COMPONENT_ID}</span>
        {<span class="hljs-attr">...otherProps</span>}
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"answer-component"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">px-5</span> <span class="hljs-attr">pt-5</span> ${<span class="hljs-attr">styles.answer</span>}`}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">AIAnswer</span>
          <span class="hljs-attr">componentId</span>=<span class="hljs-string">{AI_ANSWER_COMPONENT_ID}</span>
          <span class="hljs-attr">onData</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">loading</span>, <span class="hljs-attr">rawData</span>, <span class="hljs-attr">data</span> }) =&gt;</span> {
            if (!loading &amp;&amp; data.length) {
              if (rawData.hits &amp;&amp; rawData.hits.hits)
                setFeedData({
                  loading: false,
                  hits: rawData.hits.hits.map((hit) =&gt; hit._source),
                });
            } else {
              setFeedData({ loading: true, hits: [] });
            }
          }}
          {...otherProps}
        /&gt;
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-getting-episode-from-the-search-query">Getting episode from the search query</h3>
<p>Since we store the associated episode for each question suggestion, we would like to use this information in getting the first-pass results from the search engine. In a previous step, we put the episode name in the question, and now we will extract it and tell the index to only show those documents which match the episode name.</p>
<p>We will use the <code>transformRequest</code> method to do this, which lets us modify the payload generated by the ReactiveSearch library before sending it across the network.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [feedData, setFeedData] = useState({ <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">hits</span>: [] });
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      {<span class="hljs-attr">...configProps</span>}
      <span class="hljs-attr">transformRequest</span>=<span class="hljs-string">{(req)</span> =&gt;</span> {
        const body = JSON.parse(req.body);
        body.query = body.query.map((componentQuery) =&gt; {
          if (
            componentQuery.id === SEARCH_COMPONENT_ID &amp;&amp;
            componentQuery.type === "search"
          ) {
            const searchQuery = componentQuery.value;
            const matches = searchQuery.match(/in "(.*?)"\?$/);
            const episode = matches &amp;&amp; matches[1];

            return {
              ...componentQuery,
              customQuery: {
                query: {
                  term: {
                    "episode_name.keyword": episode,
                  },
                },
              },
            };
          }
          return componentQuery;
        });
        body.settings = {
          recordAnalytics: false,
          backend: "opensearch",
        };
        const newReq = { ...req, body: JSON.stringify(body) };
        return newReq;
      }}
    &gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>The complete app should look as below:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/qna-rick-and-morty/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;view=preview" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-summary">Summary</h2>
<p>Hope you enjoyed reading! Here's a brief recap of what we covered, so you can implement the same for a domain of your choice:</p>
<ul>
<li><p>ChatGPT is a GQA system with limitations, which can be overcome by using LTM-QA to improve both precision and recall.</p>
</li>
<li><p>We used a search index of Rick and Morty dialogues to retrieve documents from when user performs a search.</p>
</li>
<li><p>We utilized the <code>SearchBox</code> component to suggest a list of popular questions that a user might have.</p>
</li>
<li><p>We added an <code>AIAnswer</code> component that depended on the SearchBox and updated dynamically based on the user's query.</p>
</li>
<li><p>Lastly, we filtered documents relevant to the query (specific episode) to improve the precision of the answer.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Discover Personalized Search: Tailor-Made Experiences with ReactiveSearch and OpenSearch]]></title><description><![CDATA[Personalized search is the key to delivering tailored experiences to users by offering relevant results based on their interests and past behavior. Social media platforms like YouTube, Twitter, or TikTok provide personalized feeds that cater to users...]]></description><link>https://blog.reactivesearch.io/personalized-search-with-reactivesearch-and-opensearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/personalized-search-with-reactivesearch-and-opensearch</guid><category><![CDATA[opensearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[opensource]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Wed, 26 Apr 2023 16:36:39 GMT</pubDate><content:encoded><![CDATA[<p>Personalized search is the key to delivering tailored experiences to users by offering relevant results based on their interests and past behavior. Social media platforms like YouTube, Twitter, or TikTok provide personalized feeds that cater to users' preferences. For instance, if two users search for "football," the fan of American football and the fan of soccer will see different results, thanks to personalized search taking their search history and behavior into account.</p>
<p>OpenSearch is an open-source search engine that enables developers to build custom search experiences. In this post, we will explore how ReactiveSearch and OpenSearch can help you craft personalized search experiences.</p>
<p>Let's explore some popular applications of personalized search and how user preferences come into play:</p>
<ol>
<li><p>Crafting algorithmic feeds based on browsing history: Platforms like YouTube and TikTok utilize personalized search to generate algorithmic feeds of videos that cater to users' interests. By analyzing watch history, likes, comments, shares, and other interactions, these platforms gain insight into users' preferences, serving up relevant and captivating content.</p>
</li>
<li><p>Curating feeds from user-selected topics: Personalized search also shines on news sites, Twitter, Quora, and other platforms where users input their interests or preferred topics. These platforms use this information to create feeds teeming with content that users find relevant and engaging. For instance, a news site might ask users to choose topics such as politics, sports, or entertainment, and then provide news articles on those subjects in their feed.</p>
</li>
</ol>
<p>Some websites blend both approaches, using explicit user-selected topics along with tracking user history, to personalize search results and elevate the user experience. So go ahead, dive into the world of personalized search, and deliver content that resonates with your audience.</p>
<p>Here's the final personalized feed search UI that you will be building by going through this post:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/feed-search-ui/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h1 id="heading-the-building-blocks"><strong>The Building Blocks</strong></h1>
<p>To create a personalized search experience, we'll be using the ReactiveSearch platform and the open-source Apache 2.0 licensed ReactiveSearch UI kit for React.</p>
<ul>
<li><p>For this blog post's hosted UI, we have utilized a hosted ReactiveSearch service connected to an OpenSearch backend. ReactiveSearch can also be deployed in self-host mode, as well as connect to other engines: Elasticsearch, Solr, MongoDB and OpenAI are currently supported.</p>
</li>
<li><p>We will use the React frontend UI components library from ReactiveSearch to develop search components like search boxes, filters, and result displays. This approach streamlines the development process and paves the way for a customized search experience.</p>
</li>
</ul>
<h2 id="heading-building-a-search-pipeline"><strong>Building a search pipeline</strong></h2>
<p>Traditionally, when a user searches for a specific term, the search engine retrieves documents from the search index, which are then ranked to determine the order in which they appear to the user. However, we want the search engine to "boost" or "promote" certain documents based on end-users' preferences. To achieve this, we will introduce an additional step to promote relevant results before presenting them to the user.</p>
<p>In ReactiveSearch, this multi-step search query process is executed through pipelines. A pipeline is a sequence of search processing stages that query one or more search engines and return results to the user. We will input the user's query into the OpenSearch engine, which will collect and rank all pertinent documents. These documents will then advance to our "boost" step, where we'll enhance user-specific terms to ultimately deliver finely-tuned, personalized results.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678370893481/eea800e7-4749-42ef-aafd-c467acfbdc41.png" alt class="image--center mx-auto" /></p>
<p>We can integrate a step in the pipeline to ensure that only authorized users can access the index. This authorization step can be positioned between the request processing and the search engine's ranking of documents. Pipelines offer incredible versatility, enabling you to incorporate features such as integrating results from ChatGPT, Google's Knowledge Graph, performing vector search, and more. To learn about ReactiveSearch pipelines and their capabilities, see the <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/concepts/">doc reference here</a>.</p>
<h3 id="heading-authoring-a-pipeline-using-the-dashboard"><strong>Authoring a pipeline using the dashboard</strong></h3>
<p>To begin, navigate to <a target="_blank" href="http://dash.reactivesearch.io">dash.reactivesearch.io</a> and select "Pipelines" from the sidebar menu. Next, click on "Create a Pipeline." You'll find several pre-built templates catering to a wide range of use-cases; for our purposes, we'll opt for the "Basic Template." Upon making your selection, a text editor will appear, signaling the completion of this step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678355524394/dbe0ea03-9e0c-4a28-bcf9-bf3b9776e09c.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-making-of-a-pipeline"><strong>The making of a pipeline</strong></h3>
<p>Upon opening the pipeline, you'll notice several components. The first elements are <code>enabled</code> and <code>description</code>, both of which are self-explanatory. Next, the <code>routes</code> property allows us to attach the pipeline to specific API endpoints, which a frontend can make a REST API call to retrieve results. We'll set the path within routes to <code>/query-rules-boost</code>.</p>
<p>Following this, we have <code>envs</code>, which are used to set the pipeline's operational context, including the index, domain settings, and more. We'll set the index to <code>movies-demo-app</code> here.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"enabled"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"description"</span>: <span class="hljs-string">"Template to create a pipeline"</span>,
    <span class="hljs-string">"routes"</span>: [
        {
            <span class="hljs-string">"path"</span>: <span class="hljs-string">"/query-rules-boost"</span>,
            <span class="hljs-string">"method"</span>: <span class="hljs-string">"POST"</span>,
            <span class="hljs-string">"classify"</span>: {
                <span class="hljs-string">"category"</span>: <span class="hljs-string">"reactivesearch"</span>
            }
        }
    ],
    <span class="hljs-string">"envs"</span>: {
        <span class="hljs-string">"index"</span>: [
            <span class="hljs-string">"movies-demo-app"</span>
        ]
    },
    <span class="hljs-string">"stages"</span>: [
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"auth"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"reactivesearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"os_query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"elasticsearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        }
    ]
}
</code></pre>
<p>Stages are the building blocks of the pipeline, representing the steps of data transformation, as illustrated in the diagram above. These stages are executed sequentially, constructing the results that are ultimately presented to the user. The "auth" stage verifies whether a user has permission to access the index or perform the intended action. Next, the <code>reactivesearch</code> query stage converts the ReactiveSearch query into an OpenSearch query. Finally, the <code>os_query</code> stage executes a query on the OpenSearch index, collecting the results that are then delivered to the user.</p>
<h3 id="heading-incorporating-a-stage-to-refine-search-results"><strong>Incorporating a stage to refine search results</strong></h3>
<p>Following the diagram, we can introduce an additional stage after retrieving results from the OpenSearch index to enhance specific search outcomes. We can incorporate a stage called <code>boost</code>, you can see its <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/query-rules/#boost-results-by-score">reference in the documentation</a>.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">// Same pipeline options as above with additional "boost" stage</span>
    <span class="hljs-string">"stages"</span>: [
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"auth"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"reactivesearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"os_query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"elasticsearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        <span class="hljs-comment">/*
        Score documents containing 
        "Harry Potter" better
        */</span>
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boost-harry-potter"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-comment">// The field to query in documents</span>
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"original_title"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"Harry Potter"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span> <span class="hljs-comment">// Boost only top 3 docs</span>
            }
        }
    ]
}
</code></pre>
<p>We're setting the stage to boost up to 3 documents that contain the value <code>Harry Potter</code> in the <code>original_title</code> index field. We start with a very specific value to test out the personalization in action.</p>
<p>The dashboard provides a realtime validation testing view where we can test the above pipeline to confirm that we indeed get the top 3 documents of the hits containing Harry Potter in their <code>original_title</code> field.</p>
<h2 id="heading-building-a-search-ui">Building a search UI</h2>
<p>With our pipeline configured, it's time to witness it in action. We'll use the React flavor of the ReactiveSearch library to develop the UI. However, you can use the ReactiveSearch dashboard's no-code UI builder, or Vue.JS, vanilla JS, React Native or Flutter libraries to do this. <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/gettingstarted">See the choosing UI library section</a>.</p>
<p><strong>npx and go!</strong></p>
<p>We will use <a target="_blank" href="https://create-react-app.dev/">create-react-app</a> to get the boilerplate code. Execute the following command in the terminal to create a React app named "feed-search":</p>
<pre><code class="lang-bash">npx create-react-app feed-search
<span class="hljs-built_in">cd</span> feed-search
yarn add @appbaseio/reactivesearch
code .   <span class="hljs-comment"># Fire up your code editor</span>
</code></pre>
<p><code>@appbaseio/reactivesearch</code> is ReactiveSearch's React npm package.</p>
<p>As you open the code editor, notice the <code>src/App.js</code> file, we will be editing this file primarily to build the search UI.</p>
<p><strong>Connecting to the pipeline</strong></p>
<p>Let's connect the pipeline using the <code>ReactiveBase</code> component. Each Reactivesearch UI has a <code>ReactiveBase</code> component as the connector component, where you configure the index and cluster URL. Here, we do this by specifying the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/reactivebase/#endpoint"><code>endpoint</code> property</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.js</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
        <span class="hljs-attr">endpoint</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">url:</span> "<span class="hljs-attr">https:</span>//<span class="hljs-attr">a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61</span>@<span class="hljs-attr">appbase-demo-ansible-abxiydt-arc.searchbase.io</span>/<span class="hljs-attr">query-rules-boost</span>",
          <span class="hljs-attr">method:</span> "<span class="hljs-attr">POST</span>",
        }}
        <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">true</span>,
        }}
      &gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Now we can add some more components like a <code>SearchBox</code> which shows a search bar where a user inputs their search query. We can also add a <code>ReactiveList</code> to display the results returned from the pipeline. You can find more information in the docs for <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/"><code>SearchBox</code></a> and <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/result/reactivelist/"><code>ReactiveList</code></a>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase,
  ReactiveList,
  ResultCard,
  SearchBox,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
        <span class="hljs-attr">endpoint</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">url:</span> "<span class="hljs-attr">https:</span>//<span class="hljs-attr">a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61</span>@<span class="hljs-attr">appbase-demo-ansible-abxiydt-arc.searchbase.io</span>/<span class="hljs-attr">query-rules-boost-static</span>",
          <span class="hljs-attr">method:</span> "<span class="hljs-attr">POST</span>",
        }}
        <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">false</span>,
        }}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row mt-4 p-3"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
            <span class="hljs-attr">dataField</span>=<span class="hljs-string">{[</span>"<span class="hljs-attr">original_title</span>"]}
            <span class="hljs-attr">componentId</span>=<span class="hljs-string">"BookSensor"</span>
            <span class="hljs-attr">highlight</span>
            <span class="hljs-attr">URLParams</span>
            <span class="hljs-attr">size</span>=<span class="hljs-string">{5}</span>
            <span class="hljs-attr">enablePredictiveSuggestions</span>
            <span class="hljs-attr">showClear</span>
            <span class="hljs-attr">renderNoSuggestion</span>=<span class="hljs-string">{()</span> =&gt;</span> "No suggestions found."}
          /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveList</span>
            <span class="hljs-attr">componentId</span>=<span class="hljs-string">"SearchResult"</span>
            <span class="hljs-attr">dataField</span>=<span class="hljs-string">"original_title"</span>
            <span class="hljs-attr">size</span>=<span class="hljs-string">{12}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"result-list-container"</span>
            <span class="hljs-attr">pagination</span>
            <span class="hljs-attr">react</span>=<span class="hljs-string">{{</span>
              <span class="hljs-attr">and:</span> "<span class="hljs-attr">BookSensor</span>",
            }}
            <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">data</span> }) =&gt;</span> {
              // Render results returned from the pipeline
            }}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>You can look at the final UI below. Observe that we boost 3 "Harry Potter" documents when the search query is empty.</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/feed-search-ui/tree/step-1/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-getting-user-preferences">Getting user preferences</h2>
<p>We aim to promote specific search results dynamically based on an individual's preferences. This can be achieved by tracking browser history and boosting search results based on recent searches. In this case, we'll allow users to explicitly select the topics they're interested in using a straightforward tag-based selection. Users can choose up to three movie genres, which the search engine will then prioritize in the search results.</p>
<p>To accomplish this, we will create a component inside a file called <code>TagSelector.js</code>. This will feature a select input, enabling users to choose their preferred genres. Additionally, we'll need to incorporate the selected options into our request payload. This data will then be utilized within the pipeline to enhance search results. We can achieve this by using the <code>transformRequest</code> prop of <code>ReactiveBase</code>, which modifies the search request before making a call to the pipeline API.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase,
  ReactiveList,
  ResultCard,
  SearchBox,
  SelectedFilters,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> TagSelector <span class="hljs-keyword">from</span> <span class="hljs-string">"./TagSelector"</span>;

<span class="hljs-keyword">import</span> styles <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.module.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [options, setOptions] = useState([<span class="hljs-string">"Comedy"</span>]);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (options) {
      setIsLoading(<span class="hljs-literal">true</span>);
      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setIsLoading(<span class="hljs-literal">false</span>);
      }, <span class="hljs-number">1000</span>);
    }
  }, [options]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-3"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.userPreference}</span>&gt;</span>User Preference:<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TagSelector</span> <span class="hljs-attr">options</span>=<span class="hljs-string">{options}</span> <span class="hljs-attr">setOptions</span>=<span class="hljs-string">{setOptions}</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      {isLoading ? (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row mt-4 p-3"</span>&gt;</span>Resetting topics...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ) : (
        <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
          {<span class="hljs-attr">...samePropsAsBefore</span>}
          <span class="hljs-attr">transformRequest</span>=<span class="hljs-string">{(req)</span> =&gt;</span> {
            const body = JSON.parse(req.body);
            body.customData = options;
            const newReq = { ...req, body: JSON.stringify(body) };
            return newReq;
          }}
        &gt;
          {/*
              Search and other UI components
          */}
        <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>If you try out the live search UI, the request body payload should look like below:</p>
<pre><code class="lang-javascript">POST /query-rules-boost
{
    <span class="hljs-attr">customData</span>: [<span class="hljs-string">"Comedy"</span>, <span class="hljs-string">"Horror"</span>] <span class="hljs-comment">// user selected options</span>
}
</code></pre>
<h2 id="heading-incorporating-preferences-within-the-pipeline">Incorporating Preferences within the Pipeline</h2>
<p><strong>Accessing Context</strong></p>
<p>With data from the frontend now structured within the request body, we need to access it inside the pipeline using context.</p>
<p>Each pipeline operates within a specific context. This context can be accessed within the stages to dynamically modify their input. First, however, we must parse the request and incorporate the selected options into the context. To achieve this, we'll create a script called <code>promoteParams.js</code>, which will be added as a stage prior to our <strong>boost</strong> stage. This script introduces an <code>additionalParams</code> property to the context object, which can be accessed within the stage as {{additionalParams}}.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// promoteParams.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> requestBody = <span class="hljs-built_in">JSON</span>.parse(context.request.body);
    <span class="hljs-keyword">const</span> customData = (requestBody &amp;&amp; requestBody.customData) || [];

    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">additionalParams</span>: {
            <span class="hljs-attr">paramOne</span>: customData[<span class="hljs-number">0</span>] || <span class="hljs-string">''</span>,
            <span class="hljs-attr">paramTwo</span>: customData[<span class="hljs-number">1</span>] || <span class="hljs-string">''</span>,
            <span class="hljs-attr">paramThree</span>: customData[<span class="hljs-number">2</span>] || <span class="hljs-string">''</span>,
        },
    };
}
</code></pre>
<p>You can add the script as a stage using the dashboard. First, reference the script inside the pipeline as a stage and then add a file named <code>promoteParams.js</code> with the above content. Add the script before the boost stages.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">// Same pipeline options as above with additional script stage</span>
    <span class="hljs-string">"stages"</span>: [
        <span class="hljs-comment">//Other stages same as above</span>
        <span class="hljs-comment">// Add below stage before the boost stage</span>
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"addPromoteParams"</span>,
            <span class="hljs-string">"scriptRef"</span>: <span class="hljs-string">"promoteParams.js"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boost-harry-potter"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-comment">// The field to query in documents</span>
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"original_title"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"Harry Potter"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span> <span class="hljs-comment">// Boost only top 3 docs</span>
            }
        }
    ]
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678381239056/bc5a34d6-b4b5-472a-bb89-4467bafb77c9.gif" alt="Custom script stage" class="image--center mx-auto" /></p>
<p><strong>Using Context:</strong></p>
<p>Each pipeline has a context in which it's executing. We can access this context as an input to a stage.</p>
<p>Now we can reference the context inside the boost stages. We also add two more boost stages to handle all three genres.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">// Same pipeline options as above with dynamic input from context</span>
    <span class="hljs-string">"stages"</span>: [
        <span class="hljs-comment">// Other stages</span>
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"addPromoteParams"</span>,
            <span class="hljs-string">"scriptRef"</span>: <span class="hljs-string">"promoteParams.js"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boostOne"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"genres"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"{{additionalParams.paramOne}}"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span>
            }
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boostTwo"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"genres"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"{{additionalParams.paramTwo}}"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span>
            }
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boostThree"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"genres"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"{{additionalParams.paramThree}}"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span>
            }
        }
    ]
}
</code></pre>
<p>Congratulations, we've now updated the pipeline to use the user preferences passed from the frontend search UI. The complete frontend code and live hosting of the search UI is shown below:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/feed-search-ui/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-summary">Summary</h2>
<p>In this post, we explored the concept of personalized search and its implementation using ReactiveSearch and OpenSearch. We learned how to create a pipeline with multiple stages to process and enhance search results based on user preferences. Additionally, we built a search UI using the ReactiveSearch library and a React app, allowing users to explicitly define their interests through a tag-based selection. We also delved into accessing and modifying context within the pipeline stages to dynamically adjust search results. By combining these techniques, we're able to provide users with a customized and engaging search experience.</p>
<p>ReactiveSearch Pipelines offer a V8 engine-based JavaScript runtime (similar to CloudFlare workers) that you can run alongside search engines to craft relevant search experiences for SaaS, Enterprise and E-Commerce use cases.</p>
<p>Additional links to explore more:</p>
<p><a target="_blank" href="https://reactivesearch.io/">https://reactivesearch.io/</a></p>
<p><a target="_blank" href="https://github.com/awesome-reactivesearch/feed-search-ui">https://github.com/awesome-reactivesearch/feed-search-ui</a></p>
<p><a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/">https://docs.reactivesearch.io/docs/pipelines/how-to/</a></p>
]]></content:encoded></item><item><title><![CDATA[A Beginner's Guide to Server-Side Rendering in ReactiveSearch]]></title><description><![CDATA[In this article, we share a step-by-step guide on how to convert a ReactiveSearch built UI into a SSR search UI, which is beneficial for SEO, caching, and user experience.
Why SSR a search UI?
To understand the importance of pre-rendering a web app, ...]]></description><link>https://blog.reactivesearch.io/a-beginners-guide-to-server-side-rendering-in-reactivesearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/a-beginners-guide-to-server-side-rendering-in-reactivesearch</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[Algolia]]></category><category><![CDATA[Server side rendering]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Lohita Suvvari]]></dc:creator><pubDate>Wed, 19 Apr 2023 10:50:10 GMT</pubDate><content:encoded><![CDATA[<p>In this article, we share a step-by-step guide on how to convert a ReactiveSearch built UI into a SSR search UI, which is beneficial for SEO, caching, and user experience.</p>
<h3 id="heading-why-ssr-a-search-ui"><strong>Why SSR a search UI?</strong></h3>
<p>To understand the importance of pre-rendering a web app, one needs to understand two mechanisms that can be used for rendering a web page, namely <strong>CSR</strong> and <strong>SSR</strong>.</p>
<p>Below are production applications of <a target="_blank" href="http://walmart.com">walmart.com</a> rendered with SSR vs CSR.</p>
<p><img src="https://miro.medium.com/max/700/0*-EWG5MXBIo-D_ug3." alt class="image--center mx-auto" /></p>
<p>We compared three of our applications(home, category, and search) in SSR vs CSR. These are the chrome network screen grabs of the pages being rendered. You’ll notice that SSR renders faster and that using CSR has a blank white page while loading. Most applications that use CSR will obviously replace the blank white page with a loading icon, but since we use SSR for our normal operations when forced into CSR mode, the pages are blank. Please note these are one-off captures, with our machines, at a certain time of day with the current prod build, so individual performance can vary-but this should be the general trend that you see for apps.</p>
<p><img src="https://miro.medium.com/max/545/1*7LcOn9FuMR6uE0PT2htrJg.png" alt class="image--center mx-auto" /></p>
<p>Here is the first server response for the home, category, and search pages. I would ignore the green bar because that is more relative compared to the rest of the network graph. The two things I want to call attention to are the document size and the TTFB. Since the server is responding with HTML for the page, you’ll notice the document size for SSR is always bigger. Another point that was talked about earlier, the CSR response is faster(except for the home page for some reason in this test).</p>
<p>I can’t stress enough though that these captures are variable based on application, latency, server, location, and a bunch of other variables, so they shouldn’t be taken as scientific fact, but more of a general trend.</p>
<p>What is Client Side rendering (CSR)</p>
<p><img src="https://miro.medium.com/max/700/1*CRiH0hUGoS3aoZaIY4H2yg.png" alt /></p>
<p>A React application consists of one HTML page, which is <code>index.html</code>, and to create other HTML content, we use JSX. JSX is first converted into plain JavaScript because browsers support only JavaScript. After that, JavaScript creates the HTML content and renders it on the user's screen. React moves all the content into one HTML file. This is known as Single Page Application, or SPA.</p>
<p><strong>Problem with Client Side rendering (CSR)</strong></p>
<ul>
<li><p><strong>Slow at First</strong></p>
<ul>
<li>The whole application is rendered on the client side by the browser. So due to this, rendering of our initial page will take some time. But after the first load, it becomes very smooth and user-friendly.</li>
</ul>
</li>
<li><p><strong>SEO problem</strong> (<em>search engine optimization)</em></p>
<ul>
<li><p>CSR requires a two-wave process for JS rendering and indexing in a browser, generally by Google.</p>
</li>
<li><p>The first wave requests the source code, crawls, and then indexes the presented HTML. But in CSR we don't have much HTML because it takes time to convert from JavaScript to HTML.</p>
</li>
</ul>
</li>
<li><p><strong>Caching Issue</strong></p>
<ul>
<li><p>Since the HTML is not available in the initial render, browsers cannot cache the HTML structure of the page.</p>
</li>
<li><p>One way to avoid this issue is to cache the JavaScript, but this may prove to be costly as JavaScript files can take up a lot of space in the browser's memory.</p>
</li>
</ul>
</li>
</ul>
<p>What is Server Side Rendering (SSR)</p>
<p><img src="https://miro.medium.com/max/700/1*jJkEQpgZ8waQ5P-W5lhxuQ.png" alt /></p>
<p>A server-side rendered application <strong>enables pages to load faster, improving the user experience</strong>. When rendering server-side, search engines can easily index and crawl content because the content can be rendered before the page is loaded, which is ideal for <strong>SEO</strong>.</p>
<p>So to render our pages much faster we can use Next.js (Framework for React), which renders our pages on the server side and give us pre-rendered HTML for our pages.</p>
<h3 id="heading-ssr-reactivesearch">SSR + Reactivesearch</h3>
<p>With Reactivesearch's Server Side Rendering support, you can handle the initial render when a user (or search engine crawler) first requests your app. To achieve the relevant results on an initial render, we need to pre-populate the redux store of the ReactiveSearch App.</p>
<p>ReactiveSearch provides an API that works with any SSR solution, The basic idea of SSR support for ReactiveSearch is to perform any necessary API calls to the search client and compute the initial state of the App, then rehydrate the client side with the initialState computed on the server-side.</p>
<p>ReactiveSearch offers SSR via <code>getServerState()</code>, a method that takes two params with second one being optional:</p>
<ul>
<li><p>the first param of the function receives the <code>App</code> component ref and,</p>
</li>
<li><p>the second param <em>[optional]</em> receives the URL string or query param object(should be parsed) to respect the URL query string.</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getServerState } <span class="hljs-keyword">from</span> <span class="hljs-string">'@appbaseio/reactivesearch'</span>;
</code></pre>
<h2 id="heading-how-to-build"><strong>How to build</strong></h2>
<p>Here, we will go step-by-step and build our application. We’ll use <a target="_blank" href="http://Codesandbox.io"><strong>Codesandbox.io</strong></a> to generate code at each intermediate step, making it easy to test</p>
<h3 id="heading-things-we-will-need">Things we will need</h3>
<p>Let's start with a prebuilt ReactiveSearch UI developed using Next.js but still required configuration to SSR the search UI.</p>
<p><strong>The starter app</strong></p>
<iframe src="https://codesandbox.io/embed/nameless-brook-skwkgf?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<p>Converting the above starter app into SSR is as easy as passing 2 props and writing 2-3 lines of code.</p>
<ul>
<li><p>The <code>initialState</code> would be computed and passed to the client side to rehydrate the App tree.</p>
</li>
<li><p>To compute the initial state, Let's import <code>getServerState</code> from the library.</p>
<pre><code class="lang-jsx">  <span class="hljs-keyword">import</span> {
      <span class="hljs-comment">//  ... other components imported</span>
      getServerState
  } <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;
</code></pre>
</li>
<li><p>In the case of Next.js, We would utilize <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props"><code>getServerSideProps</code></a> it to compute the initial state of our app on the server side. You may read about SSR using Next.js <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props">here</a>.</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// src/pages/index.js</span>
  <span class="hljs-comment">// add the below snippet at the end of your page </span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params">props</span>)</span>{
  <span class="hljs-comment">// your app component</span>
  }

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getServerSideProps = <span class="hljs-keyword">async</span> (context) =&gt; {    
      <span class="hljs-keyword">const</span> initialState = <span class="hljs-keyword">await</span> getServerState(Main, context.resolvedUrl);
      <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">props</span>: { initialState },
          <span class="hljs-comment">// will be passed to the page component as props</span>
      };
  };
</code></pre>
</li>
<li><p>Finally, the computed state should be passed to the <code>&lt;ReactiveBase /&gt;</code> component.</p>
<pre><code class="lang-javascript">  &lt;ReactiveBase
      <span class="hljs-comment">// ... other props</span>
      initialState={props.initialState}
      <span class="hljs-comment">// the contextCollector is another prop required for RS SSR</span>
      <span class="hljs-comment">// to do some magic internally</span>
      <span class="hljs-comment">// you should pass it without worrying about its source</span>
      <span class="hljs-comment">// we handle it for you</span>
      contextCollector={props.contextCollector}
  &gt;
  <span class="hljs-comment">// ...</span>
  &lt;/ReactiveBase&gt;
</code></pre>
</li>
</ul>
<p>Finally, you can now run the dev server and catch the SSR in action. Here is the final Codesandbox.</p>
<iframe src="https://codesandbox.io/embed/rs-ssr-final-app-6z86mo?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<p>You can look at the code for this app at <a target="_blank" href="https://github.com/appbaseio/reactivesearch/tree/next/packages/web/examples/ssr">https://github.com/appbaseio/reactivesearch/tree/next/packages/web/examples/ssr</a></p>
<h2 id="heading-summary">Summary</h2>
<blockquote>
<p>ReactiveSearch provides an API to support Server Side Rendering (SSR) which allows for API calls to the search client and the computation of the initial state of the App on the server-side. The initialState is computed and passed to the client side to rehydrate the App tree, and the dev server can be run to see the SSR in action.</p>
</blockquote>
<p>Here comes the end of our journey, and we have got our awesome CLIENT-SIDE search app built with ReactiveSearch converted into an SSR search app. To summarize our journey:</p>
<ul>
<li><p>Step 1: Start with the starter CSR app - <a target="_blank" href="https://codesandbox.io/s/rs-ssr-starter-csr-app-skwkgf?from-embed">https://codesandbox.io/s/rs-ssr-starter-csr-app-skwkgf</a></p>
</li>
<li><p>Step 2: Import <code>getServerState</code> from the library and compute the <code>initialState</code> on the server side.</p>
</li>
<li><p>Step3: Pass the <code>initialState</code> prop to <code>ReactiveBase</code> component received from the server side through props. Additionally, pass <code>contextCollector</code> the prop to ReactiveBase which is required internally to do the SSR config magic.</p>
<ul>
<li>Codesandbox link: <a target="_blank" href="https://codesandbox.io/s/rs-ssr-final-app-6z86mo?file=/package.json">https://codesandbox.io/s/rs-ssr-final-app-6z86mo?file=/package.json</a></li>
</ul>
</li>
</ul>
<p>    Voila 🎉, We have finally converted our CSR search UI into an SSR search UI with just 2-3 lines of code.</p>
]]></content:encoded></item><item><title><![CDATA[KNN Search with OpenSearch and OpenAI Embeddings: An In-Depth Guide]]></title><description><![CDATA[K-nearest neighbors (KNN) search aka semantic search is a simple and intuitive algorithm although if you haven't used them, the topic can seem daunting. In this blog post, we will go from no familiarity to KNN to building a full functional backend an...]]></description><link>https://blog.reactivesearch.io/knn-search-with-opensearch-and-openai-embeddings-an-in-depth-guide</link><guid isPermaLink="true">https://blog.reactivesearch.io/knn-search-with-opensearch-and-openai-embeddings-an-in-depth-guide</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[openai]]></category><category><![CDATA[VectorSearch]]></category><category><![CDATA[search]]></category><dc:creator><![CDATA[Shubham Shah]]></dc:creator><pubDate>Mon, 03 Apr 2023 13:44:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680014166132/22bdcc02-0aff-426f-9fd0-84cb08c878da.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>K-nearest neighbors (KNN) search aka semantic search is a simple and intuitive algorithm although if you haven't used them, the topic can seem daunting. In this blog post, we will go from no familiarity to KNN to building a full functional backend and search UI - using OpenSearch for the search engine, OpenAI for vector embeddings and ReactiveSearch for cloud hosting of the backend and the search UI components. KNN search is a very useful algorithm that can be used for a variety of tasks, such as clustering data, building recommendation systems, etc. Some examples of it are below:</p>
<ol>
<li><p><strong>Improve search results:</strong></p>
<p> KNN Search can be used to improve traditional search results by providing more relevant and accurate results to users. This can be particularly useful when searching for information whose meaning is difficult to describe in words. For example, if a user searches "Not good dog food", then traditional search might show results containing "good dog food" which is opposite to the user's intention.</p>
</li>
<li><p><strong>Recommend items</strong>:<br /> It can be used to recommend items to users based on their preferences or behavior. For example, a recommendation system might use similarity search to find items that are similar to those that a user has previously purchased or viewed.</p>
</li>
<li><p><strong>Categorizing data into clusters</strong>:</p>
<p> KNN search can group similar items together, allowing us to identify patterns and relationships in the data. For example, an e-commerce company might use clustering to group customers based on their purchase behavior. Customers who frequently purchase items in a specific category or price range can be grouped together in a cluster, while customers who tend to make one-time purchases can be grouped in a different cluster. The company can then create targeted marketing campaigns for each cluster, such as offering discounts or promotions on items that are popular within a particular cluster.</p>
</li>
</ol>
<h2 id="heading-visualizing-data-as-a-vector-space">Visualizing data as a vector space</h2>
<hr />
<p>Before we implement KNN Search we need to understand a fundamental concept on which it is built, "vectors". Vectors are simply arrays. While in programming we can have arrays of strings, objects, etc., vectors can contain only numeric values like <code>[2, 3]</code>. Hence vectors are numeric arrays. The cool part about vectors is that we can plot them on a graph and find distance between them and see how close one is to another.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679919930195/ce9ced6e-b272-48f8-a650-1cd3f050a9bd.png" alt class="image--center mx-auto" /></p>
<p>We can't plot text or audio information on graph or can we? What if we transform the textual data to a vector. That is essentially what a machine learning model does. It takes real world objects like text, audio, etc. and generates a vector. A collection of vectors is called a vector space. You can see the space of sentences below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679920183365/979801f9-8d2c-4998-ba19-0ddcdc6f094f.png" alt="Source: DeepAI" class="image--center mx-auto" /></p>
<p>Above is a 2 dimensional graph. Adding more dimensions to the vector means nearest points get more similar. A real world model can map data to thousands of dimensions.</p>
<h2 id="heading-what-are-we-building">What are we building?</h2>
<hr />
<p>We are going to build a search UI for a dataset of <a target="_blank" href="https://www.kaggle.com/datasets/snap/amazon-fine-food-reviews">Amazon reviews</a> of various products. This search wouldn't be a traditional search, but would show a user similar results. For example, if a user searches "Not good dog food", then traditional search might show results containing "good dog food" which might not be the intention. But KNN search would show similar results like "bad dog food" because it maps the two queries as nearest neighbours.</p>
<h2 id="heading-the-building-blocks">The Building Blocks</h2>
<hr />
<h3 id="heading-openai-vector-embeddings-api-model-for-vectorizing-data">OpenAI Vector Embeddings API: Model for vectorizing data</h3>
<p>We would use this API to convert our textual review data into vectors. You would need an API key to access the Embeddings API. You can do that by going to the <a target="_blank" href="https://platform.openai.com/">OpenAI website</a> and signing up.</p>
<h3 id="heading-opensearch-search-index">OpenSearch: Search index</h3>
<p>We use a search index that has the capability of performing a KNN search on vectors. For this, we will use an OpenSearch index because it doesn't have limitations as opposed to other search indexes.</p>
<blockquote>
<p>💡 Note: ElasticSearch has a vector dimension limitation. This means the vector array in ElasticSearch can have a maximum length of <code>1024</code> but OpenAI vectors need an array of <code>1536</code> length. <a target="_blank" href="https://github.com/elastic/elasticsearch/issues/92458">Read more about this limitation in ElasticSearch on this open issue.</a></p>
</blockquote>
<p><strong>Update</strong>: The issue looks addressed now and as Elasticsearch releases a new version addressing this limitation, we will be publishing a tutorial on the same using Elasticsearch.</p>
<h3 id="heading-reactivesearch-the-catalyst">Reactivesearch: The Catalyst</h3>
<p>We have all the tools but we need the infrastructure to make the above pieces work with each other without you having to do any work. <a target="_blank" href="https://www.reactivesearch.io/">Reactivesearch.io</a> provides hosting an OpenSearch index but you can also "Bring Your Own Cluster"(BYOC). We would also use a feature called pipelines which helps in organizing various steps like vectorizing data, indexing it, etc. into stages. This makes the process of developing such an application highly efficient. Reactivesearch also has a <a target="_blank" href="https://github.com/appbaseio/reactivesearch">UI library</a> which we would use to build the app.</p>
<h2 id="heading-lets-dive-into-code">Let's dive into code</h2>
<hr />
<p>We are going to use Reactivesearch.io pipelines to build this app. If you are unfamiliar with it, then you can read it in the <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/concepts/">documentation</a>. In short, they help us to perform operations on data in various stages. The results can then be sent through as response to the front end.</p>
<h3 id="heading-indexing-pipeline">Indexing pipeline:</h3>
<p><strong>Creating and configuring index:</strong></p>
<p>First, we would need to create an index with vectorized data of our product reviews. We would do so by using an indexing pipeline. First things first, we would need to create an index named <code>amazon_reviews</code>. The index also needs to be created such that it is aware of the vectorized form of the data which is later going to be used for KNN search. We specify the field type as <code>knn_vector</code> and also have additional settings. Also, note that the field name is <code>vector_data</code> which would be referenced later when we build the frontend app. You can look at below curl script which you can run from the terminal with <code>reactivesearch_cloud_url</code> of your cluster. We would also need the index.</p>
<pre><code class="lang-bash">curl --location --request PUT <span class="hljs-string">'https://{{reactivesearch_cloud_url}}/amazon_reviews'</span> \
--header <span class="hljs-string">'Content-Type: application/json'</span> \
--data-raw <span class="hljs-string">'{
    "settings": {
        "knn": true,
        "knn.algo_param.ef_search": 100
    },
    "mappings": {
        "properties": {
            "vector_data": {
                "type": "knn_vector",
                "dimension": 1536,
                "method": {
                    "name": "hnsw",
                    "space_type": "cosinesimil",
                    "engine": "nmslib"
                }
            }
        }
    }
}'</span>
</code></pre>
<p><strong>Anatomy of pipeline:</strong></p>
<p>We need a pipeline to first convert the raw product data into vector form. Then, we can take that data and index as <code>knn_vector</code> in search index. This would make us ready to perform KNN search.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679979233678/e9545dcf-1391-458f-8136-29ea4f7fcca9.png" alt class="image--center mx-auto" /></p>
<p>Here is what our pipeline looks like in a config file. If you don't know how to setup an indexing pipeline using Reactivesearch.io you can follow the <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/create-an-indexing-pipeline">documentation.</a> You can paste the below in the pipeline editor and it would convert itself to JSON config. You would need to paste OpenAI api key in <code>envs</code> field to make the pipeline work.</p>
<p>We want to vectorize to text data fields <code>Text</code> and <code>Summary</code> and we specify that in the stage.</p>
<p>Notice that we mention the <code>output_key</code> as <code>vector_data</code>. This is the field where the text information would be stored as vectors.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">Index</span> <span class="hljs-string">pipeline</span> <span class="hljs-string">to</span> <span class="hljs-string">store</span> <span class="hljs-string">vectorized</span> <span class="hljs-string">data</span>
<span class="hljs-attr">routes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/amazon_reviews/_doc</span>
    <span class="hljs-attr">method:</span> <span class="hljs-string">POST</span>
    <span class="hljs-attr">classify:</span>
      <span class="hljs-attr">category:</span> <span class="hljs-string">elasticsearch</span>
      <span class="hljs-attr">acl:</span> <span class="hljs-string">index</span>

<span class="hljs-attr">envs:</span>
  <span class="hljs-attr">openAIApiKey:</span> <span class="hljs-string">${{</span> <span class="hljs-string">OPENAI_API_KEY</span> <span class="hljs-string">}}</span>

<span class="hljs-attr">stages:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">authorize</span> <span class="hljs-string">user</span>
  <span class="hljs-attr">use:</span> <span class="hljs-string">authorization</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">fetch</span> <span class="hljs-string">embeddings</span>
  <span class="hljs-attr">use:</span> <span class="hljs-string">openAIEmbeddingsIndex</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">apiKey:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{openAIApiKey}}</span>"</span>
    <span class="hljs-attr">inputKeys:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Summary</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Text</span>
    <span class="hljs-attr">outputKey:</span> <span class="hljs-string">vector_data</span>
  <span class="hljs-attr">continueOnError:</span> <span class="hljs-literal">false</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">index</span> <span class="hljs-string">data</span>
  <span class="hljs-attr">use:</span> <span class="hljs-string">elasticsearchQuery</span>
  <span class="hljs-attr">needs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">fetch</span> <span class="hljs-string">embeddings</span>
</code></pre>
<p><strong>Indexing data:</strong></p>
<p>Once the pipeline is deployed at the above endpoint we can index the data. We have <a target="_blank" href="https://github.com/awesome-reactivesearch/knn-search-ui/tree/master/backend/index/util">created a script to index data</a>. You can follow the steps after creating the above indexing pipeline.</p>
<h3 id="heading-search-pipeline">Search pipeline:</h3>
<p>We just indexed our textual data (product reviews) inside the index. But when the user performs a search it is still text and not a vector. Hence we can't perform a KNN search without vectorizing the query value. Also, we shouldn't index the search query using the index pipeline because the search query isn't a product review and would be different each time. Instead, we create a new pipeline which transforms the search query using the OpenAI embedding into a vector and then gives it to the search index to perform KNN search.</p>
<p>Config of the pipeline looks like below.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">routes:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">"/amazon_reviews/_reactivesearch"</span>
  <span class="hljs-attr">method:</span> <span class="hljs-string">POST</span>
  <span class="hljs-attr">classify:</span>
    <span class="hljs-attr">category:</span> <span class="hljs-string">reactivesearch</span>

<span class="hljs-attr">envs:</span>
  <span class="hljs-attr">openAIApiKey:</span> <span class="hljs-string">${{</span> <span class="hljs-string">OPENAI_API_KEY</span> <span class="hljs-string">}}</span>

<span class="hljs-attr">stages:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">authorize</span> <span class="hljs-string">user</span>
  <span class="hljs-attr">use:</span> <span class="hljs-string">authorization</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">fetch</span> <span class="hljs-string">embeddings</span>
  <span class="hljs-attr">use:</span> <span class="hljs-string">openAIEmbeddings</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">apiKey:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{openAIApiKey}}</span>"</span>
    <span class="hljs-attr">useWithReactiveSearchQuery:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">continueOnError:</span> <span class="hljs-literal">false</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">use:</span> <span class="hljs-string">reactivesearchQuery</span>
  <span class="hljs-attr">needs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">fetch</span> <span class="hljs-string">embeddings</span>
  <span class="hljs-attr">continueOnError:</span> <span class="hljs-literal">false</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">use:</span> <span class="hljs-string">elasticsearchQuery</span>
  <span class="hljs-attr">continueOnError:</span> <span class="hljs-literal">false</span>
</code></pre>
<p>Below shows a diagram of what might happen in the search phase. We already have the reviews inside the index(Green dots). We get the search query from the user and vectorize it and provide it to the search index, which maps it near to some reviews in the vector space (review 1 and 2). Those are the nearest neighbours and the results sent back to the user would reflect it i.e. Review 1 and 2 would be ranked higher, and would appear before other reviews. Note that we don't index the search query vector. It is temporary and would not be present in the next search.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679980531705/3451b0eb-f3a0-4d9e-9878-735150eb023f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-building-the-ui">Building the UI:</h3>
<p>All the code we are going to show is present inside <a target="_blank" href="https://github.com/awesome-reactivesearch/knn-search-ui">knn-search-demo github repo</a>. We also would like you to go through the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/quickstart/">quick start guide</a> of the Reactivesearch UI library to get some familiarity. The guide will not only show how to set up a boilerplate react app but also how to setup <code>@appbaseio/reactivesearch</code> library. We would primarily modify the <code>src/App.jsx</code> file and would be using <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/reactivebase/"><code>ReactiveBase</code></a>, <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/"><code>SearchBox</code></a>, and <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/result/reactivelist/"><code>ReactiveList</code></a> which the quick start guide already covers.</p>
<p><strong>Connecting to the search pipeline:</strong></p>
<p>In order to send the search query to the pipeline and getting results back we would need to establish a connection with it. We do it by using the <code>endpoint</code> property available on <code>ReactiveBase</code>. You would be able to get the credentials from <a target="_blank" href="https://dash.reactivesearch.io">dash.reactivesearch.io</a>. Also, note that you would have to set <code>reactivesearchAPIConfig.recordAnalytics</code> to <code>false</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> HOST_URL = <span class="hljs-string">"https://{{user}}:{{password}}@{{host}}/amazon_reviews/_reactivesearch"</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">endpoint</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">url:</span> <span class="hljs-attr">HOST_URL</span>,
        <span class="hljs-attr">method:</span> "<span class="hljs-attr">POST</span>",
      }}
      <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">false</span>,
        <span class="hljs-attr">userId:</span> "<span class="hljs-attr">jon</span>",
      }}
    &gt;</span>
      {/* Search and ReactiveList component go here */}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Main</span> /&gt;</span></span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p><strong>Adding search and result component:</strong></p>
<p>In our dataset, there are two fields in which we are mainly interested, <code>Summary</code> and <code>Text</code>. <code>Summary</code> is a short description of the whole review(<code>Text</code>).</p>
<p>We would keep the <code>dataField</code> as <code>Summary</code> inside the <code>Searchbox</code>. This would be used in showing suggestions matching the search query (this would not be KNN search, rather a regular search). We would also control the value of the <code>SearchBox</code> component by using <code>value</code> and <code>onChange</code> props. This would be used later when we transform the normal search request. We also use <code>debounce</code> which fetches suggestions only after an interval when the user has stopped typing.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.jsx</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [searchValue, setSearchValue] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      {<span class="hljs-attr">...configProps</span>}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">{[</span>"<span class="hljs-attr">Summary</span>"]}
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"SearchComponent"</span>
        <span class="hljs-attr">size</span>=<span class="hljs-string">{5}</span>
        <span class="hljs-attr">showClear</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">{searchValue}</span>
        <span class="hljs-attr">debounce</span>=<span class="hljs-string">{SUGGESTION_DEBOUNCE_DELAY}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(value)</span> =&gt;</span> {
          setSearchValue(value);
        }}
      /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>Next, we would add a component to show the results, <code>ReactiveList</code>. Make sure to set the <code>react</code> property to the <code>componentId</code> value of the SearchBox component, i.e. "SearchComponent". We can then use the <code>render</code> prop to customize the look and feel of the results.</p>
<pre><code class="lang-javascript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [searchValue, setSearchValue] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span> {<span class="hljs-attr">...configProps</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveList</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"SearchResult"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">"Summary"</span>
        <span class="hljs-attr">size</span>=<span class="hljs-string">{12}</span>
        <span class="hljs-attr">pagination</span>
        <span class="hljs-attr">react</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">and:</span> "<span class="hljs-attr">SearchComponent</span>" }}
        <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">data</span> }) =&gt;</span> {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mx-5 my-2"</span>&gt;</span>
                {data.map((item) =&gt; (
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{item["Summary"]}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{item["Text"]}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          );
        }}
      /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p><strong>Transforming the request:</strong></p>
<p>Before we transform the request, we need to have several clarifications regarding when the network requests are performed and which network requests are performed.</p>
<ul>
<li><p>Suggestion query: When the user types into the <code>SearchBox</code> and doesn't hit enter, he sees a list of suggestions. Those suggestions are fetched by performing a network request whose <code>type</code> is <code>suggestion</code>. This query is debounced such that it fires only after an interval the user stops typing in the <code>SearchBox</code> and can be controlled by passing a numeric value to <code>debounce</code> prop.</p>
</li>
<li><p>Search query: When the user hits Enter or selects a suggestion then a query of <code>type</code> <code>search</code> is fired. The search query is fired for both the components, <code>SearchBox</code> and <code>ReactiveList</code>. We can identify the component for which the query by looking at <code>id</code> property of the query. This <code>id</code> matches with the <code>componentId</code> of the specified component.</p>
</li>
</ul>
<p>Now we are all set to transform network requests. Our search pipeline works if we provide a query of <code>type</code> <code>search</code> with a <code>vectorDataField</code> which we indexed earlier. We also need to provide a <code>value</code> which is the search query that would be vectorized and used for performing the KNN search. Rough structure of component queries is as below:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"query"</span>: [
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"search"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"search"</span>,
            <span class="hljs-attr">"dataField"</span>: [
                <span class="hljs-string">"Text"</span>,
                <span class="hljs-string">"Summary"</span>
            ],
            <span class="hljs-attr">"vectorDataField"</span>: <span class="hljs-string">"vector_data"</span>,
            <span class="hljs-attr">"value"</span>: <span class="hljs-string">"good dog food"</span>,
            <span class="hljs-attr">"excludeFields"</span>: [
                <span class="hljs-string">"vector_data"</span>
            ]
        }
    ]
}
</code></pre>
<p>We can transform the network request by specifying a prop on <code>ReactiveBase</code> called <code>transformRequest</code>.</p>
<p>We would change only the query of type <code>search</code> and would augment it with <code>vectorDataField</code> and <code>value</code> of the search query. Here we would use the controlled value of the search component.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [searchValue, setSearchValue] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      {<span class="hljs-attr">...otherConfigProps</span>}
      <span class="hljs-attr">transformRequest</span>=<span class="hljs-string">{(req)</span> =&gt;</span> {
        const body = JSON.parse(req.body);
        // Transform query
        body.query = body.query.map((componentQuery) =&gt; {
          if (
            componentQuery.id === "SearchComponent" &amp;&amp;
            componentQuery.type === "search"
          ) {
            return { ...componentQuery, vectorDataField: "vector_data" };
          }
          if (
            componentQuery.id === "SearchResult" &amp;&amp;
            componentQuery.type === "search"
          ) {
            const searchQuery = body.query.find(
              (q) =&gt; q.id === "SearchComponent" &amp;&amp; q.type === "search"
            );
            const searchValue = searchQuery.value;
            delete componentQuery.react;

            return {
              ...componentQuery,
              vectorDataField: "vector_data",
              value: searchValue,
            };
          }
          return componentQuery;
        });
        body.settings = {
          recordAnalytics: true,
          backend: "opensearch",
        };

        const newReq = { ...req, body: JSON.stringify(body) };
        return newReq;
      }}
    &gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p><strong>Finishing Up</strong></p>
<p>Putting all of the above together and styling the app we would get a complete app below. We also add a few sample queries which can be used to perform search. Since the <code>value</code> of <code>SearchBox</code> is controlled, it is easy to make it possible. You can browse the codesandbox below. All the code is primarily present in <code>src/App.jsx</code>.</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/knn-search-ui/tree/master/client?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h3 id="heading-summary">Summary</h3>
<p>In this blog post, we discuss the implementation of K-nearest neighbors (KNN) search to improve search results, recommend items, and categorize data into clusters. We explain the concept of vector spaces and how they relate to KNN search. We then dive into the implementation of a search UI for a dataset of Amazon reviews, utilizing OpenAI's Vector Embeddings API, OpenSearch as the search engine backend, and ReactiveSearch for cloud hosting of OpenSearch and to build the UI for the app. We provide code examples and explanations for each step of the process, including indexing pipelines, search pipelines, and building the UI with ReactiveSearch components. Finally, we show how to transform the network request to complete the app.</p>
]]></content:encoded></item><item><title><![CDATA[7 Powerful Ways To Use The SearchBox Component]]></title><description><![CDATA[The SearchBox component from the @appbaseio/reactivesearch library provides a powerful way to build reactive search UIs that are connected to one or more Elasticsearch indices. It comes with many different props that enable you to customize the user ...]]></description><link>https://blog.reactivesearch.io/7-uses-searchbox-component-reactivesearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/7-uses-searchbox-component-reactivesearch</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Siddharth Kothari]]></dc:creator><pubDate>Wed, 29 Mar 2023 14:26:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680099343127/29eac24e-814c-4e42-a272-8e3351ca6431.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <code>SearchBox</code> component from the <strong>@appbaseio/reactivesearch</strong> library provides a powerful way to build reactive search UIs that are connected to one or more Elasticsearch indices. It comes with many different props that enable you to customize the user interface and behavior of the search box in various ways. In this post, we'll explore some of the different SearchBox UIs you can create by leveraging these props.</p>
<p>Let's take a look at how the <code>SearchBox</code> component can be used in multiple ways to cover different use cases.</p>
<h2 id="heading-voice-search"><strong>Voice Search</strong></h2>
<p>One interesting way to enhance the SearchBox is to enable voice search. You can do this by setting the <code>showVoiceSearch</code> prop to <code>true</code>. This adds a microphone icon to the search box, allowing users to search using their voice instead of typing. This can be especially useful for users on mobile devices or for those with accessibility needs.</p>
<pre><code class="lang-javascript">    &lt;SearchBox
        <span class="hljs-comment">// ... other props</span>
        showVoiceSearch={<span class="hljs-literal">true</span>}            
    /&gt;
</code></pre>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithVoiceSearch?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-autosuggestion-search"><strong>Autosuggestion Search</strong></h2>
<p>Another powerful feature of the SearchBox is its ability to provide autosuggestions to users as they type. This can be accomplished using the <code>autosuggest</code> prop. When <code>autosuggest</code> prop is set to <code>true</code>, the SearchBox will query the index for suggestions based on the user's input. There are different types of autosuggestions you can enable using other props:</p>
<ul>
<li><p><strong>Index Suggestions -</strong> Index suggestions are suggestions that show up from the indexed data in your search engine.</p>
</li>
<li><p><strong>Popular Suggestions -</strong> Popular suggestions are displayed from collecting the end-user search data based on popularity.</p>
</li>
<li><p><strong>Recent Suggestions</strong> - Recent suggestions are displayed from the current user's recent searches.</p>
</li>
</ul>
<p>You can individually configure how each of these suggestions show when you enable autosuggestions.</p>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithAutosuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-instant-search">Instant Search</h2>
<p>One of the most powerful features is instant search, which allows search results to update in real time as the user types in the search box. This can be enabled by setting the <code>autosuggest</code> prop to false. With instant search, users can quickly see which search terms are yielding the most relevant results, and adjust their query as needed.</p>
<pre><code class="lang-javascript">    &lt;SearchBox
        <span class="hljs-comment">// ... other props</span>
        autosuggest={<span class="hljs-literal">false</span>}            
    /&gt;
</code></pre>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithInstantSearch?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-keyboard-shortcuts">Keyboard Shortcuts</h2>
<p>Another useful feature is keyboard shortcuts, which can improve the speed and efficiency of the search process. The <code>focusShortcuts</code> prop allows you to define a list of keyboard shortcuts that will focus the search box when pressed.</p>
<p>For example, if you want to focus the search box when the user presses the <code>/</code> key, you can set <code>focusShortcuts</code> to <code>['/']</code>. You can also define more complex keyboard shortcuts, such as <code>SHIFT+A</code>, by separating the key names with a <code>+</code>.</p>
<p>The string is case-insensitive and space in-between is optional. You can also pass several shortcuts <code>["SHIFT+A", "SHIFT+B"]</code>.</p>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithKeyboardShortcuts?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-instant-search-with-pill-based-suggestions">Instant Search with Pill based Suggestions</h2>
<p>Combine Instant Search with Pill-based Suggestions to provide users with a highly interactive search experience. Instant Search offers real-time search results, while Pill-based Suggestions enable users to refine their search with predefined tags or filters displayed as pill-shaped buttons. This combination helps users easily navigate large datasets by providing a structured and intuitive way to explore data. It also reduces the likelihood of spelling errors or other mistakes, as users can quickly select the appropriate tag or filter to refine their search results.</p>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithPillSuggestions?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<p>The example here uses:</p>
<ol>
<li><p>controlled usage for triggering the result query as per the user's wish/ needs. <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/#onchange">Click here to know more</a> 🔗</p>
</li>
<li><p><code>render</code> prop to render suggestions in a pill-based 💊 UI. <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/#render">Click here to know more</a> 🔗</p>
</li>
</ol>
<h2 id="heading-tag-based-search-ui">Tag-based Search UI</h2>
<p>Create a highly customizable search experience with the Tag-based search UI, allowing users to select multiple search terms from a list of suggestions. Use the renderSelectedTags prop to define custom rendering functions for selected tags, offering users a more visually appealing interface or adding extra functionality to the tags.</p>
<pre><code class="lang-javascript">&lt;SearchBox
    componentId=<span class="hljs-string">"searchSensor"</span>
    <span class="hljs-comment">// ... other props</span>
    mode=<span class="hljs-string">"tag"</span>
/&gt;
</code></pre>
<p><code>renderSelectedTags</code> is used to custom render tags when the <code>mode</code> is set to <code>tag</code>.</p>
<p>Function param accepts an object with the following properties:</p>
<ul>
<li><p><code>values</code>: <code>Array&lt;String&gt;</code> is an array of selected values</p>
</li>
<li><p><code>handleClear</code>: <code>Function - (string) =&gt; void</code> is a function to clear a tag value. It accepts the tag value(String) as a parameter</p>
</li>
<li><p><code>handleClearAll</code>: <code>Function - () =&gt; void</code> is a function to clear all selected values</p>
</li>
</ul>
<pre><code class="lang-javascript">    &lt;SearchBox
        id=<span class="hljs-string">"search-component"</span>
        mode=<span class="hljs-string">"tag"</span>
        renderSelectedTags={<span class="hljs-function">(<span class="hljs-params">{ 
            values = [], 
            handleClear, 
            handleClearAll }</span>) =&gt;</span> {
                <span class="hljs-comment">// return custom-rendered tags </span>
            }
        }
    /&gt;
</code></pre>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBoxWithTags?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-featured-suggestions">Featured Suggestions</h2>
<p>Featured suggestions are a powerful feature in Reactive Search that can be used to display a set of custom search suggestions at the top of your search results. These suggestions are designed to be prominently displayed to your users and can be used to guide them toward popular or high-value search terms.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1654727133522/ok9bNPl2u.png?w=1600&amp;h=840&amp;fit=crop&amp;crop=entropy&amp;auto=compress,format&amp;format=webp" alt="Different ways to use the SearchBox component - Featured Suggestions" class="image--center mx-auto" /></p>
<p>Some extensive use cases include:</p>
<ul>
<li><p><strong>In-app navigation</strong>: Offer an in-app navigation experience through your search bar allowing users to navigate through your site or dashboard without needing to jump between multiple menus, or relying on header/footer navigations.</p>
</li>
<li><p><strong>Onboarding of new users</strong>: New users who are not familiar with the product or how to use it can just go to the search bar and search for the documentation of the product or search for help if needed. Through the in-app navigation, a user can search for frequently asked questions and navigate to the page effortlessly.</p>
</li>
<li><p><strong>User productivity with actions</strong>: When actions like toggling between the light mode or dark mode of your app can be done directly from the search bar, it can further enhance the search user experience.</p>
</li>
</ul>
<p>To enable featured suggestions, you need to first set Featured suggestions via the ReactiveSearch dashboard, and then use the <code>enableFeaturedSuggestions</code> prop with <code>searchboxId</code> at query time.</p>
<p>Following is the dashboard workflow for setting the featured suggestions.</p>
<iframe src="https://scribehow.com/embed/SearchBoxESOpenSearch_workflow__67pssprZSv6hOEdWj2eoIg" width="100%" height="640"></iframe>

<p>Following is the query time usage for returning featured suggestions.</p>
<pre><code class="lang-javascript">&lt;SearchBox
    <span class="hljs-comment">// .. other props</span>
    enableFeaturedSuggestions={<span class="hljs-literal">true</span>}
    searchboxId=<span class="hljs-string">"your_created_searchbox_id"</span>
    featuredSuggestionsConfig={{
        <span class="hljs-attr">maxSuggestionsPerSection</span>: <span class="hljs-number">10</span>,
        <span class="hljs-attr">sectionsOrder</span>: [<span class="hljs-string">'repositories'</span>, <span class="hljs-string">'docs'</span>, <span class="hljs-string">'functions'</span>],
    }}
/&gt;
</code></pre>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/q-n-a-search-ui/tree/plain-search-app/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<p>Follow this guide to know more about Featured suggestions and how you can build one using the ReactiveSearch Control plane <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/ui-builder/searchbox-ui/build-searchbox-with-elasticsearch/">here</a> 🚀.</p>
<h3 id="heading-ai-answer-coming-soon">AI Answer: Coming Soon ⏳</h3>
<p>Get an AI Answer powered by ChatGPT, GPT-3 and GPT-4 models right in your SearchBox. Here's an example of how this works.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://player.vimeo.com/video/812826736?h=bdae7b3a33&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479">https://player.vimeo.com/video/812826736?h=bdae7b3a33&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479</a></div>
<p> </p>
<h3 id="heading-summary">Summary</h3>
<p>In summary, the <code>SearchBox</code> component from the @appbaseio/reactivesearch library enables you to create diverse, user-centric search experiences that cater to a wide range of use-cases. By leveraging its various customization options, you can provide your users with an intuitive, efficient, and engaging search experience that truly meets their needs.</p>
<p>Consider experimenting with different combinations of these features to find the perfect balance for your specific search scenario. As you refine your search experience, you'll be able to deliver an even more engaging experience to your end-users.</p>
]]></content:encoded></item><item><title><![CDATA[Discover Personalized Search: Tailor-Made Experiences with ReactiveSearch and Elasticsearch]]></title><description><![CDATA[Imagine scrolling through your favorite platforms like YouTube, Twitter, or TikTok, and instantly finding a feed curated just for you. Personalized search is the magic behind this seamless experience, offering tailored results based on your interests...]]></description><link>https://blog.reactivesearch.io/discover-personalized-search-tailor-made-experiences-with-reactivesearch-and-elasticsearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/discover-personalized-search-tailor-made-experiences-with-reactivesearch-and-elasticsearch</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[UI]]></category><dc:creator><![CDATA[Shubham Shah]]></dc:creator><pubDate>Wed, 15 Mar 2023 15:22:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678885735924/3679513b-ead2-4b85-8773-025c73add33f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine scrolling through your favorite platforms like YouTube, Twitter, or TikTok, and instantly finding a feed curated just for you. Personalized search is the magic behind this seamless experience, offering tailored results based on your interests and past behavior. For example, if two users search "football," the fan of American football and the fan of soccer will see completely different results, thanks to a personalized search taking their search history and behavior into account.</p>
<p>By employing user behavior tracking, personalized search delivers highly relevant results, boosting user satisfaction. If you're intrigued by the prospect of integrating personalized search into your website or search engine, you're in the right place.</p>
<p>Let's explore some popular applications of personalized search and how user preferences come into play:</p>
<ol>
<li><p>Crafting algorithmic feeds based on browsing history: Platforms like YouTube and TikTok utilize personalized search to generate algorithmic feeds of videos that cater to users' interests. By analyzing watch history, likes, comments, shares, and other interactions, these platforms gain insight into users' preferences, serving up relevant and captivating content.</p>
</li>
<li><p>Curating feeds from user-selected topics: Personalized search also shines on news sites, Twitter, Quora, and other platforms where users input their interests or preferred topics. These platforms use this information to create feeds teeming with content that users find relevant and engaging. For instance, a news site might ask users to choose topics such as politics, sports, or entertainment, and then provide news articles on those subjects in their feed.</p>
</li>
</ol>
<p>Some websites blend both approaches, using explicit user-selected topics along with tracking user history, to personalize search results and elevate the user experience. So go ahead, dive into the world of personalized search, and deliver content that resonates with your audience.</p>
<p>Here's the final personalized feed search UI that you will be building by going through this post:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/feed-search-ui/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h1 id="heading-the-building-blocks">The Building Blocks</h1>
<p>To create a personalized search experience, we'll be using the ReactiveSearch platform and the open-source Apache 2.0 licensed ReactiveSearch UI kit for React.</p>
<ul>
<li><p>For this blog post's hosted UI, we have utilized a hosted ReactiveSearch service connected to an Elasticsearch backend. ReactiveSearch can also be deployed in self-host mode, as well as connect to other engines: OpenSearch, Solr, MongoDB and OpenAI are currently supported.</p>
</li>
<li><p>To construct the search UI, we'll employ the React frontend UI components library from ReactiveSearch, which simplifies the development of search components such as search boxes, filters, and result displays. This streamlined approach paves the way for a truly customized search experience.</p>
</li>
</ul>
<h2 id="heading-building-a-search-pipeline">Building a search pipeline</h2>
<p>Let's take a closer look at how traditional search functions. When a user searches for a specific term, like "football," the search engine retrieves documents from the search index. These documents are then ranked to determine the order in which they appear to the user. For our purposes, however, we want the search engine to "boost" or "promote" certain documents based on the end-users preferences. To achieve this, we'll introduce an additional step to promote relevant results before presenting them to the user.</p>
<p>In ReactiveSearch, this multi-step search query process, which adds context and more, is executed through pipelines. A pipeline consists of a sequence of search processing stages that query one or more search engines and return results to the user. We'll input the user's query into the search engine, which will collect and rank all pertinent documents. These documents will then advance to our "boost" step, where we'll enhance user-specific terms to ultimately deliver finely-tuned, personalized results.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678370893481/eea800e7-4749-42ef-aafd-c467acfbdc41.png" alt class="image--center mx-auto" /></p>
<p>In addition to the steps mentioned earlier, we can integrate a step in the pipeline to ensure that only authorized users can access the index. This authorization step can be positioned between the request processing and the search engine's ranking of documents. Pipelines offer incredible versatility, enabling you to incorporate features such as integrating results from ChatGPT, Google's Knowledge Graph, performing vector search, and more. To learn about ReactiveSearch pipelines and their capabilities, be sure to consult the documentation. You can learn about <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/concepts/">pipelines in the docs.</a></p>
<h3 id="heading-authoring-a-pipeline-using-the-dashboard"><strong>Authoring a pipeline using the dashboard</strong></h3>
<p>To begin, navigate to <a target="_blank" href="http://dash.reactivesearch.io">dash.reactivesearch.io</a> and select "Pipelines" from the sidebar menu. Next, click on "Create a Pipeline." You'll find several pre-built templates catering to a wide range of use-cases; for our purposes, we'll opt for the "Basic Template." Upon making your selection, a text editor will appear, signaling the completion of this step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678355524394/dbe0ea03-9e0c-4a28-bcf9-bf3b9776e09c.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-making-of-a-pipeline"><strong>The making of a pipeline</strong></h3>
<p>Upon opening the pipeline, you'll notice several components. Your pipeline should resemble the code snippet below. The first elements are <code>enabled</code> and <code>description</code>, both of which are self-explanatory.</p>
<p>Next, the <code>routes</code> property allows us to attach the pipeline to specific API endpoints, which a frontend can make a REST API call to retrieve results. We'll set the path within <code>routes</code> to <code>/query-rules-boost</code>.</p>
<p>Following this, we have <code>envs</code>, which are used to set the pipeline's operational context, including the index, domain settings, and more. We'll set the index to <code>movies-demo-app</code> here.</p>
<p>We would now see various parts of the pipeline. You would have a pipeline that would look something like below code snippet. We first have <code>enabled</code> and <code>description</code>. Their meanings are straightforward. <code>enabled</code> tells whether the pipeline is active and <code>description</code> provides some useful information about what the pipeline does.</p>
<p>Then, we have <code>routes</code> property which allows us to mount the pipeline on specific API endpoints which we can call to get the results. We would modify the path inside <code>routes</code> to <code>/query-rules-boost</code>.</p>
<p>After that, we have <code>envs</code> which describe the context in which the pipeline is running such as the <code>index</code>, domain settings etc. We would change the index to <code>movies-demo-app</code>.</p>
<p>We would also remove the script <code>modify_request.js</code> and it's corresponding reference.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"enabled"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"description"</span>: <span class="hljs-string">"Template to create a pipeline"</span>,
    <span class="hljs-string">"routes"</span>: [
        {
            <span class="hljs-string">"path"</span>: <span class="hljs-string">"/query-rules-boost"</span>,
            <span class="hljs-string">"method"</span>: <span class="hljs-string">"POST"</span>,
            <span class="hljs-string">"classify"</span>: {
                <span class="hljs-string">"category"</span>: <span class="hljs-string">"reactivesearch"</span>
            }
        }
    ],
    <span class="hljs-string">"envs"</span>: {
        <span class="hljs-string">"index"</span>: [
            <span class="hljs-string">"movies-demo-app"</span>
        ]
    },
    <span class="hljs-string">"stages"</span>: [
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"auth"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"reactivesearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"es_query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"elasticsearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        }
    ]
}
</code></pre>
<p>Stages are the building blocks of the pipeline, representing the steps of data transformation, as illustrated in the diagram above. These stages are executed sequentially, constructing the results that are ultimately presented to the user. The "auth" stage verifies whether a user has permission to access the index or perform the intended action. Next, the <code>reactivesearch</code> query stage converts the ReactiveSearch query into an Elasticsearch query. Finally, the <code>es_query</code> stage executes a query on the Elasticsearch index, collecting the results that are then delivered to the user.</p>
<h3 id="heading-incorporating-a-stage-to-refine-search-results">Incorporating a stage to refine search results</h3>
<p>Following the diagram, we can introduce an additional stage after retrieving results from the Elasticsearch index to enhance specific search outcomes. We can incorporate a stage called <code>boost</code>, you can see its <a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/query-rules/#boost-results-by-score">reference in the documentation</a>.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">// Same pipeline options as above with additional "boost" stage</span>
    <span class="hljs-string">"stages"</span>: [
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"auth"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"authorization"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"reactivesearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"es_query"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"elasticsearchQuery"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>
        },
        <span class="hljs-comment">/*
        Score documents containing 
        "Harry Potter" better
        */</span>
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boost-harry-potter"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-comment">// The field to query in documents</span>
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"original_title"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"Harry Potter"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span> <span class="hljs-comment">// Boost only top 3 docs</span>
            }
        }
    ]
}
</code></pre>
<p>We're setting the stage to boost up to 3 documents that contain the value <code>Harry Potter</code> in the <code>original_title</code> index field. We start with a very specific value to test out the personalization in action.</p>
<p>The dashboard provides a realtime validation testing view where we can test the above pipeline to confirm that we indeed get the top 3 documents of the hits containing Harry Potter in their <code>original_title</code> field.</p>
<h2 id="heading-building-a-search-ui">Building a search UI</h2>
<p>With our pipeline configured, it's time to witness it in action. We'll use the React flavor of the ReactiveSearch library to develop the UI. However, you can use the ReactiveSearch dashboard's no-code UI builder, or Vue.JS, vanilla JS, React Native or Flutter libraries to do this. <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/gettingstarted">See the choosing UI library section</a>.</p>
<p><strong>npx and go!</strong></p>
<p>We will use <a target="_blank" href="https://create-react-app.dev/">create-react-app</a> to get the boilerplate code. Execute the following command in the terminal to create a React app named "feed-search":</p>
<pre><code class="lang-bash">npx create-react-app feed-search
<span class="hljs-built_in">cd</span> feed-search
yarn add @appbaseio/reactivesearch
code .   <span class="hljs-comment"># Fire up your code editor</span>
</code></pre>
<p><code>@appbaseio/reactivesearch</code> is ReactiveSearch's React npm package.</p>
<p>As you open the code editor, notice the <code>src/App.js</code> file, we will be editing this file primarily to build the search UI.</p>
<p><strong>Connecting to the pipeline</strong></p>
<p>Let's connect the pipeline using the <code>ReactiveBase</code> component. Each Reactivesearch UI has a <code>ReactiveBase</code> component as the connector component, where you configure the index and cluster URL. Here, we do this by specifying the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/reactivebase/#endpoint"><code>endpoint</code> property</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.js</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
        <span class="hljs-attr">endpoint</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">url:</span> "<span class="hljs-attr">https:</span>//<span class="hljs-attr">a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61</span>@<span class="hljs-attr">appbase-demo-ansible-abxiydt-arc.searchbase.io</span>/<span class="hljs-attr">query-rules-boost</span>",
          <span class="hljs-attr">method:</span> "<span class="hljs-attr">POST</span>",
        }}
        <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">true</span>,
        }}
      &gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Now we can add some more components like a <code>SearchBox</code> which shows a search bar where a user inputs their search query. We can also add a <code>ReactiveList</code> to display the results returned from the pipeline. You can find more information in the docs for <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/search/searchbox/"><code>SearchBox</code></a> and <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/result/reactivelist/"><code>ReactiveList</code></a>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase,
  ReactiveList,
  ResultCard,
  SearchBox,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
        <span class="hljs-attr">endpoint</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">url:</span> "<span class="hljs-attr">https:</span>//<span class="hljs-attr">a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61</span>@<span class="hljs-attr">appbase-demo-ansible-abxiydt-arc.searchbase.io</span>/<span class="hljs-attr">query-rules-boost-static</span>",
          <span class="hljs-attr">method:</span> "<span class="hljs-attr">POST</span>",
        }}
        <span class="hljs-attr">reactivesearchAPIConfig</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">recordAnalytics:</span> <span class="hljs-attr">false</span>,
        }}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row mt-4 p-3"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">SearchBox</span>
            <span class="hljs-attr">dataField</span>=<span class="hljs-string">{[</span>"<span class="hljs-attr">original_title</span>"]}
            <span class="hljs-attr">componentId</span>=<span class="hljs-string">"BookSensor"</span>
            <span class="hljs-attr">highlight</span>
            <span class="hljs-attr">URLParams</span>
            <span class="hljs-attr">size</span>=<span class="hljs-string">{5}</span>
            <span class="hljs-attr">enablePredictiveSuggestions</span>
            <span class="hljs-attr">showClear</span>
            <span class="hljs-attr">renderNoSuggestion</span>=<span class="hljs-string">{()</span> =&gt;</span> "No suggestions found."}
          /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveList</span>
            <span class="hljs-attr">componentId</span>=<span class="hljs-string">"SearchResult"</span>
            <span class="hljs-attr">dataField</span>=<span class="hljs-string">"original_title"</span>
            <span class="hljs-attr">size</span>=<span class="hljs-string">{12}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"result-list-container"</span>
            <span class="hljs-attr">pagination</span>
            <span class="hljs-attr">react</span>=<span class="hljs-string">{{</span>
              <span class="hljs-attr">and:</span> "<span class="hljs-attr">BookSensor</span>",
            }}
            <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">data</span> }) =&gt;</span> {
              // Render results returned from the pipeline
            }}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>You can look at the final UI below. Observe that we boost 3 "Harry Potter" documents when the search query is empty.</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/feed-search-ui/tree/step-1/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-getting-user-preferences">Getting user preferences</h2>
<p>We aim to promote specific search results dynamically based on an individual's preferences. This can be achieved by tracking browser history and boosting search results based on recent searches. In this case, we'll allow users to explicitly select the topics they're interested in using a straightforward tag-based selection. Users can choose up to three movie genres, which the search engine will then prioritize in the search results.</p>
<p>To accomplish this, we will create a component inside a file called <code>TagSelector.js</code>. This will feature a select input, enabling users to choose their preferred genres. Additionally, we'll need to incorporate the selected options into our request payload. This data will then be utilized within the pipeline to enhance search results. We can achieve this by using the <code>transformRequest</code> prop of <code>ReactiveBase</code>, which modifies the search request before making a call to the pipeline API.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> {
  ReactiveBase,
  ReactiveList,
  ResultCard,
  SearchBox,
  SelectedFilters,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> TagSelector <span class="hljs-keyword">from</span> <span class="hljs-string">"./TagSelector"</span>;

<span class="hljs-keyword">import</span> styles <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.module.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [options, setOptions] = useState([<span class="hljs-string">"Comedy"</span>]);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (options) {
      setIsLoading(<span class="hljs-literal">true</span>);
      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setIsLoading(<span class="hljs-literal">false</span>);
      }, <span class="hljs-number">1000</span>);
    }
  }, [options]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-3"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.userPreference}</span>&gt;</span>User Preference:<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TagSelector</span> <span class="hljs-attr">options</span>=<span class="hljs-string">{options}</span> <span class="hljs-attr">setOptions</span>=<span class="hljs-string">{setOptions}</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      {isLoading ? (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row mt-4 p-3"</span>&gt;</span>Resetting topics...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ) : (
        <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
          {<span class="hljs-attr">...samePropsAsBefore</span>}
          <span class="hljs-attr">transformRequest</span>=<span class="hljs-string">{(req)</span> =&gt;</span> {
            const body = JSON.parse(req.body);
            body.customData = options;
            const newReq = { ...req, body: JSON.stringify(body) };
            return newReq;
          }}
        &gt;
          {/*
              Search and other UI components
          */}
        <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>If you try out the live search UI, the request body payload should look like below:</p>
<pre><code class="lang-javascript">POST /query-rules-boost
{
    <span class="hljs-attr">customData</span>: [<span class="hljs-string">"Comedy"</span>, <span class="hljs-string">"Horror"</span>] <span class="hljs-comment">// user selected options</span>
}
</code></pre>
<h2 id="heading-incorporating-preferences-within-the-pipeline">Incorporating Preferences within the Pipeline</h2>
<p><strong>Accessing Context</strong></p>
<p>With data from the frontend now structured within the request body, we need to access it inside the pipeline using context.</p>
<p>Each pipeline operates within a specific context. This context can be accessed within the stages to dynamically modify their input. First, however, we must parse the request and incorporate the selected options into the context. To achieve this, we'll create a script called <code>promoteParams.js</code>, which will be added as a stage prior to our <strong>boost</strong> stage. This script introduces an <code>additionalParams</code> property to the context object, which can be accessed within the stage as {{additionalParams}}.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// promoteParams.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> requestBody = <span class="hljs-built_in">JSON</span>.parse(context.request.body);
    <span class="hljs-keyword">const</span> customData = (requestBody &amp;&amp; requestBody.customData) || [];

    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">additionalParams</span>: {
            <span class="hljs-attr">paramOne</span>: customData[<span class="hljs-number">0</span>] || <span class="hljs-string">''</span>,
            <span class="hljs-attr">paramTwo</span>: customData[<span class="hljs-number">1</span>] || <span class="hljs-string">''</span>,
            <span class="hljs-attr">paramThree</span>: customData[<span class="hljs-number">2</span>] || <span class="hljs-string">''</span>,
        },
    };
}
</code></pre>
<p>You can add the script as a stage using the dashboard. First, reference the script inside the pipeline as a stage and then add a file named <code>promoteParams.js</code> with the above content. Add the script before the boost stages.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">// Same pipeline options as above with additional script stage</span>
    <span class="hljs-string">"stages"</span>: [
        <span class="hljs-comment">//Other stages same as above</span>
        <span class="hljs-comment">// Add below stage before the boost stage</span>
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"addPromoteParams"</span>,
            <span class="hljs-string">"scriptRef"</span>: <span class="hljs-string">"promoteParams.js"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boost-harry-potter"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-comment">// The field to query in documents</span>
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"original_title"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"Harry Potter"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span> <span class="hljs-comment">// Boost only top 3 docs</span>
            }
        }
    ]
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678381239056/bc5a34d6-b4b5-472a-bb89-4467bafb77c9.gif" alt="Custom script stage" class="image--center mx-auto" /></p>
<p><strong>Using Context:</strong></p>
<p>Each pipeline has a context in which it's executing. We can access this context as an input to a stage.</p>
<p>Now we can reference the context inside the boost stages. We also add two more boost stages to handle all three genres.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">// Same pipeline options as above with dynamic input from context</span>
    <span class="hljs-string">"stages"</span>: [
        <span class="hljs-comment">// Other stages</span>
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"addPromoteParams"</span>,
            <span class="hljs-string">"scriptRef"</span>: <span class="hljs-string">"promoteParams.js"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boostOne"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"genres"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"{{additionalParams.paramOne}}"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span>
            }
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boostTwo"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"genres"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"{{additionalParams.paramTwo}}"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span>
            }
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"boostThree"</span>,
            <span class="hljs-string">"use"</span>: <span class="hljs-string">"boost"</span>,
            <span class="hljs-string">"continueOnError"</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-string">"inputs"</span>: {
                <span class="hljs-string">"dataField"</span>: <span class="hljs-string">"genres"</span>,
                <span class="hljs-string">"value"</span>: [
                    <span class="hljs-string">"{{additionalParams.paramThree}}"</span>
                ],
                <span class="hljs-string">"boostType"</span>: <span class="hljs-string">"score"</span>,
                <span class="hljs-string">"boostMaxDocs"</span>: <span class="hljs-number">3</span>
            }
        }
    ]
}
</code></pre>
<p>Congratulations, we've now updated the pipeline to use the user preferences passed from the frontend search UI. The complete frontend code and live hosting of the search UI is shown below:</p>
<iframe src="https://codesandbox.io/embed/github/awesome-reactivesearch/feed-search-ui/tree/main/?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h2 id="heading-summary">Summary</h2>
<p>In this post, we explored the concept of personalized search and its implementation using ReactiveSearch and Elasticsearch. We learned how to create a pipeline with multiple stages to process and enhance search results based on user preferences. Additionally, we built a search UI using the ReactiveSearch library and a React app, allowing users to explicitly define their interests through a tag-based selection. We also delved into accessing and modifying context within the pipeline stages to dynamically adjust search results. By combining these techniques, we're able to provide users with a customized and engaging search experience.</p>
<p>ReactiveSearch Pipelines offer a V8 engine-based JavaScript runtime (similar to CloudFlare workers) that you can run alongside search engines to craft relevant search experiences for SaaS, Enterprise and E-Commerce use cases.</p>
<p>Additional links to explore more:</p>
<p><a target="_blank" href="https://reactivesearch.io/">https://reactivesearch.io/</a></p>
<p><a target="_blank" href="https://github.com/awesome-reactivesearch/feed-search-ui">https://github.com/awesome-reactivesearch/feed-search-ui</a></p>
<p><a target="_blank" href="https://docs.reactivesearch.io/docs/pipelines/how-to/">https://docs.reactivesearch.io/docs/pipelines/how-to/</a></p>
]]></content:encoded></item><item><title><![CDATA[SEO checklist for ReactiveSearch]]></title><description><![CDATA[For your website to appear in a search engine’s results, the search engine needs to be able to discover, crawl, parse, and render the key pages of your site.
When building your search experience with client-side JavaScript, you may worry that search ...]]></description><link>https://blog.reactivesearch.io/seo-checklist-for-reactivesearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/seo-checklist-for-reactivesearch</guid><category><![CDATA[SEO]]></category><category><![CDATA[Search Engines]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[reactivesearch]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Muhammad Ashraf]]></dc:creator><pubDate>Tue, 21 Feb 2023 12:28:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673581718458/39dd1788-f993-4550-ab27-c845584f2e1a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For your website to appear in a search engine’s results, the search engine needs to be able to <strong>discover, crawl, parse, and render the key pages of your site</strong>.</p>
<p>When building your search experience with client-side JavaScript, you may worry that search engines can’t crawl or render your URLs and that this may hurt your ranking. While some search engines are getting better at processing sites with client-side JavaScript search, you can also do a lot to optimize your website for search engines without sacrificing the user experience.</p>
<h2 id="heading-your-search-result-pages-are-directly-accessible-through-a-url"><strong>Your search result pages are directly accessible through a URL</strong></h2>
<p>As users interact with your search interface, <strong>the URL should dynamically update and reflect the refinements they’ve selected and their actions</strong>.</p>
<p>This allows them to share links to your website, enhancing the overall usability of your search and encouraging <a target="_blank" href="https://wikipedia.org/wiki/Backlink">backlinking</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBox?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/SearchBox?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>
<p> </p>
<p>Try opening the code sandbox <a target="_blank" href="https://fwmwo.csb.app/?BookSensor=%22Harry+Potter%22">in a new tab</a> and see how the URLs are changing with every significant search interaction.</p>
<p>The approach clearly improves the user experience, but there are concerns or well-spread myths about dynamic URLs that require busting.</p>
<p><a target="_blank" href="https://developers.google.com/search/blog/2008/09/dynamic-urls-vs-static-urls#myth:-dynamic-urls-cannot-be-crawled.">#1 Myth: "Dynamic URLs cannot be crawled."</a></p>
<p><a target="_blank" href="https://developers.google.com/search/blog/2008/09/dynamic-urls-vs-static-urls#myth:-dynamic-urls-are-okay-if-you-use-fewer-than-three-parameters.">#2 Myth: "Dynamic URLs are okay if you use fewer than three parameters."</a></p>
<p>Having read the above references we can safely conclude that Dynamic URLs don't necessarily hurt your SEO and should be favored.</p>
<h2 id="heading-your-widgets-use-crawlable-a-tags-with-href-attributes">Your widgets use crawlable <code>a</code> tags with <code>href</code> attributes</h2>
<p>Search Engines can follow your links only if they <a target="_blank" href="https://developers.google.com/search/docs/crawling-indexing/links-crawlable"><strong>use proper</strong> <code>&lt;a&gt;</code> tags with <strong>resolvable URLs</strong>.</a></p>
<ul>
<li><p><strong>✅ Dos:</strong></p>
<ul>
<li><p><code>&lt;a href="</code><a target="_blank" href="https://example.com"><code>https://example.com</code></a><code>"&gt;</code></p>
</li>
<li><p><code>&lt;a href="/relative/path/file"&gt;</code></p>
</li>
</ul>
</li>
</ul>
<p>    Note that links are also crawlable when you use JavaScript to insert them into a page dynamically as long as it uses the markup shown above.</p>
<p>    <strong>don'ts:</strong></p>
<ul>
<li><p><code>&lt;a routerLink="some/path"&gt;</code></p>
</li>
<li><p><code>&lt;span href="</code><a target="_blank" href="https://example.com"><code>https://example.com</code></a><code>"&gt;</code></p>
</li>
<li><p><code>&lt;a onclick="goto('</code><a target="_blank" href="https://example.com"><code>https://example.com</code></a><code>')"&gt;</code></p>
</li>
</ul>
<blockquote>
<p>This is relevant when you have opted to use custom markup.</p>
</blockquote>
<h2 id="heading-your-urls-are-readable"><strong>Your URLs are readable</strong></h2>
<p>A site's URL structure should be as simple as possible. Consider organizing your content so that URLs are constructed logically and in a manner that is most intelligible to humans.</p>
<p><img src="https://neilpatel.com/wp-content/uploads/2017/08/image6-1.png" alt="image6 1" class="image--center mx-auto" /></p>
<p>For every ReactiveSearch component/ widget, the basic routing configuration injects every search refinement into the URL as a <a target="_blank" href="https://en.wikipedia.org/wiki/Query_string">query string</a> parameter. These parameters are inferred from the global search state.</p>
<pre><code class="lang-xml">https://fwmwo.csb.app/?BookName=Paradise

// The below information is saved legibly in the URL params above
// componentId = 'BookName'
// component's value = 'Paradise'

// you can visit the above mentioned app to see how the URls are handled out of the box
</code></pre>
<p><strong>✅ Dos</strong></p>
<ul>
<li><p>Simple, descriptive words in the URL</p>
<p>  <code>https://en.wikipedia.org/wiki/Aviation</code></p>
</li>
<li><p>Localized words in the URL, if applicable.</p>
<p>  <code>https://www.example.com/lebensmittel/pfefferminz</code></p>
</li>
<li><p>Use UTF-8 encoding as necessary. For example, the following example uses UTF-8 encoding for Arabic characters in the URL:</p>
<p>  <code>https://www.example.com/%D9%86%D8%B9%D9%86%D8%A7%D8%B9/%D8%A8%D9%82%D8%A7%D9%84%D8%A9</code></p>
</li>
</ul>
<p><strong>🚫 Don'ts</strong></p>
<ul>
<li><p>Using non-ASCII characters in the URL:</p>
<p>  <code>https://www.example.com/نعناع</code> , <code>https://www.example.com/杂货/薄荷</code>, etc.</p>
</li>
<li><p>Unreadable, long ID numbers in the URL:</p>
<p>  <code>https://www.example.com/index.php?id_sezione=360&amp;sid=3a5ebc944f41daa6f849f730f1</code></p>
</li>
</ul>
<p>Although ReactiveSearch by default uses query strings for the Search UIs, but also provides support for customization to switch to path based routing.</p>
<p>ReactiveBase supports two props to allow users to control how the URL of the search UI behaves.</p>
<ul>
<li>getSearchParams</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Optional</td></tr>
</thead>
<tbody>
<tr>
<td><code>Function</code></td><td>Yes</td></tr>
</tbody>
</table>
</div><p>Enables you to customize the evaluation of query-params-string from the URL (or) any other source. If this function is not set, the library will use <code>window.location.search</code> as the search query-params-string for parsing selected values. This can come in handy if the URL is using hash values.</p>
<ul>
<li>setSearchParams</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Optional</td></tr>
</thead>
<tbody>
<tr>
<td><code>Function</code></td><td>Yes</td></tr>
</tbody>
</table>
</div><p>Enables you to customize the setting of the query params string in the URL by providing the updated query-params-string as the function parameter. If this function is not set, the library will set the <code>window.history</code> via <code>pushState</code> method.</p>
<p>In our example attached below,</p>
<p>We leverage <code>setSearchParams</code> to receive a URL decorated with query params and manipulate the query params to generate a human-friendly route, finally applying it using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/History/pushState"><code>window.history.pushState</code></a> method.</p>
<pre><code class="lang-js">setSearchParams = <span class="hljs-function">(<span class="hljs-params">urlWithQueryParams</span>) =&gt;</span> {
  <span class="hljs-comment">// build URL with category and sub-category path</span>
  <span class="hljs-built_in">window</span>.history.pushState({ <span class="hljs-attr">path</span>: urlWithPath }, <span class="hljs-string">''</span>, urlWithPath);
}
</code></pre>
<p>On the other hand <code>getSearchParams</code> callback is used to parse the path-based URL and turn it into library supported URL with query params.</p>
<pre><code class="lang-js">getSearchParams = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// extract query params from the path</span>
  <span class="hljs-comment">// path $host/Best%20%Buy/Mobile?color=red&amp;page=2</span>
  <span class="hljs-comment">// Extract query params: category=Best%2-Buy&amp;sub-category=Mobile&amp;color=red&amp;page=2</span>
  <span class="hljs-keyword">return</span> newURLwithAllParams
}
</code></pre>
<p>Voila 🎊 Try selecting the category and the subcategory in the example to see the route's path changing.</p>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/PathBasedRouting?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<blockquote>
<p>Resources</p>
<ul>
<li><p><a target="_blank" href="https://developers.google.com/search/docs/crawling-indexing/url-structure">Keep a simple URL structure</a></p>
</li>
<li><p><a target="_blank" href="https://neilpatel.com/blog/complete-guide-structuring-urls/">The Complete Guide on Structuring Your URLs Correctly</a></p>
</li>
</ul>
</blockquote>
<h2 id="heading-your-urls-reflect-the-structure-of-your-website"><strong>Your URLs reflect the structure of your website</strong></h2>
<p>Some search engines use your <a target="_blank" href="https://webmasters.googleblog.com/2015/04/better-presentation-of-urls-in-search.html">URL structure</a> to infer the architecture of your website, understand the context of a page, and enhance its relevance to a particular search query.</p>
<p><img src="https://neilpatel.com/wp-content/uploads/2017/08/image3.jpg" alt="image3" /></p>
<p>Well-structured URLs offer users a quick hint about the page topic and how the page fits within the website.</p>
<p><img src="https://neilpatel.com/wp-content/uploads/2017/08/image5.png" alt="image5" /></p>
<p>For example, if a website has categories and sub-categories, each category should be reachable through <a target="_blank" href="https://mywebsite.com/%3Ccategory%3E/"><code>https://mywebsite.com/&lt;category&gt;/</code></a> and each sub-category through <a target="_blank" href="https://mywebsite.com/%3Ccategory%3E/%3Csub-category%3E"><code>https://mywebsite.com/&lt;category&gt;/&lt;sub-category&gt;</code></a>.</p>
<p><strong>✅ Dos</strong></p>
<ul>
<li><p><a target="_blank" href="https://mywebsite.com/Car-Equipment/"><code>https://mywebsite.com/Car-Equipment/</code></a></p>
</li>
<li><p><a target="_blank" href="https://mywebsite.com/Women-Clothing/T-Shirts/"><code>https://mywebsite.com/Women-Clothing/T-Shirts/</code></a></p>
</li>
</ul>
<p><strong>🚫 Don'ts</strong></p>
<ul>
<li><p><a target="_blank" href="https://mywebsite.com/search?category=Cars-Equipement"><code>https://mywebsite.com/search?category=Cars-Equipement</code></a></p>
</li>
<li><p><a target="_blank" href="https://mywebsite.com/search?categorylvl1=Clothing&amp;categoryLvl2=T-shirts"><code>https://mywebsite.com/search?categorylvl1=Clothing&amp;categoryLvl2=T-shirts</code></a></p>
</li>
</ul>
<blockquote>
<p>Resources -</p>
<ul>
<li><a target="_blank" href="https://developers.google.com/search/blog/2015/04/better-presentation-of-urls-in-search">Better presentation of URLs in search results</a></li>
</ul>
</blockquote>
<h2 id="heading-your-site-is-using-a-pre-rendering-technique"><strong>Your site is using a pre-rendering technique</strong></h2>
<p>The first question is, "Why do we even need to pre-render an HTML page"?</p>
<p>In the CSR technique, which has come into our lives with modern browsers, websites send very little HTML response to connection requests. This HTML response contains JavaScript codes that make up the content of the page. <code>Unless these JavaScript codes are executed, the website is a blank page.</code> The browser renders these JavaScript files, creates the content, and presents the web page to us.</p>
<p>🚫✋🏻 But wait ✋🏻🚫, The server sends <strong>little to no HTML</strong> but a script to be executed on the client side which in turn is responsible for generating the HTML. <em>The crawlers have got a problem with this.</em> The crawlers primarily like HTML and don't wait to execute JS to parse the content of the page.</p>
<p><img src="https://miro.medium.com/max/700/1*JOsWWcKet04OMzbirZgTeA.png" alt class="image--center mx-auto" /></p>
<p>Not all search engine crawlers can <a target="_blank" href="https://developers.google.com/search/docs/guides/javascript-seo-basics">process JavaScript successfully or immediately</a>. Fortunately, there are many ways around it.</p>
<h3 id="heading-server-side-rendering-ssr"><strong>Server-side rendering (SSR)</strong></h3>
<p><img src="https://www.datocms-assets.com/18376/1650547784-1647199989-javascript-seo-ssr-ve-csr3.png" alt class="image--center mx-auto" /></p>
<p>This technique fetches your data and renders a JavaScript website on the server before sending it to the browser. This process is commonly implemented through modern frameworks such as <a target="_blank" href="https://reactjs.org/">React</a>, <a target="_blank" href="https://angular.io/">Angular</a>, and <a target="_blank" href="https://vuejs.org/">Vue</a>.</p>
<h3 id="heading-lets-help-you-out">Let's help you out 🤝</h3>
<p><code>Reactivesearch</code> internally runs on a redux store. With Server Side Rendering, you can handle the initial render when a user (or search engine crawler) first requests your app. To achieve the relevant results on an initial render, we need to pre-populate the redux store of ReactiveSearch.</p>
<p>Visit our docs <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/advanced/ssr/#usage">here to know more</a>.</p>
<p>A quick demo -</p>
<iframe src="https://codesandbox.io/embed/github/appbaseio/reactivesearch/tree/next/packages/web/examples/ssr?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<blockquote>
<p>Resources</p>
<ul>
<li><p><a target="_blank" href="https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics">Understand the JavaScript SEO basics</a></p>
</li>
<li><p><a target="_blank" href="https://zeo.org/resources/blog/javascript-seo-what-is-ssr-csr-advantages-and-disadvantages">Javascript SEO: What is SSR/CSR? Advantages and Disadvantages</a></p>
</li>
<li><p><a target="_blank" href="https://medium.com/@natelapinski/how-you-render-can-affect-your-seo-csr-vs-ssr-vs-dynamic-815a91dea894">How You Render Can Affect Your SEO (CSR vs SSR vs Dynamic Rendering)</a></p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/advanced/ssr">🚀 Server side rendering with ReactiveSearch</a></p>
</li>
</ul>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Introducing: ReactiveSearch v4]]></title><description><![CDATA[With a lot of feedback-oriented development, ReactiveSearch v4 is in preview now. We have added components to enable richer search experiences, improved on performance, and also improved on the bundle size off the library.
You can download it for you...]]></description><link>https://blog.reactivesearch.io/reactivesearch-v4</link><guid isPermaLink="true">https://blog.reactivesearch.io/reactivesearch-v4</guid><category><![CDATA[reactivesearch]]></category><category><![CDATA[React]]></category><category><![CDATA[search]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Muhammad Ashraf]]></dc:creator><pubDate>Mon, 13 Feb 2023 12:53:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674788909108/595b8e98-241c-4e70-a598-ac0ea1ae1d64.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With a lot of feedback-oriented development, ReactiveSearch v4 is in preview now. We have added components to enable richer search experiences, improved on performance, and also improved on the bundle size off the library.</p>
<p>You can download it for your search project with the following command:</p>
<blockquote>
<p><em>npm install @appbaseio/reactivesearch@</em>latest</p>
</blockquote>
<p>In this blog post, I will share a summary of the key enhancements and changes included in the v4 release.</p>
<ul>
<li><p><strong>Support for React 18 😎</strong></p>
<p>  ReactiveSearch and ReactiveMaps are fully compatible with <code>React 18.x</code> and above with the <code>4.x</code> releases.</p>
</li>
<li><p><strong>Removal of search engine DSL from the library</strong></p>
<p>  To prevent a DoS scenario and as a security best practice, we're removing frontend DSL generation. ReactiveSearch library will instead use <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/advanced/migration-v4/docs/search/reactivesearch-api/">Reactivesearch API</a> to declare the search intent. This will require the use of a ReactiveSearch API server and you can choose one of the two path ways to do this:</p>
<ul>
<li><p>As a ReactiveSearch (previously appbase.io) customer, you already have the ReactiveSearch API server hosted in the cloud. There are no usage changes as compared to ReactiveSearch v3's usage in this scenario.</p>
</li>
<li><p>As an open-source user, you have the choice to run ReactiveSearch API server as an open-source docker container. The API server should be configured to point to a search engine: Elasticsearch, OpenSearch and Solr are currently supported.</p>
</li>
</ul>
</li>
<li><p><strong>Removal of</strong> <code>DataSearch</code> <strong>&amp;</strong> <code>CategorySearch</code> <strong>components</strong></p>
<p>  Besides <code>SearchBox</code> component, <code>v3.x</code> exported two components for auto-suggestions: <code>DataSearch</code> &amp; <code>CategorySearch</code>. <code>4.x</code> removes these additional components, as the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/advanced/migration-v4/docs/reactivesearch/v3/search/searchbox/">SearchBox</a> component is versatile and allows creating a search UI with many possible options.</p>
</li>
</ul>
<ul>
<li><p><strong>Richer Analytics</strong></p>
<p>  With <code>v4.x</code>, we are enabling a richer analytics experience for our users, thanks to the use of <a target="_blank" href="https://github.com/appbaseio/analytics.js">analytics.js</a> by ReactiveSearch. Users can save searches, mark searches as favorites, tag custom events, in addition to recording search and clicks. <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/advanced/analytics/">Check out the docs to learn more</a></p>
</li>
<li><p><strong>New components</strong></p>
<ul>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/list/treelist/"><strong>TreeList</strong></a></p>
<p>  <img src="https://i.imgur.com/AwUfFJ7.png" alt="TreeList image" /></p>
<p>  <code>TreeList</code> creates a selection-based hierarchical tree UI component that is connected to your search index's <strong>category &gt; sub-category &gt; sub-sub-category</strong> type of hierarchical fields. It supports both single and multiple item selections.</p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/piechart/"><strong>Chart Components</strong></a></p>
<p>  <img src="https://i.imgur.com/4TxrKmi.png" alt="Image to be displayed" class="image--center mx-auto" /></p>
<ul>
<li><p>ReactiveSearch now has chart components to build reporting UIs with advanced search capabilities, or to integrate a reporting view into your search UI.</p>
<ul>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/piechart/"><strong>Pie Chart</strong> ↗️</a></p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/barchart/"><strong>Bar Chart</strong> ↗️</a></p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/histogramchart/"><strong>Histogram Chart</strong> ↗️</a></p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/linechart/"><strong>Line Chart</strong> ↗️</a></p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/scatterchart/"><strong>Scatter Chart</strong> ↗️</a></p>
</li>
<li><p><a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/reactivechart/"><strong>Reactive Chart</strong> ↗️</a></p>
<p>  <em>Got a custom chart to render?</em> <code>ReactiveChart</code> is a generic component to render any chart UI supported by the Apache <a target="_blank" href="https://echarts.apache.org/">E-charts</a> library. Additionally, it supports pre-built charts (<code>pie</code>, <code>bar</code>, <code>line</code>, <code>histogram</code> and <code>scatter</code>) to cover the most common chart use cases that can be configured using declarative props.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Removal of Deprecated props</strong></p>
<p>  We have also removed the following deprecated props and instead recommend using their alternatives</p>
</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Prop Name</strong></td><td><strong>Component</strong></td><td><strong>Alternative</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>analyticsConfig</code></td><td><code>ReactiveBase</code></td><td><code>reactivesearchAPIConfig</code></td></tr>
<tr>
<td><code>appbaseConfig</code></td><td><code>ReactiveBase</code></td><td><code>reactivesearchAPIConfig</code></td></tr>
<tr>
<td><code>analytics</code></td><td><code>ReactiveBase</code></td><td><code>reactivesearchAPIConfig.recordAnalytics</code></td></tr>
<tr>
<td><code>enableAppbase</code></td><td><code>ReactiveBase</code></td><td><code>-</code></td></tr>
<tr>
<td><code>triggerAnalytics</code></td><td><code>ReactiveList.renderItem</code></td><td><code>triggerClickAnalytics</code></td></tr>
<tr>
<td><code>aggregationField</code></td><td><code>All Components</code></td><td><code>distinctField</code></td></tr>
</tbody>
</table>
</div><ul>
<li><strong>Simplified SSR API</strong></li>
</ul>
<p>With the release of v4, the SSR implementation is more robust and easier, requiring very little config from users. 😌</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676274560036/07ab3b44-8d78-4ff0-9ec3-fa935a0b8e5b.png" alt="A comparison of SSR config required for v3 as compared to v4" /></p>
<p>Here's a SSR usage example with the new API:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {
    <span class="hljs-comment">// ... other components</span>
    getServerState <span class="hljs-comment">// the method to calculate initial state</span>
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@appbaseio/reactivesearch'</span>;

<span class="hljs-keyword">const</span> components = {
    <span class="hljs-comment">// component props-settings</span>
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> getInitialProps({ pathname, query }) {
        <span class="hljs-keyword">return</span> {
            <span class="hljs-comment">// 1st Argument</span>
            <span class="hljs-comment">// pass the reference to the app, i.e. Main in this case</span>
            <span class="hljs-comment">// The new method saves you from explicitly specifying the</span>
            <span class="hljs-comment">// component type, props, and source</span>
            <span class="hljs-comment">// 2nd Argument</span>
            <span class="hljs-comment">// The queryString or the query object to consider </span>
            <span class="hljs-comment">// URLParams while computing the initialState at server side</span>
            <span class="hljs-attr">store</span>: <span class="hljs-keyword">await</span> getServerState(Main, query); 
        };
    }

    <span class="hljs-comment">// In ReactiveBase (refer below)</span>
    <span class="hljs-comment">// the contextCollector is another prop required for RS SSR</span>
    <span class="hljs-comment">// to do some magic internally</span>
    <span class="hljs-comment">// you should pass it without worrying about its source</span>
    <span class="hljs-comment">// we handle it for you</span>

    render() {
        <span class="hljs-keyword">return</span> (
            <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span> 
                    {<span class="hljs-attr">...components.settings</span>}                     
                    <span class="hljs-attr">initialState</span>=<span class="hljs-string">{props.initialState}</span>                   
                    <span class="hljs-attr">contextCollector</span>=<span class="hljs-string">{props.contextCollector}</span>
                &gt;</span>                    
                    {/* ... other components ...*/}
                <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span>
            <span class="hljs-tag">&lt;/&gt;</span></span>
        );
    }
}
</code></pre>
<p>The new usage mechanism takes care of a lot of configuration from the user on its own now, significantly reducing human error while writing the config.</p>
<p>See the new SSR usage in action with the CodeSandbox below:</p>
<iframe src="https://codesandbox.io/embed/rs-ssr-final-app-6z86mo?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<blockquote>
<p>💫 Additionally, you can read the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/advanced/ssr/">docs here</a> to know more about how ReactiveSearch provides SSR support out of the box.</p>
</blockquote>
<h2 id="heading-summary">Summary</h2>
<p>We went over React v18 support, introduction of new components for TreeList and Charts, richer analytics support, low-config SSR usage support, and removal of frontend DSL generation from the library.</p>
<p>Take ReactiveSearch v4 for a spin and let us know your feedback on the <a target="_blank" href="https://github.com/appbaseio/reactivesearch/issues">GitHub issues</a>.</p>
<p>You can follow the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/overview/quickstart/">quickstart guide over here</a> to build a search UI with ReactiveSearch v4.</p>
]]></content:encoded></item><item><title><![CDATA[Building Programmatic Charts UIs with ReactiveSearch]]></title><description><![CDATA[Are you looking to build a search UI with charts capabilities for your SaaS product? We will talk about creating programmatic charts with the ReactiveSearch UI library in this post. We will use ElasticSearch as the search engine backend and use the R...]]></description><link>https://blog.reactivesearch.io/building-charts-ui-with-reactivesearch</link><guid isPermaLink="true">https://blog.reactivesearch.io/building-charts-ui-with-reactivesearch</guid><category><![CDATA[charts]]></category><category><![CDATA[search]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[search engine]]></category><dc:creator><![CDATA[Shubham Shah]]></dc:creator><pubDate>Sat, 28 Jan 2023 09:14:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673332299816/6c59df5a-b9db-4294-9253-355b5cbeac8b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Are you looking to build a search UI with charts capabilities for your SaaS product? We will talk about creating programmatic charts with the ReactiveSearch UI library in this post. We will use <a target="_blank" href="https://www.reactivesearch.io/what-is/elasticsearch"><strong>ElasticSearch</strong></a> as the search engine backend and use the <a target="_blank" href="https://www.reactivesearch.io/product/search-ui">ReactiveSearch library</a> for rendering the charts UI widgets.</p>
<p>ReactiveSearch now supports 5 built-in chart components: Pie Chart, Bar Chart, Histogram, Line, and Scatter, and comes with a generic component - ReactiveChart which can use any of the <a target="_blank" href="https://echarts.apache.org/examples/en/index.html">Apache E-charts UI components</a>. You can read more about creating charts with ReactiveSearch over <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/react/chart/piechart/">here</a>.</p>
<h1 id="heading-who-is-this-for">Who is this for?</h1>
<p>The need for the overall representation of data in a single glance is an essential requirement for all businesses. That’s where a <strong>charts UI</strong> comes into play.</p>
<p>A charts UI can be used to:</p>
<ul>
<li><p>Enable your business team to visualize the important business metrics in a single glance,</p>
</li>
<li><p>Share performance and ROI reports with your clients,</p>
</li>
<li><p>Empower your customers and end-users to visualize metrics relevant to them,</p>
</li>
<li><p>Collate existing dashboards into a unifying dashboard.</p>
</li>
</ul>
<p>There are dashboard builder tools such as <a target="_blank" href="https://www.elastic.co/kibana"><strong>Kibana</strong></a> and <a target="_blank" href="https://grafana.com/"><strong>Grafana</strong></a> which let you create Elasticsearch-powered dashboards with no code. We recommend them whenever they fit your use case. However, they become limiting as soon as you need to programmatically filter and visualize the data, e.g. filter data for the logged-in user and set access controls on the data that’s visible, need to integrate other JavaScript code within your dashboard, or extend further by calling other backend routes.</p>
<h1 id="heading-what-are-we-building">What are we building?</h1>
<p>We are going to build a drill-down charts dashboard app that has the following features:</p>
<ul>
<li><p>An explore page where we can apply various filters and see the number of results it finds.</p>
</li>
<li><p>A search page where we can see the details of the results.</p>
</li>
<li><p>The explore page can drill down to the search page with all the facets selected on the page.</p>
</li>
</ul>
<p>You can take a look at the live version below.</p>
<iframe src="https://codesandbox.io/embed/github/SavvyShah/reactivesearch/tree/feat%2Fnew-charts-demo/site/demos/charts-dashboard?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden;margin-bottom:1rem" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h1 id="heading-what-do-we-need">What do we need?</h1>
<p>We would be using some tools to help make this complex task simple.</p>
<ol>
<li><p><strong>Dataset:</strong> To make a great UI we should have a good dataset. We would be using an e-commerce dataset that has a vast amount of product data across categories. We would also need an index to store the data.</p>
<p> You can set up and install an Elasticsearch server by following the <a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html"><strong>official installation guide</strong></a>, or you can create a free account at <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a> which provides Elasticsearch hosting as a service and is easy to use. For simplicity, we will be using <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a> service to get started.</p>
<p> I’ve already created an index with the dataset. You can check out the dataset from above <a target="_blank" href="https://dejavu.appbase.io/?appname=clone-airbeds&amp;url=https://73afb5484d0e:26bd5cb0-1afc-4e19-8870-4a2eda8d0b56@appbase-demo-ansible-abxiydt-arc.searchbase.io"><strong>over here in the data browser tool Dejavu</strong></a>, which is built by <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a>.</p>
</li>
<li><p><a target="_blank" href="https://github.com/appbaseio/reactivesearch/"><strong>ReactiveSearch</strong></a> — A declarative, props-driven UI library for querying and managing the search state and comes with over 30 UI component presets. This lets you customize both your queries and UIs to render in minutes and comes with extensive documentation.</p>
</li>
<li><p><a target="_blank" href="https://ant.design/">Antd</a>: A UI components library so that we don't have to focus on making our UI beautiful and consistent.</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/react-router-dom">React Router</a>: We are going to build a multi-page application, which would need to handle URL navigation. We would use <code>react-router-dom</code> for that.</p>
</li>
</ol>
<h1 id="heading-lets-get-started">Let's get started</h1>
<h3 id="heading-initial-setup">Initial Setup</h3>
<p>We can bootstrap a react project by running <code>yarn create react-app [app-name]</code> or <code>npx create-react-app [app-name]</code>. This would create a project directory and initialize a <code>package.json</code> file in the root with all the necessary packages and scripts.</p>
<p>We would also need to install <code>react-router-dom</code> and <code>antd</code> library. This app was made using <code>v4</code> of both libraries.</p>
<pre><code class="lang-bash">yarn add react-router-dom@4 antd@4
</code></pre>
<p>You would also need to install <code>@appbaseio/reactivesearch</code> which would help us create the search UI.</p>
<pre><code class="lang-javascript">yarn add @appbaseio/reactivesearch
</code></pre>
<p>Below, shows the app after running the create react app command.</p>
<iframe src="https://codesandbox.io/embed/github/SavvyShah/reactivesearch/tree/step-1-charts-dashboard-demo/site/demos/charts-dashboard?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden;margin-bottom:1rem" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h3 id="heading-building-the-explore-page">Building the Explore page</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671712621838/zg83yFhn1.png" alt class="image--center mx-auto" /></p>
<p>This is the main page of our app. This would contain different chart facets which we would use to narrow down our search results. We would be using <a target="_blank" href="https://github.com/appbaseio/reactivesearch">@appbaseio/reactivesearch</a> library which provides a range of facets.</p>
<p>Firstly, we need to modify our <code>App.js</code> file inside to configure our routing like below. We would create two pages, <code>Explore.js</code> and <code>Search.js</code>, inside a new directory. We would name the directory <code>pages</code>. Below routing configuration shows the <code>Explore</code> page on <code>/explore</code> , <code>Search</code> page on <code>/search</code> and for any other route, it would redirect to <code>Explore</code> page.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.js</span>
<span class="hljs-keyword">import</span> { BrowserRouter <span class="hljs-keyword">as</span> Router, Switch, Route, Redirect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> Explore <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Explore'</span>;
<span class="hljs-keyword">import</span> Search <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Search'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Router</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Switch</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/explore"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">Explore</span> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/search"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">Search</span> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">Redirect</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/explore"</span> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Switch</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Router</span>&gt;</span></span>
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>We would start building the <code>Explore.js</code> page now. First, we need to connect to the Elasticsearch index storing our dataset. For that, we would use <code>ReactiveBase</code> component from the library. The index name is <code>best-buy-dataset</code> and we have configured the credentials and URL. Since this is made using <a target="_blank" href="https://www.reactivesearch.io/"><strong>reactivesearch.io</strong></a> we are also passing <code>enableAppbase</code> to be true.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Explore.js</span>
<span class="hljs-keyword">import</span> { ReactiveBase } <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Explore</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">app</span>=<span class="hljs-string">"best-buy-dataset"</span>
      <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
      <span class="hljs-attr">enableAppbase</span>
    &gt;</span>
    {/* Our App goes here */}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>Then we are going to add a chart facet. For this, we would use <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/chart/reactivechart/"><code>ReactiveChart</code></a> component and pass it required props to show us a pie chart. <code>componentId</code> differentiates this facet from others. <code>dataField</code> determines the field in your dataset/index on which you want to query. Finally, <code>chartType</code> is the chart you want to render. It can be <code>pie</code>, <code>bar</code>, <code>line</code>, etc. You can look at how we can configure different chart facets in the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/chart/reactivechart/">documentation</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/pages/Explore.js</span>
<span class="hljs-keyword">import</span> { ReactiveChart, ReactiveBase } <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Explore</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">app</span>=<span class="hljs-string">"best-buy-dataset"</span>
      <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
      <span class="hljs-attr">enableAppbase</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveChart</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"Category"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">"class.keyword"</span>
        <span class="hljs-attr">chartType</span>=<span class="hljs-string">"pie"</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>We can use the <code>Card</code> component from <code>antd</code> to wrap each chart facet which would make the UI look a little polished.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/pages/Explore.js</span>
<span class="hljs-keyword">import</span> { ReactiveChart, ReactiveBase } <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Explore</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">app</span>=<span class="hljs-string">"best-buy-dataset"</span>
      <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
      <span class="hljs-attr">enableAppbase</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Card</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveChart</span>
            <span class="hljs-attr">componentId</span>=<span class="hljs-string">"Category"</span>
            <span class="hljs-attr">dataField</span>=<span class="hljs-string">"class.keyword"</span>
            <span class="hljs-attr">chartType</span>=<span class="hljs-string">"pie"</span>
          /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Card</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>You can see the complete Explore page. We would render an empty page for the Search page. We have also added some styles in <code>src/App.css</code> and imported them in <code>src/App.js</code>. You can check the source code in the codesandbox below.</p>
<iframe src="https://codesandbox.io/embed/github/SavvyShah/reactivesearch/tree/step-2-charts-dashboard-demo/site/demos/charts-dashboard?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden;margin-bottom:1rem" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h3 id="heading-building-the-search-page">Building the Search page</h3>
<p>Now <code>Search.js</code> would be different from the Explore page, such that it would show the results with all the facets by the side. It would look like something below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671711939381/oFASw8Qbm.png" alt class="image--center mx-auto" /></p>
<p>We would add facets on the side of the results. Firstly, create a new file under <code>src/components</code> and name it <code>Facets.js</code>. The facets we would configure are <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/list/singlelist/">SingleList</a>, <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/chart/barchart/">BarChart</a>, <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/range/dynamicrangeslider/">DynamicRangeSlider</a> and <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/chart/linechart/">LineChart</a>. The final configuration should look like below.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/components/Facets.js</span>
<span class="hljs-keyword">import</span> {
  DynamicRangeSlider,
  ReactiveChart,
  SingleList,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Facets</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SingleList</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"Category"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">"class.keyword"</span>
        <span class="hljs-attr">URLParams</span>
        <span class="hljs-attr">loader</span>=<span class="hljs-string">"Loading..."</span>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveChart</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"SubCategory"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">"subclass.keyword"</span>
        <span class="hljs-attr">chartType</span>=<span class="hljs-string">"bar"</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"term"</span>
        <span class="hljs-attr">URLParams</span>
        <span class="hljs-attr">useAsFilter</span>
        <span class="hljs-attr">loader</span>=<span class="hljs-string">"Loading..."</span>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">DynamicRangeSlider</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"ReviewAverage"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">"customerReviewAverage"</span>
        <span class="hljs-attr">range</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">start:</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">end:</span> <span class="hljs-attr">5</span> }}
        <span class="hljs-attr">rangeLabels</span>=<span class="hljs-string">{(min,</span> <span class="hljs-attr">max</span>) =&gt;</span> ({
          start: min + " ⭐️",
          end: max + " ⭐️",
        })}
        loader="Loading..."
        showHistogram
        URLParams
      /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveChart</span>
        <span class="hljs-attr">componentId</span>=<span class="hljs-string">"Color"</span>
        <span class="hljs-attr">dataField</span>=<span class="hljs-string">"color.keyword"</span>
        <span class="hljs-attr">chartType</span>=<span class="hljs-string">"line"</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"term"</span>
        <span class="hljs-attr">URLParams</span>
        <span class="hljs-attr">useAsFilter</span>
        <span class="hljs-attr">loader</span>=<span class="hljs-string">"Loading..."</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/pages/Search.js</span>
<span class="hljs-keyword">import</span> { ReactiveBase, ReactiveList } <span class="hljs-keyword">from</span> <span class="hljs-string">'@appbaseio/reactivesearch'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Facets <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Facets'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Search</span>(<span class="hljs-params"></span>) </span>{

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
            <span class="hljs-attr">app</span>=<span class="hljs-string">"best-buy-dataset"</span>
            <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
            <span class="hljs-attr">enableAppbase</span>
            <span class="hljs-attr">initialQueriesSyncTime</span>=<span class="hljs-string">{1000}</span>
        &gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">Facets</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
    );
}
</code></pre>
<p>We also have to show results on the right side. For that, we are going to use another component called <code>ReactiveList</code>. Not only does it show results but it also updates itself when one of the facets changes. It also gives us a nice interface to render data in our dataset.</p>
<p>We would add a <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/result/reactivelist/"><code>ReactiveList</code></a> by configuring it's <code>componentId</code>, <code>dataField</code>, and <code>renderItem</code> method. We've already seen what <code>componentId</code> and <code>dataField</code> do. As for the <code>renderItem</code> method, it is a method that passes down each individual document from the Elasticsearch index which you can use to customize the look and feel. Apart from that we also configure an additional property called <code>react</code>. This tells what facet components would update the results in this list. We should pass the <code>componentId</code> of the facets when doing this. You can read more about configuring <code>ReactiveList</code> component in the <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/result/reactivelist/">documentation</a>. We also use <code>Col</code> and <code>Row</code> layout components from <code>antd</code> which would help us to create responsive layouts easily.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { ReactiveBase, ReactiveList } <span class="hljs-keyword">from</span> <span class="hljs-string">"@appbaseio/reactivesearch"</span>;
<span class="hljs-keyword">import</span> { Col, Row } <span class="hljs-keyword">from</span> <span class="hljs-string">"antd"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Facets <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Facets"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Search</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
      <span class="hljs-attr">app</span>=<span class="hljs-string">"best-buy-dataset"</span>
      <span class="hljs-attr">url</span>=<span class="hljs-string">"https://a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61@appbase-demo-ansible-abxiydt-arc.searchbase.io"</span>
      <span class="hljs-attr">enableAppbase</span>
      <span class="hljs-attr">initialQueriesSyncTime</span>=<span class="hljs-string">{1000}</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Row</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Col</span> <span class="hljs-attr">xs</span>=<span class="hljs-string">{24}</span> <span class="hljs-attr">md</span>=<span class="hljs-string">{8}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p10"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Facets</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Col</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Col</span> <span class="hljs-attr">xs</span>=<span class="hljs-string">{24}</span> <span class="hljs-attr">md</span>=<span class="hljs-string">{16}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p10"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveList</span>
            <span class="hljs-attr">componentId</span>=<span class="hljs-string">"SearchResult"</span>
            <span class="hljs-attr">dataField</span>=<span class="hljs-string">"original_title"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"result-list-container"</span>
            <span class="hljs-attr">from</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">size</span>=<span class="hljs-string">{5}</span>
            <span class="hljs-attr">renderItem</span>=<span class="hljs-string">{(data)</span> =&gt;</span> {
              return (
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"resultItem"</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{data._id}</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                    <span class="hljs-attr">src</span>=<span class="hljs-string">{data.image}</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">"Book Cover"</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"resultItem__img"</span>
                  /&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"resultItem__body"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"book-header"</span>&gt;</span>{data.name}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex column justify-space-between"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"resultItem__category"</span>&gt;</span>
                            {data.class}
                          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>{" "}
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>&gt;<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>{" "}
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"resultItem__subCategory"</span>&gt;</span>
                            {data.subclass}
                          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ratings-list flex align-center"</span>&gt;</span>
                          Sale price: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">""</span>&gt;</span>{data.salePrice}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              );
            }}
            react={{
              and: [
                "Category",
                "SubCategory",
                "Color",
                "ReviewAverage",
                "SearchBox",
              ],
            }}
            includeFields={[
              "class",
              "subclass",
              "name",
              "image",
              "salePrice",
              "categoryPath",
            ]}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Col</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Row</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
  );
}
</code></pre>
<p>We would also add a <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/search/searchbox/"><code>SearchBox</code></a> from <code>@appbaseio/reactivesearch</code>. You can check the look and feel of the page with all the components below.</p>
<iframe src="https://codesandbox.io/embed/github/SavvyShah/reactivesearch/tree/step-3-charts-dashboard-demo/site/demos/charts-dashboard?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden;margin-bottom:1rem" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h3 id="heading-drill-down-behavior">Drill down behavior</h3>
<p>We have built both pages. Now we need to connect them. We would add a card at the bottom, which would show the number of results filtered by charts, clicking which would lead to the search page.</p>
<p>For that, we would also use a <code>ReactiveList</code> component. We would use the <code>render</code> method to customize our render. It is different from <code>renderItem</code> such that it gets all the documents at once. It also gets some additional props useful to display the result stats such as the total number of documents. We would also hide the default result stats by passing <code>showResultStats</code> as <code>false</code> and hide pagination as well.</p>
<p>We would also add an <code>onClick</code> method which would take us to the search page. The onclick handler would apply all the filters from the Explore page to the Search page.</p>
<p>Since the above functionality relies on URL params to pass this information, we would need to pass <a target="_blank" href="https://docs.reactivesearch.io/docs/reactivesearch/v3/list/singlelist/#urlparams"><code>URLParams</code> prop</a> as <code>true</code> in all the facet components on both pages.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/pages/Explore.js</span>
<span class="hljs-comment">// ... import statements</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Explore</span>(<span class="hljs-params">{ history }</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ReactiveBase</span>
            {<span class="hljs-attr">...configuration</span>}
        &gt;</span>
        {/* Chart facets */}    
        <span class="hljs-tag">&lt;<span class="hljs-name">Row</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ReactiveList</span>
                    <span class="hljs-attr">componentId</span>=<span class="hljs-string">"ListComponent"</span>
                    <span class="hljs-attr">dataField</span>=<span class="hljs-string">"albumTitle"</span>
                    <span class="hljs-attr">pagination</span>=<span class="hljs-string">{false}</span>
                    <span class="hljs-attr">infiniteScroll</span>=<span class="hljs-string">{false}</span>
                    <span class="hljs-attr">showResultStats</span>=<span class="hljs-string">{false}</span>
                    <span class="hljs-attr">renderNoResults</span>=<span class="hljs-string">{()</span> =&gt;</span> null}
                    react={{ and: ['Category', 'SubCategory', 'ReviewAverage', 'Color'] }}
                    className="fullWidth"
                    render={({ data, ...props }) =&gt; {
                        return (
                            <span class="hljs-tag">&lt;<span class="hljs-name">Card</span>
                                <span class="hljs-attr">className</span>=<span class="hljs-string">"fullWidth"</span>
                                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                                    const urlLocation = new URL(window.location.href);
                                    const urlSearchParams = new URLSearchParams(urlLocation.search);
                                    history.push(`/search?${urlSearchParams}`);
                                }}
                            &gt;
                                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"resultsCard"</span>&gt;</span>
                                    {!props.loading
                                        ? `${props.resultStats.numberOfResults} matched the above criteria. View now.`
                                        : 'View Search Results'}
                                <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">Card</span>&gt;</span>
                        );
                    }}
                /&gt;
            <span class="hljs-tag">&lt;/<span class="hljs-name">Row</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ReactiveBase</span>&gt;</span></span>
    );
}
</code></pre>
<p>Congratulations! We've created a super-duper charts dashboard with a search interface. You can look at the complete working app below.</p>
<iframe src="https://codesandbox.io/embed/github/SavvyShah/reactivesearch/tree/feat%2Fnew-charts-demo/site/demos/charts-dashboard?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden;margin-bottom:2rem" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

<h1 id="heading-summary">Summary</h1>
<p>I hope you enjoyed this tutorial. We introduced so many things in this post, which might get overwhelming. So, let's summarize what you need to keep in mind while building such an app.</p>
<ul>
<li><p><a target="_blank" href="https://codesandbox.io/s/github/SavvyShah/reactivesearch/tree/step-1-charts-dashboard-demo/site/demos/charts-dashboard?from-embed"><strong>Step 1:</strong> Set up a react project. (We used CRA for convenience)</a></p>
</li>
<li><p><a target="_blank" href="https://codesandbox.io/s/github/SavvyShah/reactivesearch/tree/step-2-charts-dashboard-demo/site/demos/charts-dashboard?from-embed">Step 2: Add an explore page where we can apply facets and see the number of results filtered.</a></p>
</li>
<li><p><a target="_blank" href="https://codesandbox.io/s/github/SavvyShah/reactivesearch/tree/step-3-charts-dashboard-demo/site/demos/charts-dashboard">Step 3: We created a search page to show the results with the applied facets.</a></p>
</li>
<li><p><a target="_blank" href="https://codesandbox.io/s/github/SavvyShah/reactivesearch/tree/feat%2Fnew-charts-demo/site/demos/charts-dashboard">Step 4: We connected them with a drill-down behavior.</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>