What is the best way to access the parent document of a Firestore collectionGroup query?

I'm currently working on accessing the parent documents of all subcollection queries in my database structure. The setup I am aiming for is something like this:

/production/id/position/id/positionhistory

While I have successfully retrieved all the documents from the positionhistory collection, I also require data from the position and production collections. Is there a way to access the parent documents using a collectionGroup query? Additionally, I am utilizing 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();

Answer №1

Instructed by Pierre Janineh, it is recommended to utilize the parent properties within the DocumentReference and CollectionReference classes.

Specifically, for each QueryDocumentSnapshot in the QuerySnapshot, you can perform the following:

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
  // ...
});

This allows easy access to the DocumentReferences along with their corresponding ids of the parent and grandparent documents.

If you require specific data from these parent/grandparent documents such as "position" and "production," a more intricate process is involved... as querying these documents based on their DocumentReferences becomes necessary.

To accomplish this, one approach involves using Promise.all() with arrays of promises created within the loop, as depicted:

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);

This results in two arrays of DocumentSnapshots containing the desired data. However, establishing connections with the respective child/grandchild documents may be necessary...

Since the values returned by Promise.all() are in the order of the Promises passed, utilizing the index of the initial array may assist in linking them with the correct child/grandchild documents, although it may be complex...

Furthermore, if multiple documents exist within a subcollection like "positionhistory," redundant fetching of the same parent and grandparent documents may occur. Managing a list of fetched document IDs can help mitigate this issue but adds another layer of complexity.

Therefore, considering all factors mentioned above, evaluating the feasibility of denormalizing the data could potentially offer a simpler and more efficient solution.

Answer №2

To access multiple QueryDocumentSnapshot instances, you can utilize the QuerySnapshot.

const parent = querySnapshot.ref.parent;

To learn more, refer to the official Firebase Documentation

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Notifications for AngularJS tabs

I need help finding a method to incorporate "tab notification" using AngularJS, in order to show that there are important alerts that require attention. For example: (1) (3) TAB_ONE TAB_TWO TAB_THREE Could you provide any recom ...

Stop the iframe video when the modal is closed

I'm currently developing a website that incorporates the code showcased in this tutorial to launch a modal window featuring an iframe for playing YouTube or Vimeo videos. The issue arises when, as mentioned in the comments on the tutorial page, there ...

Is it acceptable to include the bundled main.js file in the gitignore for a HUGO project?

Is it possible to exclude the bundled main.js file from a HUGO project by adding it to .gitignore? ...

Is there any specific value that will always result in a true comparison in JavaScript?

Is there a special JavaScript value that will always make a comparison true? For example using the less than operator: true < 10 true false < 10 true null < 10 true Or using the greater than operator: true > 10 ...

Is the entire web page generated on the server with each request using getServerSideProps()?

As I understand it, getServerSideProps will dynamically generate the complete page on every request, rather than pre-building it. But does getServerSideProps always generate the entire page upon each server request, or only when the data in the database ha ...

What is the best way to show my button only when within a specific component?

Is there a way to display the Logout button on the same line as the title only when the user has reached the Home component? In simpler terms, I don't want the logout button to be visible all the time, especially when the user is at the login screen. ...

Angular utilizing external parameter for Ajax requests

As a newcomer to Angular, I am eager to upgrade some old jQuery code with AngularJS. The task at hand is to extract a string from a span element, split it into two separate strings, and then use these as parameters in a GET request. I am dedicated to lea ...

Error code 405 (METHOD NOT ALLOWED) is received when attempting to make a post request to an API

Struggling to develop a basic front end that can communicate with my API. The API functions properly as I am able to retrieve and send data using the POSTMAN client. Fetching data from the server and displaying it on the frontend works fine, but encounteri ...

Challenges with handling form elements in JavaScript

I'm currently developing a project and facing challenges in implementing input validation for a form. My goal is to ensure that the form only gets submitted and redirects to a specific URL if all requirements are met. To achieve this, I am utilizing J ...

Toggle button visibility in AngularJS based on checkbox selection

I'm currently utilizing ng-table to construct my table. I have a button positioned at the top of the table that is initially disabled. My goal is to enable this button only when any of the checkboxes are selected. The button should automatically disab ...

Monitoring and recording every server-side interaction within the AngularJS user interface

Is there a way to efficiently display all server side REST actions on the angular js UI of my application? For example, how can I immediately show a message on the GUI when a user is created or when an action fails? I currently store all such actions in a ...

What steps can be taken to extend the duration that the HTML necessary validation message is displayed?

Is it possible to extend the duration in which the HTML required validation message is displayed? Currently, it seems to appear for too short a time. ...

Building a Next.js application that supports both Javascript and Typescript

I currently have a Next.js app that is written in Javascript, but I am looking to transition to writing new code in Typescript. To add Typescript to my project, I tried creating a tsconfig.json file at the project root and then ran npm install --save-dev ...

Utilizing string interpolation within the parameters of an AJAX call

I have a locale variable that can hold values such as en, fr, or es. The goal is to include this locale key in an AJAX request as part of the data parameter. let locale = "en"; let name = "example"; $.ajax({ url: "/path", method: "PUT", data: {chara ...

Misplace reference to object during method execution

Here's a simple demonstration of the issue I'm facing. The count function is supposed to keep track of the number of items returned from a query. However, my current implementation causes me to lose reference to the function when calling it from ...

Unique ActionBar design for my NativeScript-Vue application

I'm currently working on customizing the ActionBar for my nativescript-vue app. I have implemented FlexBoxLayout within the ActionBar, but I am facing an issue where the icon and title of the bar are not aligning to the left as intended; instead, they ...

Issue encountered: "require" is not recognized when attempting to access my local JSON file in Vue.js

I am venturing into the world of vuejs... I attempted to retrieve data from my JSON file stored locally, but the decision on which specific JSON file's data to fetch is dynamic. I keep encountering an error stating 'require' is not define ...

Configuring Google Maps API (including charts) for maximum height of 100%

I am having trouble getting my map to display at 100% height using the Google Maps API. I've encountered similar issues in the past with the Google Charts API as well. From what I've gathered, it seems like setting the height of the html and bod ...

Dealing with callback errors: error handling is anticipated

In my Vue web application, I have enabled ESLint and encountered an issue with the following code: myApi.get('products/12').then((prodResponse) => { state.commit('ADD_PRODUCT', {product: prodResponse.data}) }, error => { cons ...

JavaScript Bingo Game - Create Interactive Cell Selection

Below is the HTML code that I am using to create a Bingo card: ... <th class="orange">B</th> <th class="orange">I</th> <th class="orange">N</th> ...