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
Faster Firestore With Cloud Functions Data Aggregation
Episode 63 written by Jeff DelaneyIn this lesson, I will show you how to make Firestore queries faster and more cost-effective by aggregating data from a subcollection to its parent. Aggregation is simply the process of totaling up a bunch of documents and calculating some combined or cumulative information about them.
A Firebase Cloud Function configured with a Firestore database trigger makes it possible to perform this task automatically whenever a document changes. This demo simulates the relationship between blog posts and a subcollection of comments. Our goal is to show a post’s total comment count and last five comments, while only reading a single document.
This technique is best suited for situations where you perform many reads and relatively few writes. Think Yelp! reviews. Many people want to view the total aggregated review score, but only a small percentage actually write their own review.
Full source code for the Firestore data aggregation cloud function.
Isn’t that Data Duplication
You must have a RDBMS background… Data duplication in a NoSQL document database is perfectly acceptable. A little bit of duplication makes it possible to display data faster by avoiding unnecessary queries.
Imagine you wanted to show a feed of 10 posts, each with the comment count. If each post has 10 comments, a grand total 110 document reads would be needed to show this content per user. If you have a 1000 daily active users, that’s 110,000 reads per day just to show this feed - that will not scale well.
Data Aggregation Cloud Function
If new to cloud functions, first initialize your project.
firebase init functions |
Also, make sure the package.json is up-to-date. Older version may not support Firestore.
"dependencies": { |
Advantages
Data aggregation excels when you need to show relational data or a preview from another collection, but would rather not make a ton of extra queries to do so.
- Faster reads. Reading a single document is always faster then reading multiple documents. The more documents you aggregate, the higher the speed gains will be.
- Lower costs. Assuming you read the subcollection frequently, but write infrequently, you should see major cost savings as your user base grows.
- Aggregate data is cool. The with right type of data, you can display all sorts of interesting statistics about a collection and improve the user experience.
Drawbacks
Performing the update from a cloud function has few tradeoffs.
- Higher latency on writes. In addition to performing the read/write, you also need to invoke the function. Overall, I found that aggregating around 15 comments took about 1 second. Not bad, but noticeably slower than a direct Firestore connection.
- Different cost structure. Although are goal is to reduce reads, we now have additional writes and cloud function invocations.
- Additional maintenance. Another cloud function to worry about.
index.js
The cloud function will be triggered on any comment write operation - create, update, or delete. When one occurs, a reference is made the the parent post document, then we query the entire subcollection.
A querySnapshot
is not an array, even though you can deceptively call forEach
on it. It is a class called QuerySnapshot in Firestore that only has a few methods on it. In the function, I convert its underlying data to a regular JS Array so it can be more easily aggregated. The three aggregated fields are as follows:
commentCount
- total size of collectionrecentComments
- last five commentslastActivity
- timestamp of last comment
const functions = require('firebase-functions'); |
Don’t forget to deploy the function.
firebase deploy --only functions |
If your aggregation operation will be updating multiple documents, it’s a good idea to use a Firestore transaction or batch to make the operation atomic. All operations should succeed or fail together.
Lazy Loading Firestore Data in the Component
We can setup a simple blog in the app component to simulate the relationship between blog posts and comments. This is not meant to be a robust feature, but rather to show you how aggregated data can be used to make queries faster.
App Component TypeScript
The loadMore()
method will call valueChanges()
only when the user clicks the button. This will load the Firestore data lazily for the small percentage of users who want to see older comments.
import { Component, OnInit } from '@angular/core'; |
Html
Notice how I only show the post.recentComments
when the comments$
Observable is not defined. If the user decides to load more, then the aggregated data is replaced with the full comments collection.
<div *ngIf="post$ | async as post"> |
The End
Today we built a simple data aggregation cloud function that could be expanded to handle more sophisticated tasks. Hopefully this gives you some inspiration that will lead to faster/cheaper Firestore queries and help you deal with the challenges of large collections.