Show values from a related different collection with Firestore

I hope some of you can help me with this. I have a users collection with their own uid where they save some values related with my main collection, which is named “preloads”. Specifically they usually save the preloadID (id that identifies the document from the main collection) and the score they have obtained for that preloadID.

I have created a page with a list where they can see the scores they have obtained for each preload, but as it is listing their own collection, it lacks of many other values that are in the main collection (for example the image, link, etc.)

I would like to create a component that would show not just the score and name of the preload, but it would also fetch the data for that preloadID from the main collection. I don’t want to duplicate the data because they would have access to read that data according to my security rules, so the data has to remain as it is and the solution would be to create a component that would list their collection and would fetch the additional data from the main preload collection.

I hope some of you can guide me about this. Thank you as always.

This would be against the basic principles of noSQL database design but maybe you could create a cloud function or a custom script into screen script template which makes two queries and combines the result from those two queries into one data sheet.

Maybe @Marcos_Alencar could help you on building the script?

I know the usual way to do it would be to duplicate all data. That could be one way to solve it. What I am not convinced about that solution is that in the long run I know it wouldn’t be what I want because I rather have the users collection with the data that I am already saving.

I am aware the solution is through a script in the Screen script section. I believe I almost know how it would be but I struggle writing scripts so they just work. I have improve a bit my knowledge and now I understand some scripts but it is difficult for me to create them. I know I have to create a script that create an array fetching the data from the main collection and it has also to query the users collection. I would be able to create the variables and references, but I need some additional help to make the script properly so the values that I need are there to fill the data in the component.

Let’s see if @Marcos_Alencar can guide though this.

Thank you!

Ok. I have been working on this and I achieved to do it with some workaround according to React Studio logic. The idea is to create a list/grid with the data from the user collection (main collection in this list) and inside that component, create another list based on an array taking the additional data from the root “preloads” collection. It is working but I have a small inconvenience. The code that I am using in the Script editor for the scoreCard component is:

import React, { Component } from 'react';
import './App.css';
{{IMPORTS}}
import firebase from 'firebase';

export default class {{CLASS_NAME}} extends Component {

{{PROPERTIES}}

  constructor(props) {
    super(props);
    
    this.state = {
{{INITIAL_STATE_PROPS}}
    };
  }
  
_updateItems = (docKey) => {
  const db = firebase.firestore();
  let preloadID=this.props.dataSheetRow.document_key;
  const ref = db.collection('preloads')
  .where('preloadID', '==', preloadID)
  this._cancelSnapshot = ref.onSnapshot(
      (querySnapshot) => {
        let jsonArr = [];
        let key = 0;
        querySnapshot.forEach((doc) => {
          const data = { ...doc.data(), key: key++, document_key: doc.id, document_ref: doc.ref };
          jsonArr.push(data);
        });
        this.setState({"scoreCardComp": jsonArr, _itemsDocKey: docKey});
      }
    );
  }

  componentDidMount() {
{{COMPONENT_DID_MOUNT_CODE}}
	this._updateItems(this.props.document_key);
  }

  componentWillUnmount() {
{{COMPONENT_WILL_UNMOUNT_CODE}}
    this._cancelSnapshot();
  }

  componentDidUpdate() {
{{COMPONENT_DID_UPDATE_CODE}}
  let itemsDocKey = this.props.document_key;
  if (this.state._itemsDocKey && this.state._itemsDocKey !== itemsDocKey) {
  this._cancelSnapshot();
  this._updateItems(itemsDocKey);
  }
}

{{METHODS}}
}

It works just fine. But my problem is here:

let preloadID=this.props.dataSheetRow.document_key;
const ref = db.collection(‘preloads’)
.where(‘preloadID’, ‘==’, preloadID)

The collection “preloads” has already thousands of documents where there is no field named “preloadID” because that value is the unique document_key. I use that value to write a field in the users collection to link the document to the document. So how I have it organized is:

/preloads
   /document_key1
       name: Lorem1
       image: Ipsum1
   /document_key2
       name: Lorem2
       image: Ipsum2

And then in the users collection:

/userID
    /scores
          /document_key1
             preloadID: document_key1
             score: 23
         /document_key2
             preloadID: document_key2
             score: 97

If I create a field in the preloads collection specifying the preloadID it just works. But I don’t want to go over all of my documents adding that value. How would I change the reference to ref the document_key?

Cant you simply fetch the object by referencing to preload document instead of making a query for the whole collection? I don’t know if this works but just you to get an idea to digg into.

I mean something like :

const ref = db.collection(‘preloads/<your_document_key_here>’)

Interesting. But how do I add the variable in the document_key? Because if I just add the name of the variable ‘preloadID’ it is going to treat it as the document_key itself.

Hey Pedro. Maybe you can get the values you want from preloadID and save them in the data slot later use. The preloadID list would be placed in some hidden component, where you can do this and filled with the data you need. Does it make sense? Using cloud functions would require some brain and firebase costs. In my project, I am avoiding as much as I can, keeping only the essential and not expensive ones. hoping this helps. Take care. :syringe:

Hey @Marcos_Alencar . Nice emoji the :syringe: I am getting the vaccine tomorrow!

I am also avoiding Google Cloud Functions so far because I am still holding to the Blaze Plan. I feel much more confident now and I have all my queries limited so my numbers are pretty good. I am thinking about updating to the Spark Plan in order to use Algolia but I am waiting for that step later.

As for your suggestion to save the preloadID in a slot, I understand but I don’t see how it would help in this particular case. The thing that I am trying to do is to fetch several documents with different document_key from the main collection related with the list that is being shown in the screen. Something like this:

In that image there is a collection that is listing documents and data from the users collection. The image link is not there. That’s why it is not being shown in some of the documents. The document_key is the text in bold in the top of each document and it is in the document_key and also in a field named preload_ID. When I create a field in the main collection with the preloadID = document_key, then the image is shown as it is expected, but it would be great if I can avoid to write this field for each document now.

@Antti_Neonto suggested to point directly to the document but I don’t know how to write the variable in the reference. Something similar to the $slot() that we do in RS but in the Javascript editor.

Thank you and take care!

Ok. I reviewed some docs and info and I discovered how to include the variable in the reference, but it does not work as it is. I believe the problem is that I am using a script that tries to fetch an array and the reference points to a single document. I guess this new approach would need to do things different because when I just place in the code:

_updateItems = (docKey) => {
  const db = firebase.firestore();
  let preloadID=this.props.dataSheetRow.document_key;
  const ref = db.collection('preloads').doc(preloadID);
  this._cancelSnapshot = ref.onSnapshot(
      (querySnapshot) => {
        let jsonArr = [];
        let key = 0;
        querySnapshot.forEach((doc) => {
          const data = { ...doc.data(), key: key++, document_key: doc.id, document_ref: doc.ref };
          jsonArr.push(data);
        });
        this.setState({"scoreCardComp": jsonArr, _itemsDocKey: docKey});
      }
    );
  }

After compilation and entering the screen that contains this list I get the error:

In the meantime I have placed in the component for the documents an icon that would save the additional value “preloadID” with the same document_key and that would sort this issue but I wonder:

  • Am I doing it wrong this way and increasing unnecessary the readings fetching documents this way (with the preloadID field way)?
  • When I click in this “Save interaction”, it automatically saves a “document_path” and a “key” field that I haven’t stablished. I am not worried about the document_path, but the key value will cause problems because lists in React Studio has prove issues with documents that have duplicated keys. How can I avoid RS to create this “key” value?

You may want to take a look @ Typesense, if still thinking about the full-text search in Firestore. As they say " Lightning-fast,
Open Source Search.
No PhD required."

Hey! That sounds really interesting! Did you implement it in your project? Even though No PhD requited, I wonder if I will be able to integrate it myself :upside_down_face:

Not yeat but I’ll try. I’m so bananas right now fixing the mess caused by losing the language keys.

I hope you can solve it soon enough! I have two goals to accomplish now in my project: to implement the search with your suggestion and to better integrate the webapp within my project. I asked about the way to do it and I believe I have to dig into React Router and the Navigation paramemters but I didn’t have time yet.

1 Like