Collection
Collection Reference
use the collection reference
We use a collection reference when an individual document reference is not needed or not yet available:
-
fetch all documents (it acts as a query):
getDocs(colRef) -
build a query (targeting the collection):
query(colRef, filters..) -
build a random-ID document reference:
doc(colRef), or one that refers to a specific document:doc(colRef, docId) -
add a random-ID document to the collection in a single step:
addDoc(colRef, data).
build a collection reference
We use the path that identifies the collection uniquely. Root collections have the simplest path: the collection name, such as "users" (no starting slash). Sub-collections' paths are built from several components.
We set the path as:
-
a string, with slash separators.
-
a sequence of strings, with no slash separators.
const colRef = collection(db, "users")
const colRef = collection(db, `users/${uid}/custom_list`)
const colRef = collection(db, "users", uid, "custom_list")
const colRef = db.collection(`users/${uid}/custom_list`) // admin SDK
TypeScript: a collection's documents as DocumentData
Collections are schema-less: they don't define the shape of their documents.
When receiving data from the database, the client SDK instantiates documents with no regard to the content: documents are of any shape and may differ from one another. It types them as DocumentData, which doesn't provide information about the content.
TypeScript: annotate the document's type at the collection level.
We provide a more precise type at the collection reference level. The simplest way to do it is through a type assertion:
const colRef = collection(db, "players") as CollectionReference<Player, Player>
Converter (optional)
the case for a converter
The SDK supports working with two document shapes on the client:
CollectionReference<AppModel, DbModel>
DbModel represents the object that the SDK instantiates from the raw data. It is DocumentData by default.
We can add a converter to transform it to a different shape for use in the app.
AppModel represents the object after the converter's transformation. It also defaults to DocumentData. We set it to whatever type the converter converts to.
Before sending to Firestore, the converter transforms back AppModel to DbModel.
Transformation examples:
- We transform the DbModel's Timestamp field to an AppModel Date field.
- We add properties to AppModel.
implement the converter
We transform the documents at the app boundaries:
- upon receiving from Firestore (
fromFirestore()) - upon sending to Firestore (
toFirestore())
We define the functions and add them to the converter.
fromFirestore() takes the snapshot as instantiated:
fromFirestore(snapshot: QueryDocumentSnapshot<FirestoreWorkout>): Workout{
// to client shape
// FirestoreWorkout -> Workout
const firestoreItem = snapshot.data()
const workout = { ...firestoreItem, date: firestoreItem.date.toDate()}
return workout
}
toFirestore() takes the object in its AppModel shape.
toFirestore(workout: Workout) {
// to database shape
// Workout -> FirestoreWorkout
return { ...workout, date: Timestamp.fromDate(workout.date)}
}
We gather the transforms in the converter (FirestoreDataConverter). While the type are inferred from the transforms, we can still add them at the converter level:
// FirestoreDataConverter<AppModel, DbModel>
const myConverter: FirestoreDataConverter<Workout, FirestoreWorkout> = {
toFirestore() {},
fromFirestore() {},
}
We attach it to the collection reference to let it type its documents.
const colRef = collection(db, "players").withConverter(conv)