Spotted a bug? Have a great idea? Help us make google.dev great!

Goals

In this codelab, you'll enhance a restaurant recommendation web app powered by Cloud Firestore.

What you'll learn

  • Write complex Cloud Firestore queries
  • Deploy indexes

What you'll need

This codelab builds on the previous codelab in this playlist. If you have not completed the previous codelab, please do so before continuing here.

Currently, your app displays a list of restaurants, but there's no way for the user to filter based on their needs. In this section, you'll use Cloud Firestore's advanced querying to enable filtering.

Here's an example of a simple query to fetch all restaurants in a category:

var filteredQuery = query.where('category', '==', 'Dim Sum')

As its name implies, the where() method makes the query return only members of the collection whose fields meets the specified restrictions. In this case, the query returns only restaurants where category is Dim Sum.

In your app, the user can chain multiple filters to create specific queries, like "Pizza in San Francisco" or "Seafood in Los Angeles ordered by popularity".

You'll create a method that builds up a query that filters restaurants based on multiple criteria selected by the user.

  1. Go back to the file scripts/FriendlyEats.Data.js.
  2. Find the function FriendlyEats.prototype.getFilteredRestaurants.
  3. Replace the entire function with the following code:

FriendlyEats.Data.js

FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) {
  var query = firebase.firestore().collection('restaurants');

  if (filters.category !== 'Any') {
    query = query.where('category', '==', filters.category);
  }

  if (filters.city !== 'Any') {
    query = query.where('city', '==', filters.city);
  }

  if (filters.price !== 'Any') {
    query = query.where('price', '==', filters.price.length);
  }

  if (filters.sort === 'Rating') {
    query = query.orderBy('avgRating', 'desc');
  } else if (filters.sort === 'Reviews') {
    query = query.orderBy('numRatings', 'desc');
  }

  this.getDocumentsInQuery(query, renderer);
};

This code adds multiple where filters and a single orderBy clause to build a compound query based on user input.

Refresh the FriendlyEats app in your browser, and then verify that you can filter by price, city, and category. While testing, you'll see errors in the JavaScript console of your browser that look like this:

The query requires an index. You can create it here: https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

These errors are because Cloud Firestore requires indexes for most compound queries. Requiring indexes on queries keeps Cloud Firestore fast at scale.

Opening the link from the error message automatically opens the index creation UI in the Firebase console with the correct parameters filled in. In the next section, you'll write and deploy the indexes needed for this application.

If you don't want to explore every path in your app and follow each of the index creation links, you can deploy many indexes at once using the Firebase CLI.

  1. In your app's downloaded local directory, find the firestore.indexes.json file.

This file describes all of the indexes needed for all of the possible combinations of filters.

firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. Deploy these indexes with the following command:
firebase deploy --only firestore:indexes

After a few minutes, your indexes will be live and the error messages will go away.