In this section, we will delve into advanced querying techniques in Cloud Firestore. These techniques will help you retrieve data more efficiently and perform complex queries that go beyond basic CRUD operations.

Key Concepts

  1. Compound Queries: Combining multiple conditions in a single query.
  2. Array-Contains Queries: Querying documents where a specific array field contains a certain value.
  3. In Queries: Querying documents where a field matches any value in a given list.
  4. Range Queries: Querying documents within a specific range of values.
  5. Pagination: Retrieving data in chunks to handle large datasets.
  6. Indexing: Optimizing queries with custom indexes.

Compound Queries

Compound queries allow you to combine multiple conditions using logical operators.

Example

// Query documents where 'age' is greater than 25 and 'city' is 'New York'
db.collection("users")
  .where("age", ">", 25)
  .where("city", "==", "New York")
  .get()
  .then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  })
  .catch((error) => {
    console.log("Error getting documents: ", error);
  });

Explanation

  • where("age", ">", 25): Filters documents where the age field is greater than 25.
  • where("city", "==", "New York"): Further filters documents where the city field is 'New York'.

Array-Contains Queries

Array-contains queries allow you to filter documents where a specific array field contains a certain value.

Example

// Query documents where 'tags' array contains 'firebase'
db.collection("posts")
  .where("tags", "array-contains", "firebase")
  .get()
  .then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  })
  .catch((error) => {
    console.log("Error getting documents: ", error);
  });

Explanation

  • where("tags", "array-contains", "firebase"): Filters documents where the tags array contains the value 'firebase'.

In Queries

In queries allow you to filter documents where a field matches any value in a given list.

Example

// Query documents where 'status' is either 'active' or 'pending'
db.collection("tasks")
  .where("status", "in", ["active", "pending"])
  .get()
  .then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  })
  .catch((error) => {
    console.log("Error getting documents: ", error);
  });

Explanation

  • where("status", "in", ["active", "pending"]): Filters documents where the status field is either 'active' or 'pending'.

Range Queries

Range queries allow you to filter documents within a specific range of values.

Example

// Query documents where 'price' is between 10 and 50
db.collection("products")
  .where("price", ">=", 10)
  .where("price", "<=", 50)
  .get()
  .then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  })
  .catch((error) => {
    console.log("Error getting documents: ", error);
  });

Explanation

  • where("price", ">=", 10): Filters documents where the price field is greater than or equal to 10.
  • where("price", "<=", 50): Further filters documents where the price field is less than or equal to 50.

Pagination

Pagination helps in retrieving data in chunks, which is useful for handling large datasets.

Example

let first = db.collection("users")
  .orderBy("name")
  .limit(10);

first.get().then((documentSnapshots) => {
  // Get the last visible document
  let lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];

  // Construct a new query starting at this document
  let next = db.collection("users")
    .orderBy("name")
    .startAfter(lastVisible)
    .limit(10);

  next.get().then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  });
});

Explanation

  • orderBy("name"): Orders the documents by the name field.
  • limit(10): Limits the query to 10 documents.
  • startAfter(lastVisible): Starts the next query after the last visible document from the previous query.

Indexing

Custom indexes can optimize your queries, especially when using compound queries.

Example

To create a custom index, you need to use the Firebase Console. Navigate to the Firestore section, and under the Indexes tab, you can create a new index by specifying the fields and their order.

Explanation

  • Custom indexes help in optimizing queries that involve multiple fields or complex conditions.

Practical Exercise

Task

  1. Create a Firestore collection named books.
  2. Add documents with the following fields: title, author, genre, publishedYear, and ratings (an array of numbers).
  3. Write a query to retrieve books published after the year 2000 and have a rating of at least 4.

Solution

// Add sample documents
db.collection("books").add({
  title: "Book One",
  author: "Author A",
  genre: "Fiction",
  publishedYear: 2001,
  ratings: [4, 5]
});

db.collection("books").add({
  title: "Book Two",
  author: "Author B",
  genre: "Non-Fiction",
  publishedYear: 1999,
  ratings: [3, 4]
});

// Query documents
db.collection("books")
  .where("publishedYear", ">", 2000)
  .where("ratings", "array-contains", 4)
  .get()
  .then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  })
  .catch((error) => {
    console.log("Error getting documents: ", error);
  });

Explanation

  • where("publishedYear", ">", 2000): Filters books published after the year 2000.
  • where("ratings", "array-contains", 4): Further filters books that have a rating of at least 4.

Conclusion

In this section, we explored advanced querying techniques in Cloud Firestore, including compound queries, array-contains queries, in queries, range queries, pagination, and indexing. These techniques will help you retrieve data more efficiently and perform complex queries. In the next module, we will discuss security rules to protect your Firestore data.

© Copyright 2024. All rights reserved