Full-Text Search in Flutter with Algolia
Tags: flutter, algolia, android, ios
Published: 2020-10-05 11:48:29

####### *This article was originally posted on my Medium publication. [Here](https://medium.com/flutter-community/full-text-search-in-flutter-with-firestore-and-algolia-8de1a10b0c49) is a link to the original.*

Searching is one of the most important features of any mobile application that deals with data. It is almost inevitable to not have a reliable and consistent search in a mobile application these days. If your app deals with data that it retrieves from an API or a web service, you can have the backend developers develop the search mechanism for you and you can then consume the search results via the API, but, if you are using something like Cloud Firestore to store your data, you are in a puddle.

You can perform basic searches in Cloud Firestore where you need to match some fields with some values and order the results in a certain order as long as you are not dealing with text. Basically, it is not even searching. You can only look for exact value matches in the documents fields. For example, if a document has a field called `name` with the value `John Doe`, your search query should contain the exact field `name` and its value to be able to retrieve this document. Even if you use `John` or `Doe` as your search string, it won’t work because for text, you always check for equality in Firestore.

Recently, I had to add the search functionality to an app I have been working on and I had to write the search logic in such a way that it should consider multiple fields within a document on Cloud Firestore. For example, If I have the following documents inside my `Posts` collection.
```
post_id: 1234
post_title: Web Development for Beginners
post_text: This is a wonderful book for those who want to learn web development from scratch.

post_id: 1235
post_title: Software Development for Beginners
post_text: ...

post_id: 1236
post_title: Programming for Beginners
post_text: ...
```

In the above dataset, we can only perform a search within Firestore if the search query is exactly the text that we have stored in the field in the document. Firestore will not return any results for the following query.

```
Firestore.collection("Posts").where("post_title", "==", "Software").get()
```

And that’s obvious. The search term `software` does not match exactly within any document’s `post_title` field. And the problem maximizes when you are supposed to search across fields. Moreover, you cannot expect your users to be able to type the title of the book exactly the same way you have it in the database.

Enter Algolia. 
![Algolia](https://miro.medium.com/max/450/1*m2zbMG9VI38PvfZoH9UScA.jpeg)

I have been an Algolia user and it is a life-saver if you are using Firestore and 3 months into development you realize you are in a puddle.

I used Algolia around 2 years ago for the first time for a project and I decided to use it again. This was a piece of cake for Algolia considering what it can do with JSON data. So the first step in integrating Algolia in your app is to decide what data you want to perform your searches on. Narrow it down as much as you can and then add that data to Algolia.

Algolia has SDKs for both clients and servers in many popular programming languages but not Dart! But we will get around that. For now, I will be writing a small NodeJS script that will read all the documents from the Posts collection and add them to an index on Algolia. An Algolia index is like a collection of related data. An index has JSON documents within which it searches.
I will be using the Firebase’s Admin SDK to sync Firestore and Algolia. I will be using the `algoliasearch` NodeJS package to use the Algolia SDK. I will be skipping the details of this.

```
const algolia = algoliasearch("APP_ID", "API_ADMIN_KEY");
const index = algolia.initIndex("Posts"); // You can choose any name
let records = [];
let querySnapshot = await admin.firestore().collection("Posts").get();
    
for (let i in querySnapshot.docs) {
    let obj = querySnapshot.docs[i].data();
    obj.objectID = querySnapshot.docs[i].id;
    records.push(obj);
}
  
await index.saveObjects(records);
console.log("Pushed everything to Algolia"); 
```
 

You can have a look at [this](https://www.youtube.com/watch?v=vDsOQ58TrI0) video on [my YouTube channel](https://www.youtube.com/thesamarthagarwal) to see how I uploaded the data from a Firestore collection to an Algolia index.

The above code snippet just reads all the documents from the `Posts` collection and inserts them one by one in the results array. Each object in the array has a property called `objectID` that Algolia needs to index your data. Once the script finishes, you will have all the documents in an Algolia index called `Posts`.

![Index created on Algolia](https://miro.medium.com/max/1400/1*PcpPgsBl4b87GAEab7hV4A.png)

Go to the configuration section, select the Searchable Attributes option on the left, and add the `post_title` and `post_text` both to the list of searchable attributes. This will allow Algolia to search within both these fields.

![Added post_text and post_title to searchable attributes](https://miro.medium.com/max/1400/1*ubwy1bJ7uMZWOlyCUhu5HA.png)


Now that we have the data on Algolia and we have configured how we want to perform our searches, let’s see how we can implement search in the Flutter App.

Unfortunately, there is no Official SDK for Dart/Flutter for Algolia but you can use the amazing package `dart_algolia` which is a wrapper around the REST API or you are feeling wild, you can go ahead with the REST API yourself. Your options are open.

{% github knoxpo/dart_algolia no-readme %}

I will be creating a brand new app in Flutter and quickly install the `dart_algolia` package by adding it as a dependency in the `pubspec.yaml` file. Save the file and run `flutter packages get` command to install the package.

![Added algolia as a dependency in pubspec.yaml](https://miro.medium.com/max/1400/1*zOjlzFSUt2Ow5ALgMqrGXw.png)


Next, in the `main.dart` file, we will create a minimal UI with a text field and a button to perform the search. Here is the complete `build()` method.

```
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text("Algolia Search"),
    ),
    body: Container(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text("Search"),
          TextField(
            decoration: InputDecoration(hintText: "Search query here..."),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              FlatButton(
                color: Colors.blue,
                child: Text(
                  "Search",
                  style: TextStyle(color: Colors.white),
                ),
                onPressed: () {},
              ),
            ],
          )
        ],
      ),
    ), // This trailing comma makes auto-formatting nicer for build methods.
  );
}
```

And here is how it looks like.

![Preview of the UI](https://miro.medium.com/max/1400/1*WSfkt6enIVWKSB9t8CYOQQ.png)

Next, let’s quickly add a `TextEditingController` so that we can access the value of the `TextField`.

```
TextEditingController _searchText = TextEditingController(text: "");
```

And then assign the `_searchText` to the controller property of the `TextField`.

```
TextField(
  controller: _searchText,
  decoration: InputDecoration(hintText: "Search query here..."),
),
```

Finally, it is time to write the code for the `onPressed` event on the `FlatButton`. Let’s create a method called `search` and write all the search logic inside of it.

```
FlatButton(
  color: Colors.blue,
  child: Text(
    "Search",
    style: TextStyle(color: Colors.white),
  ),
  onPressed: _search,
),
```

We will now add a few variables at the top inside the class.

```
List<AlgoliaObjectSnapshot> _results = [];
bool _searching = false;
```

The `_results` array will hold the data returned by Algolia and we will use this to generate a `ListView`. The `_searching` boolean will just be used to indicate if searching has completed or not.

```
_search() async {
  setState(() {
    _searching = true;
  });

  Algolia algolia = Algolia.init(
    applicationId: 'APP_ID',
    apiKey: 'SEARCH_API_KEY',
  );

  AlgoliaQuery query = algolia.instance.index('Posts');
  query = query.search(_searchText.text);

  _results = (await query.getObjects()).hits;

  setState(() {
    _searching = false;
  });
}
```

Finally, let’s create the `ListView` using `ListView.builder` widget constructor. We will put it as the last element inside the `Column` and wrap it inside an `Expanded` widget so that it occupies all the available vertical space.

```
Expanded(
  child: _searching == true
      ? Center(
          child: Text("Searching, please wait..."),
        )
      : _results.length == 0
          ? Center(
              child: Text("No results found."),
            )
          : ListView.builder(
              itemCount: _results.length,
              itemBuilder: (BuildContext ctx, int index) {
                AlgoliaObjectSnapshot snap = _results[index];

                return ListTile(
                  leading: CircleAvatar(
                    child: Text(
                      (index + 1).toString(),
                    ),
                  ),
                  title: Text(snap.data["post_title"]),
                  subtitle: Text(snap.data["post_text"]),
                );
              },
            ),
),
```

All done. The code can be a little bit cleaner but let’s ignore it for now. Let’s test this out.

![Final App Demo](https://miro.medium.com/max/1224/1*m9lPWaFMkmviYfLJeoqjgQ.gif)

That is just the beginning. Algolia offers tremendous power and allows you to perform facetted searches, geolocation-based searches, ranking and sorting, and a lot more. I am still experimenting and you should give it a shot. Also, feel free to share your experiences.

The code for the demo above is available on GitHub.

{% github samarthagarwal/flutter_algolia %}

Here is a complete video on YouTube where I have implemented Full-text search using Algolia in a Flutter app.

{% youtube vDsOQ58TrI0 %}

You can also subscribe to my YouTube channel for more fun content. 

Keep coding, cheers.