Skip to content

Latest commit

 

History

History
311 lines (262 loc) · 10.1 KB

collections.md

File metadata and controls

311 lines (262 loc) · 10.1 KB

3. Collections in AngularFirestore

This documentation is updated for modular version 9 of angular fire package. For previous or missing features refer to older docs.

Cloud Firestore is a NoSQL, document-oriented database. Unlike a SQL database, there are no tables or rows. Instead, you store data in documents, which are organized into collections. Each document contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents.

Using AngularFirestoreCollection

The AngularFirestoreCollection service is a wrapper around the native Firestore SDK's CollectionReference and Query types. It is a generic service that provides you with a strongly typed set of methods for manipulating and streaming data. This service is designed for use as an @Injectable().

import { Component } from "@angular/core";
import { Subscription } from "rxjs";
import { collection, getDocs, QuerySnapshot } from "firebase/firestore";
import { Firestore } from "@angular/fire/firestore";
export interface Item {
  name: string;
}

@Component({
  selector: "app-root",
  template: `
    <ul>
      <li *ngFor="let item of items">
        {{ item.name }}
      </li>
    </ul>
  `,
})
export class AppComponent implements OnInit {
  items: Item[] = [];
  constructor(private firestore: Firestore) {}
  ngOnInit() {
    getDocs(collection(this.firestore, "data")).then(
      (querySnapshot: QuerySnapshot) => {
        this.items = [];
        querySnapshot.forEach((doc: any) => {
          this.items.push(doc.data());
        });
      }
    );
  }
}

Streaming collection data or get realtime data

There are multiple ways of streaming collection data from Firestore.

  1. collectionSnapshots() It is a method which returns a subscription with latest and complete list of documents in a collection.
  2. collectionChanges() It is a method which returns a subscription with only the changed data in a collection.

collectionSnapshots() Implementation example

import { Component } from "@angular/core";
import { Subscription } from "rxjs";
import { collection } from "firebase/firestore";
import { collectionSnapshots, Firestore } from "@angular/fire/firestore";
export interface Item {
  name: string;
}

@Component({
  selector: "app-root",
  template: `
    <ul>
      <li *ngFor="let item of items">
        {{ item.name }}
      </li>
    </ul>
  `,
})
export class AppComponent implements OnInit, OnDestroy {
  itemListSubscription: Subscription = Subscription.EMPTY;
  items: Item[] = [];
  constructor(private firestore: Firestore) {}
  ngOnInit() {
    this.dataListSubscription = collectionSnapshots(
      collection(this.firestore, "data")
    ).subscribe((data) => {
      this.items = [];
      data.forEach((doc: any) => {
        this.items.push(doc.data());
      });
    });
  }
  ngOnDestroy(): void {
    this.itemListSubscription.unsubscribe();
  }
}

collectionChanges() Implementation example

This method only returns data of a changed document so it can be used for keeping an eye on edits on any dataset. This is good method because you don't have to compare all data on the fly to know which one is new.

Cool tip: It has the best use case when showing notifications. But remember

WARNING: It outputs every document last change present in the collection when it is first invoked or executed.

import { Component } from "@angular/core";
import { Subscription } from "rxjs";
import { collection } from "firebase/firestore";
import { collectionChanges, Firestore } from "@angular/fire/firestore";
export interface Item {
  name: string;
}

@Component({
  selector: "app-root",
  template: `
    <ul>
      <li *ngFor="let item of items">
        {{ item.doc.data().name }}
        <strong>Type: </strong>{{ item.type }} <strong>New Index: </strong
        >{{ item.newIndex }} <strong>Old Index: </strong>{{ item.oldIndex }}
      </li>
    </ul>
  `,
})
export class AppComponent implements OnInit, OnDestroy {
  itemListSubscription: Subscription = Subscription.EMPTY;
  items: Item[] = [];
  constructor(private firestore: Firestore) {}
  ngOnInit() {
    this.dataListSubscription = collectionChanges(
      collection(this.firestore, "data")
    ).subscribe((dataChange) => {
      this.items = [];
      dataChange.forEach((doc: any) => {
        this.items.push(doc.data());
      });
    });
  }
  ngOnDestroy(): void {
    this.itemListSubscription.unsubscribe();
  }
}

Conditional collection querying

Cloud Firestore provides powerful query functionality for specifying which documents you want to retrieve from a collection or collection group. These queries can also be used with either getDocs() or collectionSnapshots(), as described in Get Data and Get Realtime Updates.

More on queries refer here

Simple example demonstrating functioning of queries.

import { Component } from "@angular/core";
import { collection, getDocs, query, where } from "firebase/firestore";
import { Firestore } from "@angular/fire/firestore";
export interface Item {
  name: string;
  salary:number;
}

@Component({
  selector: "app-root",
  template: `
    <ul>
      <li *ngFor="let item of items">
        {{ item.name }}
      </li>
    </ul>
  `,
})
export class AppComponent implements OnInit {
  items: Item[] = [];
  constructor(private firestore: Firestore) {}
  ngOnInit() {
    getDocs(
      query(
        collection(this.firestore,'data'),
        where('salary', '>=', 400))).then(collections:any)=>{
        collections.forEach((doc:any)=>{
         this.items.push(doc.data())
        })
    })
  }
}

The where() method takes three parameters: a field to filter on, a comparison operator, and a value. Cloud Firestore supports the following comparison operators:

  • < less than
  • <= less than or equal to
  • == equal to
  • \> greater than
  • \>= greater than or equal to
  • != not equal to
  • array-contains
  • array-contains-any
  • in
  • not-in

Looking for more advanced queries


Order and limit data with Cloud Firestore

Cloud Firestore provides powerful query functionality for specifying which documents you want to retrieve from a collection. These queries can also be used with either getDocs() or collectionChanges(), as described in Get Data.

Order and limit data

By default, a query retrieves all documents that satisfy the query in ascending order by document ID. You can specify the sort order for your data using orderBy(), and you can limit the number of documents retrieved using limit().

For example, you could query for the first 3 cities alphabetically with:

import { Component } from "@angular/core";
import { collection, getDocs, query, limit, where } from "firebase/firestore";
import { Firestore } from "@angular/fire/firestore";
export interface City {
  name: string;
}

@Component({
  selector: "app-root",
  template: `
    <ul>
      <li *ngFor="let city of cities">
        {{ city.name }}
      </li>
    </ul>
  `,
})
export class AppComponent implements OnInit {
  cities: City[] = [];
  constructor(private firestore: Firestore) {}
  ngOnInit() {
    getDocs(
      query(
        collection(this.firestore,'cities'),
        orderBy('name'),
        limit(10))).then((collections:any)=>{
          collections.forEach((doc:any)=>{
            console.log('Conditional',doc.data())
            this.cities.push(doc.data())
          })
    })
  }
}

For more references and extra options visit full docs

Paginate data with query cursors

With query cursors in Cloud Firestore, you can split data returned by a query into batches according to the parameters you define in your query.

Query cursors define the start and end points for a query, allowing you to:

  • Return a subset of the data.
  • Paginate query results.

However, to define a specific range for a query, you should use the where() method described in Simple Queries.

Add a simple cursor to a query

Use the startAt() or startAfter() methods to define the start point for a query. The startAt() method includes the start point, while the startAfter() method excludes it.

For example, if you use startAt(A) in a query, it returns the entire alphabet. If you use startAfter(A) instead, it returns B-Z.

import { Component } from "@angular/core";
import { collection, getDocs, query, limit, where } from "firebase/firestore";
import { Firestore } from "@angular/fire/firestore";
export interface City {
  name: string;
  population:number;
}

@Component({
  selector: "app-root",
  template: `
    <ul>
      <li *ngFor="let city of cities">
        {{ city.name }}
      </li>
    </ul>
  `,
})
export class AppComponent implements OnInit {
  cities: City[] = [];
  constructor(private firestore: Firestore) {}
  ngOnInit() {
    getDocs(
      query(
        collection(this.firestore,'population'),
        orderBy("population"),
        startAt(1000000))).then((collections:any)=>{
          collections.forEach((doc:any)=>{
            this.cities.push(doc.data())
        })
    })
  }
}

Now to paginate this query. You just need to get the length of current cities length(-1) and then use it inside startAfter function so that all the results will be after the last document

Full example on pagination

Manipulating individual documents

To retrieve, update, or delete an individual document you can use the doc() method. This method returns an AngularFirestoreDocument, which provides methods for streaming, updating, and deleting. See Using Documents with AngularFirestore for more information on how to use documents.