Querying and Sorting Firestore Data

Photo of Paweł Wysowski

Paweł Wysowski

Updated Jan 4, 2023 • 5 min read

Using Firestore, you will probably want to query the data in some way before fetching it. If you have experience in .NET or .NET Core, there is the Entity Framework package, which allows you to do some querying operations before returning data. When it comes to Firestore, you can mostly do the same things! There are some limitations, but firstly, let’s talk about the possibilities.

You can query data using multiple operators and one method – where, which allows you to query data when using Firestore. You just have to pass the property for which you will be querying data, then pass the operator and then the parameter value. For this article, I’ve created a simple database structure, and you can take a look at the data inside it here:

[
({
name: 'Just Cause 3',
releaseDate: '7/18/2015',
genres: ['action'],
},
{
name: 'Witcher 3',
releaseDate: '6/18/2015',
genres: ['action', 'rpg'],
},
{
name: 'Assassins Creed Odyssey',
releaseDate: '11/18/2018',
genres: ['action', 'adventure'],
},
{
name: 'The Last of Us 2',
releaseDate: '6/17/2020',
genre: ['action', 'adventure'],
},
{
name: 'Far Cry 5',
releaseDate: '3/18/2018',
genre: ['fps'],
}),
];

Now let’s perform some queries on it!

Simple queries

Equality operator

The first and most obvious operator you can use is the equality operator. Let’s query the data to find Witcher 3:

const gamesRef = db.collection('games');
const queryRef = gamesRef.where('name', '==', 'Witcher 3');

Now to execute the query, you have to call the get() method:

const witcherData = await queryRef.get();

Greater/less than operator

Now let’s find all games released after 2016. To do that, we can use the > operator. There are of course all comparison operators that are available in most programming languages: <, >, <=, >=.

const gamesRef = db.collection('games');
const queryRef = gamesRef.where('releaseDate', '>', new Date('2016-01-01'));

Array-contains operator

This one is really useful when you want to fetch data only if your parameter value is inside the array. In our case we can fetch action games:

const gamesRef = db.collection('games');
const queryRef = gamesRef.where('genres', 'array-contains', 'action');

Just remember – this query will return games that also have some other genres in the genres array!

Array-contains-any operator

When you want to fetch data using an array to filter it, you will probably want to use the array-contains-any operator. In our case, we can fetch games that are categorized as RPG games or adventure games:

const gamesRef = db.collection('games');
const queryRef = gamesRef.where('genres', 'array-contains-any', ['adventure', 'rpg'])

In operator

The in operator is quite similar to array-contains-any, but it can be used with non-array fields. So let’s try it out – we can get Witcher 3 and The Last of Us 2 games with the in operator:

const gamesRef = db.collection('games');
const queryRef = gamesRef.where('name', 'in', ['Witcher 3', 'The Last of Us 2'])

Compound queries

Sometimes you will need to query data in a more complex way. There will be times that you’re not able to query data by just chaining where methods, and then you just have to create a custom index. Let’s go through the most common compound queries.

Queries without a custom index

You can chain where methods as long as:

  • You use a comparison operator on a single field.
  • You use only one array-contains or array-contains-any operator in a compound query.
const gamesRef = db.collection('games');
const queryRef = gamesRef
.where('name', 'in', ['Witcher 3', 'The Last of Us 2'])
.where('genres', 'array-contains', 'adventure')

Queries with a custom index

Sometimes you will run into queries that can’t be executed. Fortunately, Firestore handles these types of cases automatically and you will see this message in the console:

Error: [firestore/failed-precondition] The query requires an index.
You can create it here: https://console.firebase.google.com/v1/r/project/fir-testsplayground/firestore/indexes?create_composite=ClFwcm9qZWN0cy9maXItdGVzdHNwbGF5Z3JvdW5kL2RhdGFiYXNlcy8oZGVmYXVsdCkvY29sbGVjdGlvbkdyb3Vwcy9nYW1lcy9pbmRleGVzL18QARoKCgZnZW5yZXMYARoICgRuYW1lEAEaDAoIX19uYW1lX18QAQ

Now you will have to click on this link and create your composite index. Remember, you can only create 200 composite indexes in your Firestore, so use them wisely!

You can also create these fancy indexes using the Firebase Console. Just go to Firestore > Indexes > Composite. You will then have to choose the fields that will be used for indexing and the order that they will be used for querying.

Sorting data

You can sort data using the orderBy method. There is only one not-so-obvious limitation concerning ordering data – when you’re using a range comparison (<, <=, >, >=), you can order data only by the parameter you’re querying with. So in our case, when we fetched games released after 2016, we could only order them by release date (i.e., ascending order).

const gamesRef = db.collection('games');
const queryRef = gamesRef
.where('releaseDate', '>', new Date('2016-01-01'));
.orderBy('releaseDate', 'asc');

Photo of Paweł Wysowski

More posts by this author

Paweł Wysowski

Efficient software development  Build faster, deliver more  Start now!

Read more on our Blog

Check out the knowledge base collected and distilled by experienced professionals.

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business