Build a pipeline for full-text search indexing with Firestore Cloud Functions 682 words.
Last Updated
One of the most commonly encountered limitations of Cloud Firestore (and GCP) is full-text search. This functionality is essential if you need to query complex text patterns in a database or filter results by multiple dynamic properties. My favorite solution to this limitation is Algolia, which provides a powerful, developer-friendly, search & discovery API. In the following lesson you will learn how to sync your Firestore data to an Algolia index via Cloud Functions.
Frontend Integrations
This lesson is integrated with multiple frontend frameworks. After deploying the backend code in this lesson, choose your favorite frontend flavor 🍧.
Initial Setup
Blaze plan required
Make sure you are at least using the Firebase Blaze plan because this will enable you to make network requests to third-party APIs like Algolia.
Initialize Cloud Functions
First, initialize Cloud Functions in your project with the Firebase Tools CLI. This lesson uses vanilla JS, but feel free to select TypeScript if you prefer.
npm i -g firebase-tools
firebase init functions
Install Algolia
Indexing tasks can be handled in Node with the Algolia JS SDK package.
cd functions
npm install algoliasearch
Create an Index
Algolia provides a guided UI for building indexes that support full-text search.
Add the Algolia API Key to Firebase
You can find your credentials under the API Keys tab. Your backend code requires the Application ID and Admin API Key. Keep the admin key safe and do not expose it to the public.
Now set the API key and template ID in the Firebase project with the following command.
firebase functions:config:set algolia.app=YOUR_APP_ID algolia.key=ADMIN_API_KEY
Algolia Cloud Functions
Setup the Cloud Functions environment by initializing the Algolia client and index.
const functions = require('firebase-functions');
const algoliasearch = require('algoliasearch');
const APP_ID = functions.config().algolia.app;
const ADMIN_KEY = functions.config().algolia.key;
const client = algoliasearch(APP_ID, ADMIN_KEY);
const index = client.initIndex('customers');
/// Cloud Functions
exports.addToIndex; // TODO
exports.updateIndex; // TODO
exports.deleteFromIndex; // TODO
Add Record to Algolia
Our goal is to duplicate a subset of data from the Firestore customer document to Algolia. The first step is to add a new record to the index with the onCreate
Firestore event.
💡 Tip. Only index data in Algolia that is actually useful for searching and/or displaying in search results. Do NOT index personally identifiable information (PII), like phone numbers and email addresses. You may not need to duplicate the entire document.
exports.addToIndex = functions.firestore.document('customers/{customerId}')
.onCreate(snapshot => {
const data = snapshot.data();
const objectID = snapshot.id;
return index.saveObject({ ...data, objectID });
});
Update a Record in Algolia
If the Firestore document data changes, we want these changes reflected in the index and search results. We can easily handle this task with an onUpdate
function.
exports.updateIndex = functions.firestore.document('customers/{customerId}')
.onUpdate((change) => {
const newData = change.after.data();
const objectID = change.after.id;
return index.saveObject({ ...newData, objectID });
});
Delete a Record
When a Firestore document is deleted, we must also remove it from the search index. In this case, we simply pass the objectID to Algolia within an onDelete
function.
exports.deleteFromIndex = functions.firestore.document('customers/{customerId}')
.onDelete(snapshot =>
index.deleteObject(snapshot.id)
);
Deploy
Deploy your functions and test them from the Firebase console, or follow the seeding instructions in the next section.
firebase deploy --only functions
Optional: Seed the Database
In the video you saw how I quickly seeded Firestore with dummy data, which was accomplished with Faker and the Firebase Admin SDK. Create a vanilla node script in the functions directory that sends writes to the database.
const admin = require('firebase-admin');
admin.initializeApp();
const faker = require('faker');
const db = admin.firestore();
const fakeIt = () => {
return db.collection('customers').add({
username: faker.internet.userName(),
avatar: faker.internet.avatar(),
bio: faker.hacker.phrase()
});
}
Array(20).fill(0).forEach(fakeIt);
Run the script from the command line like so:
cd functions
node seed.js