Skip to content

Using Cloud Firestore

Flindy Ly edited this page Feb 23, 2023 · 1 revision

Documentation: https://firebase.google.com/docs/firestore

Firebase’s Cloud Firestore is a NoSQL (i.e. schemaless) database that stores data in documents and collections. Each Document contains a set of key-value pairs, and Documents must be grouped under Collections. Documents can also contain sub-collections.

The current Firestore structure for Media Hub is modelled as below:

COLLECTION: MediaItems

DOCUMENT (each MediaItem document has a Document ID corresponding to the folder ID from Box)

  • uid: string - although each Document has an ID that is accessible through document snapshots, providing this field makes it easier to obtain the ID from the DocumentData object outside of Firestore queries.
  • title: string
  • type: array[shortcode]
  • keywords: array[string]
  • contributor: string
  • school/department: array[string]
  • thumbnail: downloadURL from Cloud storage (if image/interactive) or youtube link (if video/animation)
  • thumbnailType: Static (if image/interactive) or Video (if video/animation) - used to determine whether to embed a video or display an image thumbnail (includes gifs) in the gallery display
  • visitCount: int
  • date: timestamp
  • published: boolean (false by default)

COLLECTION: Albums

DOCUMENT (auto-id)

  • title: string
  • items: array[mediaitem UIDs]
  • thumbnail: image link from Cloud Storage
  • type: string (Course or Group)

Location References

Note: the Web version followed from the documentation is Web version 9 (modular).

import { doc, collection } from "firebase/firestore";

Document References

To create a reference to a document in a collection, use doc():

const docRef = doc(DATABASE_REF, COLLECTION_NAME, DOCUMENT_ID);
// OR specify the path
const docRef = doc(DATABASE_REF, 'COLLECTION_NAME/DOCUMENT_ID');

Collection References

To create a reference to a collection, use collection():

const collectionRef = collection(DATABASE_REF, COLLECTION_NAME);

Writing Data

There are 3 ways to write data to Firestore:

  • using setDoc() by explicitly specifying the document ID and passing in that document reference into the method (most suitable for Media Hub)
await setDoc(DOCUMENT_REFERENCE, {
  // key-value pairs
});
  • Adding a new document to a collection using an automatically generated ID from Firestore - call addDoc()
await addDoc(COLLECTION_REFERENCE, {
  // key-value pairs
}
  • Creating an empty document with an automatically generated ID and assigning data to it later
const docRef = doc(COLLECTION_REF);

// later...
await setDoc(docRef, data);

Reading/Retrieving Data

There are 3 ways to retrieve data from Firestore:

  • Call getDoc() or getDocs() to get the data once (most suitable for Media Hub)

Note: For each doc snapshot, access the document id using doc.id and all its key-value pairs using doc.data().

// Getting a single document
await getDoc(DOCUMENT_REF);

// Getting all documents from a collection
await getDocs(COLLECTION_REF);
// then loop through each doc snapshot
  • Set a listener to receive data-change events
  • Using data bundles

Queries

Use query() along with get(). Mainly combined with the where() method.

  • where(field_to_filter_on, comparison operator, value)

Examples of simple queries:

const q = query(citiesRef, where("regions", "array-contains", "west_coast"));
// Returns all city documents where regions[] contains the value west_coast

const q = query(citiesRef, where('country', 'in', ['USA', 'Japan']));
// Returns all city documents where country is set to USA or Japan
// limit: 10 comparison values

const q = query(citiesRef, where('regions', 'array-contains-any', ['west_coast', 'east_coast']));
// Returns all city documents where regions[] contains west_coast or east_coast
// This is most effective for Media Hub but the method is limited to 10 comparisons on the same field
// Would have to assume that user can only search using 10 keywords and that they will not select more than 10 types/tags/schools

Limitations

  • The search function is implemented by comparing the user’s input with the array of keywords of each media item. In Firestore, this is effectively achieved using array-contains-any but is limited to 10 comparison values. Note that Firestore ‘de-dupes’ results so if a media item has keywords that match more than one of the user’s input, the database only returns 1 record.
    • Assumptions: the user will enter no more than 10 keywords in the search bar (could limit it), the user will select no more than 10 items per section that includes checkboxes
  • Could consider using quotations to group 1 keyword, brackets, etc.
  • Currently, Media Hub does not use the provided limit and pagination queries from Firestore due to its limitation in chaining array compound queries together. A manual implementation is used instead, which requires retrieving all documents that match the keywords and filters and then limiting the display.

Other Useful Methods

  • Increment/Decrement a numeric field (e.g. visitCount)
await updateDoc(DOCUMENT_REF, {
  field_to_increment: increment(num_to_increment)
});

Indexes

Queries that involve ordering items by Recently Added or Most Viewed require an index to be set in the Firebase console. This can be achieved by either:

  • Manually setting composite indices in the console
  • Running each query and going to the link generated in the browser console (recommended by Firebase)
  • Adding all indexes in a JSON file firestore.indexes.json (if Firebase CLI is installed) and deploy the indexes using firebase deploy --only firestore:indexes

The below is an example from Media Hub of setting indexes manually in the Firebase Console.

image-20230210-050031

Clone this wiki locally