DEV Community

Cover image for Using an Elasticsearch client with Typescript and React to search for data
absterellio
absterellio

Posted on

Using an Elasticsearch client with Typescript and React to search for data

Overview

I recently built a search form, pulling data from an Elasticsearch index to display search results. I wanted to explain the concept, my goals, and the outcome. This is a basic example of using Elastic for searching, and through this exercise I feel more comfortable tackling more complex use cases in the future.

Structure of the index:

Below is an example index seen in ElasticVue chrome extension (https://elasticvue.com/). ID's and other data points are blocked to protect the data, but the general structure of the index is still visible. In this example, we can see there is a list of contracts that each have an ID, a CreatedByContactID, a BillingContact, and a BillingContactID.

Sample ElasticVue index

Query we want to build:

Let's say this search allows us to input a contact ID, and we want to see the contracts that have a CreatedByContactID or BillingContactID with this value, and we also want to filter any results based on other input criteria, such as a billing contact name and a contract ID (the _id field).

To do this filtering and data extraction from the index, we'll construct a boolean query (https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html), which will allow specifying many different types of fields that we want to search on.

The scoring assigned for this query (aka the calculated relevance of the result) favors matches within the must clause. I can also add filters to this boolean query, although these will not affect the score of the documents returned.

{
    "query": {
        "bool": {
            "must": {
                "multi_match": {
                    "query": 12345,
                    "type": "cross_fields",
                    "fields": [
                        "CreatedByContactID",
                        "BillingContactID"
                    ]
                }
            },
            "filter": {
                "term": {
                    "Id": 678910
                },
                "term": {
                    "BillingContact": "John Smith"
                }
            }
        }
    },
    "size": 10,
    "from": 0,
    "sort": []
}
Enter fullscreen mode Exit fullscreen mode

Search form

I built a sample form in React as a UI for the searching. Suppose this is my form, and my React application takes in values for each of the fields specified. Within my app, I can take these values and build a query to search the records in my index.

Sample search form

Without going to deep into the infrastructure, I can construct my query in Typescript using an Elasticsearch client (https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html) to make search calls to the existing Elasticsearch index I connect to.

Simply put, this client provides a ".search()" method I will be calling with the constructed query to bring data back to my UI.

Because my query will check on two fields for the same value, I plan to add a multi-match query that checks two fields on the index for the same value. For this particular multi_match, I specified a cross_fields type because I want the input contact ID to check either of the contact fields for that ID. Because I place this multi_match within a 'must' query, these fields must match the id specified and will contribute to the score of the documents returned.

The extra filters will be added to my elastic query simply as filters to search on. These filters must appear in the documents to be returned, and as mentioned earlier, these filters will not contribute to the calculated score of the results.

First, I can build an array of search filters that will be passed back to the query that we will pass into the Elastic client. Each filter has a term and a value to search on.

  const searchFilter: Array<
  | { term: { [key: string]: string } }
  | { term: { [key: string]: number } }
> = [];

Enter fullscreen mode Exit fullscreen mode

To add the filters (the ID and Billing Contact), I can push elements to the array.

searchFilter.push({ term: { ["ID"]: 678910 } }); 
searchFilter.push({ term: { ["BillingContact" ]: "John Smith" } }); 

Enter fullscreen mode Exit fullscreen mode

I can construct the multi-match 'must' fields array similarly.

  const must: Array<
    { multi_match: { query: number; type: string; fields: string[] } }  > = [];
Enter fullscreen mode Exit fullscreen mode
must.push({
          multi_match: {
              query: 12345,
              type: "cross_fields",
              fields: [
                "CreatedByContactID",
                "BillingContactID"
              ],
          },
      });
Enter fullscreen mode Exit fullscreen mode

Calling the ElasticSearch client

Assuming we have an existing elasticSearchClient built (please see Elasticsearch documentation to learn how to construct a client for searching), we can pass all of our specifications in and hopefully get back some data from the index.

const results = (await elasticSearchClient.search({
    index: // insert index here,
    body: {
      size: 10,
      from: 1,
      query: {
        bool: {
          filter,
          must
        },
      },
      sort,
    },
  })) as ElasticResponse // create your own typescript object to hold the hits returned;
Enter fullscreen mode Exit fullscreen mode

The output of this code should match the query I outlined at the top of this article. I can use the results in my application and present them back to the user, who will hopefully be seeing the contracts they expected :)

Takeaway

Overall, I learned a lot about constructing queries based on an Elastic index and how to pull out the data I need. I hope this was informative and painted a clearer picture of the concept of constructing boolean queries with bool, must, filter and multi_match queries, especially for those newer to Elasticsearch. I can see how this would be beneficial for quick access to large sets of data and I'm excited to play around more with Elasticsearch.

Thanks for reading and as always feel free to share any thoughts!

Top comments (0)