You have unlimited access as a PRO member
You are receiving a free preview of 3 lessons
Your free preview as expired - please upgrade to PRO
Contents
Recent Posts
- Object Oriented Programming With TypeScript
- Angular Elements Advanced Techniques
- TypeScript - the Basics
- The Real State of JavaScript 2018
- Cloud Scheduler for Firebase Functions
- Testing Firestore Security Rules With the Emulator
- How to Use Git and Github
- Infinite Virtual Scroll With the Angular CDK
- Build a Group Chat With Firestore
- Async Await Pro Tips
Typeahead Autocomplete With Firestore
Episode 89 written by Jeff DelaneyIn this lesson, we will build a basic typeahead or autocomplete system using nothing but Firestore. It relies on an object/map data structure that exposes some of the more advanced query patterns available to us. The database contains a collection of movie documents, and our goal is to build a search form that will auto-populate results based on the movie’s title.
Method 1: Offset with the Magic uf8ff Character
A few months ago I created a RealtimeDB Autocomplete lesson that uses '\uf8ff'
, which is a very high Unicode point. It comes after all other commonly used characters, so the query will match all other values that follow it. In Firestore, that same principle can be applied like so:
const start = 'USER FORM INPUT HERE' |
This method is works well in most cases, but I also want to talk about an alternative approach using a searchable index object on the document.
Method 2: Firestore Searchable Data Structure
For each movie document, we will create a mini-index of searchable terms. It will break the word down character-by-character into an object. I will provide you with a script that you can run either clientside or serverside to build this index automatically.
movies/{docId} |
Firestore automatically indexes every key of the searchIndex
object, so we can make queries against each combination of characters. Later in this lesson we will create a Cloud Function that generates this searchable index automatically.
Pros
While this data structure may not seem ideal, it does provide several benefits over full-text search.
- Does not require paid dependencies (Algolia, ElasticSearch, etc.)
- Easier implementation
Cons
We are also faced with some limitations, so I’d like to lay those out:
- It can only search one property (i.e the movie title).
- It will not detect spelling errors.
- The searchable index will be case-insensitive.
Hypothetically, you could address these issues by expanding the object with additional properties, but you can only embed so much data on a single document.
If you need a system that can handle spelling errors or complex multi-property suggestions, your life will be made easier with a full-text search engine like Algolia.
Angular Autocomplete Search Component
Now that we know what our data structure looks like, let’s build a component that can make the query reactively.
It works by observing the form input as a Subject
and updates the search term offset value on each keyup
event. When a new value is entered, it updates the Firestore query on the searchable index, telling firestore to emit a new array of movie documents that match the title substring.
type-ahead.component.ts
@Component({ |
import { Component, OnInit } from '@angular/core'; |
type-ahead.component.html
In the HTML, we just need to bind our onkeyup
event handler to a form input, then loop over the results observable.
<h1>Start Typing...</h1> |
Automated Indexing with Cloud Functions
While it’s possible to build the index clientside, this is a perfect feature to delegate to a Firebase Cloud Function.
Let’s initialize cloud functions, making sure to choose the TypeScript environment.
firebase init functions |
index.ts
Our cloud function will automatically index the characters of the movie title into a searchable format. Whenever a new movie is added to the database, this function will run and update the data.
If you want to run this update directly from Angular, simply extract the createIndex
function to your frontend code and use it to format the data before running a Firestore update/set operation.
import * as functions from 'firebase-functions'; |
import * as functions from 'firebase-functions'; |
The End
This solution is ideal when you only need a basic autocomplete feature and want to avoid 3rd party services that could add too much complexity or cost to your project. In essence, you’re trading flexibility for simplicity with this solution. Feel free to reach out in the comments or on Slack if you have questions.