Querying and Sorting Firestore Data
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');