Building a Leaderboard With Firestore: OrderBy and Limit
by Steve Marx on
I’ve added a leaderboard to New High Score. It really makes clicking over and over again competitive!
The leaderboard is fairly typical. It shows the top-5 players, sorted by descending score. In this post, I’ll show you how Firestore supports this type of query. I’ll also show you how security rules can be used to restrict queries.
Sorting data
Much like in SQL, sorting in Firestore is just an “order by” away. A simple query to get all the games in New High Score looks like this:
db.collection("games")
.onSnapshot((snapshot) => { ... });
Calling onSnapshot()
on the query (as opposed to get()
) gives us a callback each time the data changes. This makes it easy to have a live updating leaderboard. The argument to the callback has a docs
field which contains an array of the returned documents.
To sort by a field, I can just add an orderBy()
:
db.collection("games")
.orderBy("score", "desc")
.onSnapshot((snapshot) => { ... });
The second argument to orderBy()
controls the sort order, either “asc” (ascending) or “desc” (descending).
Indexing
All Firestore queries are backed by an index. Some indexes are created automatically, including the one I need for the above query. Among other things, Firestore automatically creates single-field ascending and descending indexes for every scalar field in each document.
This covers the above query, which just needs to sort on the value of an integer field. For more complex queries, you may have to explicitly create indexes. I plan to dig in more to indexes in a future post.
Limiting the number of results
My leaderboard only shows the top five scores. This is accomplished with a call to limit()
, which again is reminiscent of SQL:
db.collection("games")
.orderBy("score", "desc")
.limit(5)
.onSnapshot((snapshot) => { ... });
Security rules
Security rules can dictate which types of queries are allowed. Here are the rules I wrote to control read access to games:
allow get: if isOwner();
allow list: if request.query.orderBy.score == "DESC"
&& request.query.limit <= 5;
If you’ve been following this series, you may be used to seeing just allow read
, which covers both get
and list
. Here, I’ve broken them out separately.
The get
rule makes use of isOwner()
from my last post. This allows a user to read their own score.
The list
rule allows clients to list all the game documents, but only if they sort by descending score and ask for at most the top five. Note that request.query.orderBy
is a map of field names to sort direction, and the direction is always capitalized. (I couldn’t find this anywhere in the documentation!)
Summary
- Firestore queries can use
orderBy()
to sort documents andlimit()
to limit the number of documents retrieved. - All queries must be backed by an index, but simple indexes are automatically created.
- Security rules can control what types of queries are allowed.