Nuxtstop

For all things nuxt.js

Firebase Miniseries 2: Firestore Part 2

Firebase Miniseries 2: Firestore Part 2
9 0

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
})
Enter fullscreen mode Exit fullscreen mode

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
)

Enter fullscreen mode Exit fullscreen mode

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'
  })
Enter fullscreen mode Exit fullscreen mode

with set:

db.
  collection('cars')
  .doc('audi')
  .collection('s7')
  .doc('abc') //manually assign a doc id
  .set({ 
    name: 'Audi S7',
    vin: '12345',
    carID: 'abc'
  })
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

*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()...
Enter fullscreen mode Exit fullscreen mode

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()...
Enter fullscreen mode Exit fullscreen mode

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()...
Enter fullscreen mode Exit fullscreen mode

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
  })
Enter fullscreen mode Exit fullscreen mode

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
  })
Enter fullscreen mode Exit fullscreen mode

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)
  })
Enter fullscreen mode Exit fullscreen mode

To decrement, we simply add a negative to the number

firebase.firestore.FieldValue.increment(-1)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

To convert from firebase timestamp to a JS date we can use toDate() method:

firebaseDate.toDate() //JS date object
Enter fullscreen mode Exit fullscreen mode

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.