Flutter Community
Published in

Flutter Community

Full-Text Search in Flutter with Firestore and Algolia

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 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 title of the book exactly the same way you have it in the database.

Enter Algolia. 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 two 2 years ago 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 video on my YouTube channel to see how I uploaded the data from a Firestore collection to an Algolia index.

The above code snippets 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.

Posts index created on Algolia

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

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.

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

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

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 on 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

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 at https://github.com/samarthagarwal/flutter_algolia

Keep coding, cheers.

--

--

--

Articles and Stories from the Flutter Community

Recommended from Medium

Reflections on Judging Open Source Software Development

Dev C++ Hello World

Optimising a query in Snowflake.

How to become a software engineer without a degree?

Setup Multiple Nginx Ingress Controllers on EKS Clusters

Capture tokens and secret id from Hashicorp Vault and assign it to Windows Env Variables…

Oct Updates

Puppertino is dropping support for SASS, here’s why.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Samarth Agarwal

Samarth Agarwal

Software Developer, Consultant and Instructor

More from Medium

How I’m using Cubits from the Bloc library to manage my states

Flutter Vs Kotlin: Best Framework for Cross-Platform App Development

Communicate Between Flutter and Native Android and iOS Code Using Platform Channel

Person on laptop

How to use Redux in Flutter