Firebase v9 - Get a Referentiated Document Content - javascript

I don't understand why I'm not finding this documentation anywhere.
But I have a collection called users in my Firebase Firestore project. Inside users there are three collections: companies, policies and stores.
In the collection policies I have one store field and one company field that are references to one of that user's store or company.
Ok so as far as now is fine. But now, I'm performing the next query:
const subcollectionSnapshot = await getDocs(collection(db, 'users', 'S3casIyXxdddEAaa1YJL6UjBXLy2', 'policies'));
And the response is the next one:
But now... how can I get the company and store nested documents? How can I "populate" them in order to see their information and access their fields?
Thanks.

It looks like the company and store fields are of type DocumentReference.
In that case you can get a DocumentSnapshot of each of them by calling getDoc() with the DocumentReference:
subcollectionSnapshot.docs.forEach((policyDoc) => {
const companyRef = policyDoc.data()["company"];
const companyDoc = await getDoc(companyRef);
const storeRef = policyDoc.data()["store"];
const storeDoc = await getDoc(storeRef);
...
})
If you have multiple policy documents, you will need to do this for each of them. There is no concept of a server-side join in Firestore (nor in most other NoSQL databases).

Related

How to access array in Firestore v9

Articles I've read/tried before:
Firestore search array contains for multiple values
Performing a complex query with firestore v9
Firestore collection to array of objects with Firebase v9
FirebaseError: Expected type 'Tc', but it was: a custom Ac object,
https://softauthor.com/firebase-firestore-get-document-by-id/
I have a Firestore that looks like this
The current feature I'm trying to implement is a block list. Whenever you go to the person's profile, you click block, and the person doing the blocking's UID is stored as the doc id and the blocked user's id is added to the "blockedUserId" array
The home page displays all the posts and I've been trying to filter the displayed posts to not include posts from any of the users in that "blockedUserId" array
How do I go about doing this correctly?
Here is the attempted code
query(
collection(getFirestore(fireApp), "posts"),
orderBy("uid"),
where(
"uid",
"not-in",
doc(getFirestore(fireApp), "block", "vXLCRjlhOVW6oFOJvtmML6OolKA2")
)
Firestore queries can only filter on values in the document itself, and values you explicitly pass in to the query. Your doc(getFirestore(fireApp), "block", "vXLCRjlhOVW6oFOJvtmML6OolKA2") creates a DocumentReference, so the query returns documents from posts that don't contain that document reference.
What you want to do instead is:
Load the blocked UIDs
Pass them to the query
So in code:
const myRef = doc(getFirestore(fireApp), "block", "vXLCRjlhOVW6oFOJvtmML6OolKA2");
const myDoc = await getDoc(myRef);
const blocked = myDoc.data().blockedUserId;
const q = query(
collection(getFirestore(fireApp), "posts"),
orderBy("uid"),
where(
"uid",
"not-in",
blocked
)
)
// TODO: call getDocs or onSnapshot on q

How to get a subset of object's properties when querying an entire collection in Firebase Firestore? Using JS client sdk v9

I have this Firestore onSnapshot listener on a collection (getting the entire collection). I would like to get only a subset of the the properties of each object.
Something like we do with the firebase-admin using select() on a query:
Ex: admin.firestore().collection('NAME').where(conditions).select('field1', 'field2').get()
This is the onSnapshot code: it works just fine but it's getting the full objects (containing all the properties).
const db = getFirestore();
const col = 'COL_NAME';
const q = query(collection(db, col));
onSnapshot(q, (querySnapshot) => { // How to get only a subset of fields here ?
const results = {};
querySnapshot.forEach((doc) => {
// Do something with each object
});
});
Of course I can map it on the client, but my goal is to keep data network traffic to a minimum.
How can I do it?
With the Client SDKs this is not possible.
As you have mentioned this is possible with the Admin SDK but it is also possible with the Firestore REST API: You can use a DocumentMask when fetching documents, which will "restrict a get operation on a document to a subset of its fields. ".
Note however that fetching via the REST API from a web app is much less simple than using the JS SDK. In particular the format of the response is a complex object that is not funny to parse...
Another approach would be to dernomalize your data: You create another collection which contains documents that only have the fields you want to display in the front end.
The complexity is that you need to keep the two collections in sync: when you create/modify/delete a doc in the master collection, you need to update the other collection. This can be done from your front-end (e.g. you write to the two collections in a batched write) or with a Cloud Function, triggered in the back-end with an onWrite trigger.

Firebase Query, get multiple documents from a single collection where each doc.id is equal to the id in a seperate list

I am building a simple react-native app using firebase, in which users can create posts, see lists of other users posts, and posts can be saved as a "saved posts" as a sub-collection within my users collection.
I am new to react-native and firebase so this is just for educational purposes.
I am getting stuck on querying the posts for my 'saved' list. Currently, when a user clicks the 'save' button, a sub-collection of 'saved posts' is added to (or created) of the post id. I do not want to rewrite the entire post data to the users saved list, only to create a reference to the post via the post id.
I am able to save the list, but have not been able to query firestore for the documents associated with each post id.
Currently I can query based on the users id to retrieve the list of their own posts, as the authorID matches the user uid, and I thought getting the saved posts would be similar.
Here is my code for getting a users own list of posts:
userId is a single string representing user.uid
const getUserPosts = async (userId) => {
const snapshot = await firebase
.firestore()
.collection('posts')
.where('authorID', '==', userId)
.get();
let data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
return data;
};
This function is exported and then wrapped in a try catch statement within the component.
The difference between the above and getting the users list of saved posts is that I have multiple different "postId's" which I need to input (vs a single userID), but the .get() method will not accept an array for the first argument, it requires a single string.
To quickly summarize I need a method or function in which I can take an array of postId's and query my posts collection to return those posts in which the doc.id matches the postId's provided.
I apologize if this is unclear or confusing, I am getting a bit lost on this one.
Any suggestions would be very welcomed.
Thank you so much for your time!
You can do a query for multiple document IDs at once using an "in" query and using FieldPath.documentId():
const array = [...];
const snapshot = await firebase
.firestore()
.collection('posts')
.where(firebase.firestore.FieldPath.documentId(), 'in', array)
.get();
But this only works if the array is of less then 10 items long (Firestore limitation). If you need more, you'll have to either batch the IDs, into smaller arrays and perform multiple queries, or simply iterate the IDs, and get() each document individually (which is just fine, really, don't worry about performance on that).
It's working :)
Big Thank you to Doug Stevenson for your help!!
Here is the function:
postedId is an array of id's referring to the id of saved posts from the 'posts' collection
export const getSaveData = async (postedId) => {
const array = [postedId];
const snapshot = await db.collection('posts').get();
const data = snapshot.docs.map((doc) => ({ postedId: doc.id,
...doc.data() }));
return data;
};

Get Every Document from inside a Users Collection Firebase

I have written a Firebase cloud function in which I want to get every users internal collection called 'numbers' and read each document out of that collection to do some comparisons.
Any idea how to do this?
I am pretty new to firebase and for some reason the database navigation commands are just not sticking with me very well.
I have tried a handful of commands with no success
const snapshot = functions.database.collection('users').collection('numbers').get()
let sfRef = db.collection('users');
sfRef.getCollections().then(collections => {
collections.forEach(collection => {
console.log('Found subcollection with id:', collection.id);
});
});
Here is a loose cloud code infastructure
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
export const prize1 = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
const users = functions.database.ref('/users/numbers')
console.log("")
return null;
});
I feel like I have a good idea of how to do it, but the syntax is holding me back.
The collection of users. Go through each document in here, i.e. each user.
In each user go to the collection called numbers.
In the collection called numbers go through each document and find the numbers field to do logic/comparisons with.
Hopefully this can help you understand the way my database is ordered.
You could try it like this:
let usersRef = db.collection('users');
let allUsers = usersRef.get();
.then(userSnapshot => {
userSnapshot.forEach(userDoc => {
userDoc.ref.collection('numbers').get().then(numSnapshot => {
numSnapshot.forEach(numDoc => {
console.log(numDoc.data().numbers);
// here you got your numbers document with the numbers field
});
});
});
})
.catch((error) => {
console.log("Error getting document: ", error);
});
For more information you can look here and here.
You can't use functions for accessing the database. What you've defined as functions is for building triggers that respond to events. If you want to get data from Cloud Firestore, you should be using the Firebase Admin SDK via your admin instead. It might also help if you look through the official samples.
I will also point out that your code samples appear to be split between accessing Cloud Firestore and Realtime Database, which are different database products. Your screenshot shows Firestore, so ignore any APIs for Realtime Database.

firebase simple - data structure questions

I have been reading a little about how to structure your firebase database and i understand that you need to split your data into pieces so you don't forced the client to download all of the 'users' data.
So in this example you will get all the users data when you write
ref.('users').once('value', function(snap)...
/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets
but what if you specifically write the path to the location instead like
ref.('users/uid/email').once('value', function(snap)...
Will you still get all the users data or only the data in email ?
In firebase, you set the ref to be the reference for your database (the whole database) and then you got methods to iterate through each piece of data of your database object, hence, a good practice is to set all your database as the ref and then work from there to go through what you want to go.
// will select the whole db
const firebaseRef = firebase.database().ref();
// will select the whole app object of your db
const firebaseRef = firebase.database().ref().child('app');
// will select the whole users object of your db
const firebaseRef = firebase.database().ref().child('app/users');
So, it is a good practice to set a variable like firebaseRef to be your whole firebase db and then iterate from there.
Now, if you write:
firebaseRef.child(`users/${uid}/email`).once('value').then(snapshot => {
console.log('User email: ', snapshot.val());
}, e => {
console.log('Unable to fetch value');
});
Yes, you will get what you're asking, but you need to use the child method to get to the objects of your firebase ref.

Categories