maja
maja

Reputation: 799

Efficiently read Firestore's document reference field contents

I'm trying out Firestore with this data model:

I have data entry for 2 pokemon one of which has the move with name "growl" while the other has no move field.

I would like to read the pokemon collection from a Next client and build an array with two items of the shape { name: string, move?: { name: string } }

My initial code looks something like this:

import firebase from "./{configfilepath}"

...

const db = getFirestore(firebase)
const pokemonRef = collection(db, "pokemon")
const pokemonQuery = query(pokemonRef)
const pokemonSnap = await getDocs(pokemonQuery)

...

const pokemon = []
pokemonSnap.forEach(async (doc) => {
  const docData = doc.data()
  const moveRef = docData.move
  const moveSnap = moveRef ? await getDoc(moveRef) : undefined
  const moveData = moveSnap ? moveSnap.data() : undefined

  pokemon.push({
    id: doc.id,
    name: docData.name,
    move: moveData,
  })
})

When I execute this code I obtain an array with only one entry, which is the one the skips the call to the awaited calls.

So the issue is that the .forEach method of Firestore's QuerySnapshot is not awaiting for the promises to resolve, but I'm not seeing another built-in method that can deal with async operations.

So I moved to something that handles the promises outside of the Firestore's forEach:

...

const pokemonRaw = []

pokemonSnap.forEach((doc) => {
  pokemonRaw.push({
    id: doc.id,
    data: doc.data()
  })
})

const pokemon = await Promise.all(pokemonRaw.map(async (rawData) => {
  const moveRef = rawData.data.move
  const moveSnap = moveRef ? await getDoc(moveRef) : undefined
  const moveData = moveSnap ? moveSnap.data() : undefined

  return {
    id: rawData.id,
    name: rawData.data.name,
    move: moveData,
  }
}))

Honestly, this seems a bit too complex for relatively simple read operation involving referenced fields, so my question is: is there a more succinct and idiomatic way to access referenced fields when reading Firestore records from a client and would I be better off forgetting about referenced fields when modeling data with Firestore?

Upvotes: 0

Views: 49

Answers (1)

maja
maja

Reputation: 799

Since part the original question was a bit vague and requested an opinionated answer, the only really correct answer is that "it depends".

Still, I think it's worth mentioning that with Firestore it's common enough to avoid the issue of handling layers of relations when rendering a simple view of available fields by providing a human-readable string instead of a relational field.

For the move with name "growl", I can store it in a pokemon record with the string with value "growl", which is what I want the user to see in the details of a pokemon.

Then if the user is interested in knowing more details about the move I can fire a different query for moves that match the value "growl" in their name field.

Alternatively, you could also choose to use the value "growl" as the name of the record itself which would remove the need to read the value of any contained field, with the (possibly useful) constraint of having a unique record.

While this constrains a bit what you can fetch with a single query for your pokemon, I think it gives enough information for the user to receive a useful and interactable ui.

Upvotes: 1

Related Questions