Implement Search Functionality with ElasticSearch, Firebase & Flutter

Let’s see how we can implement full-text search using ElasticSearch and Firestore.

UtkarshKore
6 min readDec 6, 2020

Modern applications often require search functionality especially when apps have more textual content which cannot be presented to user in a single screen. The motivation of this article mainly comes from implementing search feature when using Firestore. Firebase doesn’t support String.contains() queries which can search for substrings inside text. But,

We’re programmers, we solve problems.

Photo by Marten Newhall on Unsplash

What this article covers:

  1. Setup ElasticSearch & Kibana locally
  2. Basics of ElasticSearch (indexes)
  3. Use SearchDelegates in Flutter with Elastic.

1. Setup ElasticSearch & Kibana Locally

You probably would have heard about ElasticSearch but what is Kibana? It’s just a visualization tool to easily work with your Elastic data. Let’s get to the steps now. First download the zip files for elastic and kibana and unzip it inside your desired folder.

UnzippedFolderView.dart

Now, let’s just verify that our elastic search works. Open command prompt and navigate to elasticsearch/bin folder. Type elasticsearch.bat and hit enter.

At first, it will take some time to setup everything. Once it is done hit your browser with http://localhost:9200/ address. You should see something like this —

Elasticsearch Working

Now that our elasticsearch is working, we can load kibana to visualize our data. Steps are same. Just navigate to unzipped kibana/bin folder and type kibana.bat in cmd. Then in your browser enter this address — http://localhost:5601/

Kibana Home Page

🎉 Congrats, you’ve setup elasticsearch successfully.

2. Basics of Elasticsearch

First things first, elasticsearch stores data in an index. You can imagine it as a document. An index can have multiple types as a value. e.g. A Computer index can have Desktop and Laptop as their types. We communicate with elasticsearch with http methods.

  1. GET — Get search results from elastic
  2. POST & PUT — Create or update data
  3. DELETE — Delete index or specific data in index

It’s time to play with Kibana now.

Photo by Zachary Kadolph on Unsplash

Head over to kibana and navigate to DevTools under Management. You’ll see an editor with some predefined http methods. We’ll create an index named services with some data to practice elasticsearch.

  1. First, create a new index services with PUT. The syntax is very simple. We first define the http method we want to use (PUT in this case) followed by the relevant endpoint on which we want to perform the operation. Type this line inside the editor to create the index and click the play button to execute the query. This will create services index.

PUT /services

2. Let’s add some data to this index to query later. We use POST method to add data. This will create a unique id for each of the doc.

POST /services/_doc
{
“search_query”: “car services”,
“service_name”: “Car Services”,
}

_doc is the type of data in the index. Anything inside {} will be stored as a key-value data. For simplicity, I’m only including the name of the service and a special field search_query (We will see the reason behind this later). You can modify the fields according to your need. Once done, hit run and execute the query. This will add the data with auto generated id. If you want to specify your own id for the doc, simply add it to the relative path after _doc/ like this.

POST /services/_doc/<your-id>
{
“search_query”: “car services”,
“service_name”: “Car Services”,
}

3. To get all the data, use GET method on /_search endpoint. Upon executing the below query you’ll see all your data inside _source attribute.

GET /services/_search

4. To delete a specific index or specific doc with id, you’d use DELETE method as below,

DELETE /test

DELETE /test/_doc/<doc-id>

5. To search data with query, we again have to use GET method with /_search endpoint. It also takes a body in which we’ll send the query to match. Let’s take an example below for our services index.

elastic query to search services with “ca” in name

As you can see, it takes a nested query JSON. The default_field indicates that the query has to be performed on [this] field. That’s why we included search_query field so that we can search names using lowercase letters for better results. And when you get the results back, you can use service_name field to populate it in the UI.

The query is passed in the query field which is a simple RegEx in our case which matched every string that contains letters ca. Feel free to modify this query according to your needs. Once you execute this, elasticsearch will return all the matching data.

Now that we’ve what we need to perform search, let’s move to flutter side and see how to communicate with this data. Most of you might have already guessed the need of http connection, so we’ll use dio package. The code looks like below —

If you try to run this code now, it won’t run as the elastic is serving on localhost and cannot be accessed on real device/emulator. Hence we’ve to make it run on public IP. It’s simple.

  1. Close the current instance of elastic and navigate to the unzipped elastic/config folder and open elasticsearch.yml file inside editor.
  2. Find network.host:192.168.0.1 line and uncomment it. Change it to network.host: 0.0.0.0. This will automatically assign a public address.
  3. Also, uncomment the discovery.seed_hosts: [“host1”, “host2”] line and change it to empty list discovery.seed_hosts: []
  4. Save your file and exit. Now run your elasticsearch again (Hopefully you remember the steps).
  5. You should see publish_address in your command prompt which is the actual IP address we’ll use for communication in Flutter. Copy this address with port number and paste it into ELASTIC_BASE_URL variable. Make sure your device and PC are on same network.
public IP address

If you make the request now, you can see the the response in print() statement. It’s just a matter of showing the results in the UI now. We’ll use SearchDelegates to populate the result on search.

Terminologies in Search Delegate

SearchDelegate provides predefined methods to implement the modern search functionality.

  • buildActions — returns list of actions to be taken like clear, search, etc.
  • buildLeading — builds the leading widget like back button in most cases.
  • buildResults — builds the results when the search action is performed(ListView in most cases).
  • buildSuggestions — builds suggestions while user is typing in the textfield. Build your suggestions list here.

SearchDelegate also has a query field which contains the latest text user has typed. You can use this field to pass it to the searchServices() method. The code would look like this,

SearchDelegate code

Wherever you want to open this search delegate just call showSearch() method provided by material.dart.

showSearch(context: context, delegate: ServiceSearchDelegate());

That’s it. This is what it takes to integrate ElasticSearch with flutter. This isn’t enough though. You’ll have to host your elastic to cloud platforms like GCP to use it remotely. You can do this with Firebase cloud functions. Refer the video here. The basic idea is to host elastic on cloud and add the new data with cloud functions when new data is added to firebase. For the sake of simplicity we’ll end the article here.

If you still want written article post it in the response. I’ll work on that. Hope you understood the basics of ElasticSearch and liked the article.

Happy Coding!

--

--