Firebase Miniseries 2: Firestore Part 2
In this second part of the 2 part article on Firestore, we will be looking at sub collections, updating and deleting documents and how to perform advanced get queries to query our firestore data. We will end with some useful techniques like adding to an array and incrementing a value.
Sub Collections
Sub collections in firestore are very useful when a collection has more properties usually such as a long list of arrays.
Let's take for example a used car shop database. In our shop we have different cars, each have different models and each of these models have different colors. So our root/parent collection would be called "cars" and it would have a sub collection called "models" and this models sub collection would have many documents each with different colors. Makes sense right? So let's see how we can get and set to and from the cars and models sub collection.
Let's start by creating a black, S7 model Audi.
import { db } from '/firebase/db/path'
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('black')
.set({
vin: '12345',
manufactureDate: new Date(),
price: 110_000
})
That's going to create the car we need in our database. Later it will be easy to retrieve. We can make a get call in very much the same way to get the car details.
import { db } from '/firebase/db/path'
const [car, setCar] = useState(null)
useEffect(() => {
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('')
.get()
.then(snap => {
setCar(snap.data())
})
},[])
return (
<h4>Price: {car.price}</h4> //display car attributes
)
Creating & Updating docs
To create or edit a document, can be done in a very similar method.
Using the set/add or update methods like so:
db.
collection('cars')
.doc('audi')
.collection('s7')
.add({ //add aitomatically assigns a random doc id
name: 'Audi S7',
vin: '12345'
})
with set:
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('abc') //manually assign a doc id
.set({
name: 'Audi S7',
vin: '12345',
carID: 'abc'
})
The difference between set() and add() is that with add we don't need to worry about assigning a document id, firebase does that for us. With set() we can manually assign our own doc id and then have more control over it by inserting it inside the document itself for later use.
Deleting docs
Deleting documents is the quickest and easiest:
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('abc')
.delete()
*note that every set, update, and delete method, returns a promise. Therefore we can add a .then() and .catch() after these methods to know when they successfully executed the code (then()) or failed to execute (catch())
Advanced queries
The main advanced queries we will look at are the where(), orderBy() and limit().
Say we want to query for a car that is inside our audi collection, that is a model S8 and who's condition is strictly new. To do this we can use the where() clause like so:
db.
collection('cars')
.doc('audi')
.collection('s8')
.where('condition', '==', 'new')
.get()...
The above code will query our database for car documents in the audi s88 collection that match the criteria of the property 'condition' which is equal to 'new'. So all docs whose property 'condition' is equal to 'new' will be retrieved.
The next method deals with ordering the data. Say after we run our where clause, we wish to order the docs by condition (firebase only lets us order docs by the the property that is inside the where() clause) in descending order, we can do:
db.
collection('cars')
.doc('audi')
.collection('s8')
.where('condition', '==', 'new')
.orderBy('condition', 'desc') //or 'asc' in ascending order
.get()...
Finally limit allows use to limit the results fetched back from the database, so we don't overload the client side with too much data. We simply call limit() and insert a number like so:
db.
collection('cars')
.doc('audi')
.collection('s8')
.where('dateAdded', '<', new Date())
.orderBy('dateAdded', 'desc')
.limit(10) //fetch only the first 10 'new' cars by date added descending order
.get()...
It is worthy to note here that order or clauses **matters. Logically if we were to insert the limit() before the orderBy(), we would be limiting the results to the first 10 random docs, **and then* order these results by date added descending order. But that is not what we wish to achieve. We want to order the 10 newest cars in the entire database and limit the results to the most 10 latest added cars. That is a completely different result. So gotta be careful there!
Array methods
Array methods are super handy when adding or removing elements inside arrays in our firestore.
Say we want to add a color to our car color selections we can do so like this:
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('abc')
.update({
availableColors: firebase.firestore.FieldValue.arrayUnion('red') //this will add the color red to the availableColors array
})
To remove an element:
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('abc')
.update({
availableColors: firebase.firestore.FieldValue.arrayRemove('red') //this will remove the color red from the availableColors array
})
Incrementing/Decrementing number values
Often we want to increment or decrement from a number value on firestore. This is a simple one liner:
To increment:
db.
collection('cars')
.doc('audi')
.collection('s7')
.doc('abc')
.update({
views: firebase.firestore.FieldValue.increment(1)
})
To decrement, we simply add a negative to the number
firebase.firestore.FieldValue.increment(-1)
Firebase doesn't have a decrement method, instead we simply negate the number we wish to decrement by.
Firebase timestamp
Finally we also have a nifty little method to get a firebase timestamp from a regular javascript date. This is especially useful to add dates or convert dates from javascript to firebase formatted dates. Since dates that are stored in our firebase are not stored as JS dates, but instead firebase timestamps.
firebase.firestore.Timestamp.fromDate(date) //the parameter date here is a javascript date and converted to a firebase date.
To convert from firebase timestamp to a JS date we can use toDate() method:
firebaseDate.toDate() //JS date object
Conclusion
We have covered the main topics of the advanced firestore APIs.
We looked at get queries, advanced query filtering, creating and editing and deleting documents. And we finished off with array methods, incrementing numbers and date formats.