MarcShayne
MarcShayne

Reputation: 87

How to get parent document of firestore collectionGroup query?

I'm trying to get the parent document of all subcollection queries I get so my database looks something like this

/production/id/position/id/positionhistory

I got all the documents of position history but I also need some data from position and production. I was hoping if there is a way to get the documents of the parents in a collectionGroup query. I'm also using firestore v9.

const getHistory = async () => {
  setLoading(true);
  try {
    const userHisRef = query(
      collectionGroup(db, "positionhistory"),
      where("userid", "==", currentUser.uid)
    );
    const querySnapshot = await getDocs(userHisRef);
    let arr = [];
    querySnapshot.forEach((doc) => {
      console.log(doc.id);
      arr.push(doc.id);
    });

    setLoading(false);
  } catch (err) {
    console.log(err);
    setLoading(false);
    
  }
};
getHistory();

Upvotes: 6

Views: 5328

Answers (2)

Renaud Tarnec
Renaud Tarnec

Reputation: 83048

As indicated by Pierre Janineh you need to use the parent properties of the DocumentReference and CollectionReference classes.

Concretely, for each QueryDocumentSnapshot (which "offers the same API surface as a DocumentSnapshot") in the QuerySnapshot you can do:

const querySnapshot = await getDocs(userHisRef);
let arr = [];
querySnapshot.forEach((doc) => {

  const docRef = doc.ref;   
  const parentCollectionRef = docRef.parent;   // CollectionReference
  const immediateParentDocumentRef = parentCollectionRef.parent; // DocumentReference
  const grandParentDocumentRef = immediateParentDocumentRef.parent.parent; // DocumentReference
  // ...
});

So you can easily get the DocumentReferences (and the ids) of the parent and grandparent docs.

However, you want to get some of the data of these parent/grandparent documents ("I also need some data from position and production") and this is more complicated... because you actually need to query these documents based on their DocumentReferences.

For that you could use Promise.all() with one or more arrays of promises that you build in the loop (as rapidly shown below) but, depending on how much data from the parents you need, you could also denormalize your data and add to the children the desired data from their parents and grandparents docs.

To get the data of all the parent and grandparent docs, you can do as follows:

const querySnapshot = await getDocs(userHisRef);
let arr = [];

const parentsPromises = [];
const grandparentsPromises = [];

querySnapshot.forEach((doc) => {
  const docRef = doc.ref;   
  const parentCollectionRef = docRef.parent;   // CollectionReference
  const immediateParentDocumentRef = parentCollectionRef.parent; // DocumentReference
  const grandParentDocumentRef = immediateParentDocumentRef.parent.parent; // DocumentReference
  
  parentsPromises.push(getDoc(immediateParentDocumentRef));
  grandparentsPromises.push(getDoc(grandParentDocumentRef));
  // ...
});

const arrayOfParentsDocumentSnapshots = await Promise.all(parentsPromises);
const arrayOfGrandparentsDocumentSnapshots = await Promise.all(grandParentDocumentRef);

You will get two arrays of DocumentSnapshots from which you can get the data. But you most probably need to link each of them with its corresponding child/grandchild doc...

Since, with Promise.all(), the returned values will be in order of the Promises passed, you can use the index of the initial array (i.e. the order in which you loop over the querySnapshot with forEach) but it is a bit cumbersome...

In addition, note that if you have several docs in one of the positionhistory subcollections, you will fecth several time the same parent and grandparent docs. You could maintain a list of the doc IDs that were already fetched but, again, this adds some complexity.

So, for all these reasons, it is probably good to analyse if it's not easier/better to denormalize the data, as explained above.

Upvotes: 14

Pierre Janineh
Pierre Janineh

Reputation: 702

You can use the QuerySnapshot. It points to a number of QueryDocumentSnapshot instances.

const parent = querySnapshot.ref.parent;

Check out the Firebase Documentation

Upvotes: 2

Related Questions