How to access array in Firestore v9 - javascript

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

Related

How to get data from first collection in Firestore

I am configuring my Vue/Firebase chat app to save the email address of a registered user in a Firestore collection when the registered user is logged in and submits a message. The Firestore Database looks like the following, with the users added to the "user" collection, and with each user containing a "message" collection:
In my code, I am attempting to add a function that returns the email addresses listed in the "users" collection, as seen in the screenshot:
const contactList = ref([])
const getContacts = () => {
firebase.firestore().collection("users").onSnapshot(snap => {
contactList.value = []
snap.forEach(doc => {
const users = doc.data();
users.id = doc.id;
contactList.value.push(users)
console.log(users)
})
})
},
However, this function does not simply return those email addresses in the user collection, since .collection('users') is the first collection in a chain of docs and collections as indicated in the screenshot. How can I go about returning the list of users in the "users" collection when .collection('users') is the first in a chain of docs and collections?
The document titles are shown in italic, meaning that there are actually no documents with that ID (also shown in the bottom) right and the console merely shows them in order to be able to show the subcollections.
The API won't return such non-existing documents. You'll have to create the document to be able to then read them, for example by running a collection group query over all messages collections and checking if their parent document exists already.
Also see:
Why are non auto-generated document Ids are in italics in Firestore console?
Firestore DB - documents shown in italics
Setting document via subpaths in firebase causes them to show up as italic?
Firestore document/subcollection is not existing

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 v9 - Get a Referentiated Document Content

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

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 Firestore document where value not in array

I know firestore doesn't allow inequality statements in .where() queries and I should instead chain > and < queries, but I don't know how this will work in my case.
In my react native app, I want to select some users who have not been already added by the user. After getting an array of all the users the current user has already added as so:
var doc = await firebase
.firestore()
.collection(`users/${currentUser.uid}/user_data`)
.doc("friends")
.get();
var friends = doc.data()
I then want to choose some users who have not been added by the current user as so:
var docs = await firebase
.firestore()
.collection("users")
.limit(10)
.where("username", "not in", friends)
.get();
How would I do this? Thanks
This kind of query is not possible with Firestore. Firestore can only query for things that exists in the indexes that it creates for each field. These indexes can't be queried efficiently for things that don't exist.
See also: Firestore: how to perform a query with inequality / not equals

Categories