Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Elasticsearch-connector] Autocomplete suggestions #743

Merged
merged 12 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ This is the configuration that provide relevant query suggestions for incomplete
autocompleteQuery: {
// performs a prefix search on the query
results: {
resultsPerPage: 5,
resultsPerPage: 5, // number of results to display. Default is 5.
result_fields: {
// Add snippet highlighting within autocomplete suggestions
title: { snippet: { size: 100, fallback: true }},
Expand Down
75 changes: 65 additions & 10 deletions docs/connectors-api-elasticsearch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,66 @@ The connector will perform a `_search` query and will derive the endpoint path w

You can restrict access to indices by using an API key. We recommend you create an apiKey that is restricted to the particular index and has read-only authorization. See [Kibana API keys guide](https://www.elastic.co/guide/en/kibana/master/api-keys.html). To use the API key, place it within the Elasticsearch connection configuration.

## Autocomplete

Search UI supports autocomplete functionality to suggest search terms that provide results. The autocomplete functionality is built on top of the Elasticsearch `suggest` and `bool prefix query` API.

To take advantage of the feature, first update the <DocLink id="api-configuration" section="autocomplete-query" text="autocomplete query" /> configuration.

Below is an example of what the `autocompleteQuery` may look like.

```js
autocompleteQuery: {
// performs a prefix search on the query
results: {
resultsPerPage: 5, // number of results to display. Default is 5.
search_fields: {
// the fields to prefix search on
title_suggest: {}
},
result_fields: {
// Add snippet highlighting within autocomplete suggestions
title: { snippet: { size: 100, fallback: true }},
nps_link: { raw: {} }
}
},
// performs a query to suggest for values that partially match the incomplete query
suggestions: {
types: {
// Limit query to only suggest based on "title" field
documents: { fields: ["title_completion"] }
},
// Limit the number of suggestions returned from the server
size: 4
}
}

```

Above we are configuring both the `results` and `suggestions` sections of the autocomplete query.

`results` will need a search field to perform a prefix search on the query. We advise using a `search_as_you_type` field to be used.
`suggestions` require a `completion` type field to perform a query to suggest for values that partially match the incomplete query.

Below is an example of the mappings for the above example. `title_suggest` is a `search_as_you_type` field and `title_completion` is a `completion` type field.

```json
{
"mappings": {
"properties": {
"title_suggest": {
"type": "search_as_you_type"
},
"title_completion": {
"type": "completion"
}
}
}
}
```

With a combination of this configuration + the <DocLink id="api-react-components-search-box" text="Searchbox" /> component with autocomplete configuration, your users will be able to see suggestions as they type within the search box.

## Node.js Integration

The Elasticsearch API Connector builds the Elasticsearch query and performs the request directly to Elasticsearch from the browser. Depending on what you're building, you may want this logic to be done on the server and provide your clients a simplified API.
Expand All @@ -78,16 +138,11 @@ var app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

const connector = new APIConnector(
{
host: "http://localhost:9200", // host url for the Elasticsearch instance
index: "search-ui-examples", // index name where the search documents are contained
apiKey: "apiKeyExample" // Optional. apiKey used to authorize a connection to Elasticsearch instance.
},
{
queryFields: ["title", "description"]
}
);
const connector = new APIConnector({
host: "http://localhost:9200", // host url for the Elasticsearch instance
index: "search-ui-examples", // index name where the search documents are contained
apiKey: "apiKeyExample" // Optional. apiKey used to authorize a connection to Elasticsearch instance.
});

app.post("/search", async (req, res) => {
const { query, options } = req.body;
Expand Down
54 changes: 43 additions & 11 deletions docs/tutorials-elasticsearch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ For our movie data-set, we will be using the following fields:
- directors (searchable, facetable)
- released (filterable)
- imdbRating (filterable)
- url

The mapping file will be as follows, and we'll once again use Kibana's dev tools console to update the mapping file for our index.

Expand All @@ -162,6 +163,11 @@ PUT /my-example-movies/_mapping
"properties": {
"title": {
"type": "text"
"fields": {
"suggest": {
"type": "search_as_you_type"
}
}
},
"plot": {
"type": "text"
Expand Down Expand Up @@ -195,6 +201,12 @@ PUT /my-example-movies/_mapping
},
"imdbRating": {
"type": "float"
},
"url": {
"type": "keyword"
},
"movie_completion": {
"type": "completion"
}
}
}
Expand All @@ -204,6 +216,16 @@ PUT /my-example-movies/_mapping

Elasticsearch will acknowledge the request in the response.

We also want to provide autocomplete functionality, so we need to setup fields for autocomplete.
joemcelroy marked this conversation as resolved.
Show resolved Hide resolved

For suggestions, we want to suggest terms that appear within the actors, directors and genre fields.
For quick result hits, we want to suggest movies that partially match the title field.

In the above example:

- we have included `movie_completion` field, which is used to provide suggestion completion functionality. This field is not searchable, but is used to provide autocomplete functionality.
- we have included a `suggest` field for the title field. This field is searchable, but is used to provide "quick hits" functionality.

## Step 3: Index Movies Data

Now with our index and mapping file created, we are ready to index some data! We will use the bulk API to index our data.
Expand All @@ -213,7 +235,7 @@ We will use the following request. In this example we will be indexing the first
```shell
PUT /my-example-movies/_bulk
{ "index": {}}
{"title": "The Godfather", "released": "1972-03-23T23:00:00.000Z","genre": ["Crime", "Drama"],"directors": ["Francis Ford Coppola"],"actors": ["Marlon Brando", "Al Pacino", "James Caan", "Richard S. Castellano"],"plot": "The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.","imdbRating": "9.2"}
{"title": "The Godfather", "released": "1972-03-23T23:00:00.000Z","genre": ["Crime", "Drama"],"directors": ["Francis Ford Coppola"],"actors": ["Marlon Brando", "Al Pacino", "James Caan", "Richard S. Castellano"],"plot": "The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.","imdbRating": "9.2", "movie_completion": ["Crime", "Drama", "Marlon Brando", "Al Pacino", "James Caan", "Richard S. Castellano"], "url": "https://www.imdb.com/title/tt0068646/"}
```

## Step 4: Setup CRA for Search UI
Expand Down Expand Up @@ -328,13 +350,9 @@ const config = {
results: {
resultsPerPage: 5,
search_fields: {
title: {
"title.suggest": {
weight: 3
},
plot: {},
genre: {},
actors: {},
directors: {}
}
},
result_fields: {
title: {
Expand All @@ -343,10 +361,16 @@ const config = {
fallback: true
}
},
nps_link: {
url: {
raw: {}
}
}
},
suggestions: {
types: {
results: { fields: ["movie_completion"] }
},
size: 4
}
},
apiConnector: connector,
Expand All @@ -362,11 +386,13 @@ In the above example, we configured the:
- we made the facets disjunctive for better user experience. The user can select more than one facet to expand their search.
- autocomplete results to suggest results with the same query fields as main search + returning some fields for display.

For more information on configuration, visit the <DocLink id="api-configuration" text="API configuration docs" />.

### Updating Components

We are going to do several steps here:

- update the `<Searchbox />` component to switch off autocomplete
- update the `<Searchbox />` component to comfigure autocomplete
joemcelroy marked this conversation as resolved.
Show resolved Hide resolved
- remove sorting options
- add a `<Facet />` component for each facet field
- update the `<Results />` component to display all the fields
Expand All @@ -378,8 +404,14 @@ We are going to do several steps here:
header={
<SearchBox
autocompleteMinimumCharacters={3}
autocompleteResults={false}
autocompleteSuggestions={false}
autocompleteResults={{
linkTarget: "_blank",
sectionTitle: "Results",
titleField: "title",
urlField: "url",
shouldTrackClickThrough: true
}}
autocompleteSuggestions={true}
debounceLength={0}
/>
}
Expand Down
4 changes: 4 additions & 0 deletions examples/sandbox/src/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import { Switch, Route } from "react-router-dom";
import Root from "./pages/root";
import Elasticsearch from "./pages/elasticsearch";
import ElasticsearchEcommerce from "./pages/elasticsearch-ecommerce";
import AppSearch from "./pages/app-search";
import SiteSearch from "./pages/site-search";
import WorkplaceSearch from "./pages/workplace-search";
Expand All @@ -17,6 +18,9 @@ export default function Router() {
<Route exact path="/elasticsearch">
<Elasticsearch />
</Route>
<Route exact path="/elasticsearch-ecommerce">
<ElasticsearchEcommerce />
</Route>
<Route exact path="/app-search">
<AppSearch />
</Route>
Expand Down
Loading