Get Firestore document where value not in array - javascript

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

Related

Firestore Database: How to search for an email in multiple documents

I need to find the ID of a document, and I must do it by comparing the email that each document contains.
This is my database:
For example, I know the email "mipaciente2#gmail.com", and I don't know what document that email is in, what query do I have to do to search each document until I find the one that contains that email?
You just need to have a subcollection reference which is the patients and then create a query using the where() method to find the email field with a specific value. Though, I'm not sure what version you are using. I'll just give the modular and the namespaced versions. See sample snippets below:
Web Version 9 (modular):
import { collection, query, where } from "firebase/firestore";
// For `user's-document-id`, I guess you have it.
const patientsRef = collection(db, "users", "<user's-document-id>");
// Create a query against the collection.
const q = query(patientsRef, where("email", "==", "mipaciente2#gmail.com"));
Web Version 8 (namespaced):
var patientsRef = db.collection("users").doc("<user's-document-id>").collection("patients);
// Create a query against the collection.
var query = patientsRef.where("email", "==", "mipaciente2#gmail.com");
For more information, you may want to check out this documentation.

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 can i get sub-collections of sub-collections in Firebase in one call

I have a firebase schema that look like this:
posts > posts(documents) > comments > comments(collections) > replies > replies(documents)
const allComments = await firebase
.collection('posts')
.doc(params.slug as string)
.collection('comments')
.orderBy("date", "desc")
.get()
if i tried to get the reply like this:
const allComments = await firebase
.collection('posts')
.doc(params.slug as string)
.collection('comments')
.doc("some_random_id")
.collection('reply')
.get()
i will get only the reply, which is not the desired output.
i need to get each reply of each comment in one call.
so the returned object would be every comment, and each comment contains an object of replies.
Firestore queries are "shallow", and only return documents immediately within the collection being queried. They don't consider any documents from subcollections. To get documents from subcollections, you would need to make another query for each subcollection.
It might be better to simply put all the comments and replies in a single collection, mark them each with a boolean indicating if it's a reply or not, and use that as a filter if needed on the query. The size of the collection will not have any impact on performance.

Firestore rules: fetch message thread by threadId in secure way

Assumptions
I'm using Firestore and I can't set the correct rule for it.
Suppose there's a chat app.
And there are threads and messages collections.
threads attributes
createdAt
userAId
userBId
messages attributes
threadId
senderId
receiverId
I set the security rule as followed since I don't want my users to see messages of other users(omitted the irrelevant part):
match /messages/{messageId} {
allow read: if isAuthenticated() &&
(request.auth.uid == resource.data.senderId || request.auth.uid == resource.data.receiverId);
}
Suppose we have only one thread and only one message of that thread saved in firestore.
Problem
When I execute the following query from my web app with authenticated user, it says insufficient permission of firestore even though the query is requesting the thread where the user is sender or receiver as required in rules.
const querySnapshot = await firebase.firestore()
.collection('messages')
.where('threadId', '==', someThreadId)
.get()
However, when I execute the following query that fetchs the message by documentId, it returns the message successfully.
const documentQuerySnapshot = await firebase.firestore()
.collection('messages')
.doc(specificMessageId)
.get()
Workaround
I'm forced to write 2 queries and I don't want since it's not effective.
const qs1 = await firebase.firestore()
.collection('messages')
.where('senderId', '==', someUserId)
.where('threadId', '==', someThreadId)
.get()
const qs2 = await firebase.firestore()
.collection('messages')
.where('receiverId', '==', someUserId)
.where('threadId', '==', someThreadId)
.get()
messages = ${merge 2 messages list above}
Quesetion
How can I fetch all messages of a thread by threadId while setting a correct security rules so that messages wouldn't be read by other users?
The key thing to realize is that Firebase's server-side security rules don't filter the data. They merely ensure that any operation your code tries to perform is authorizes. So to securely get filtered data, your code and security rules need to work together. For full details on this, see the documentation on securely querying data.
Since your rules only allow reading messages that have the correct value for senderId or receiverId, your query needs to filter for one of those fields too. Just ensuring that the threadId belongs to an allowed user isn't enough. That would require that Firestore read all documents to ensure they meet your security rules, which it can never do at scale.
So your "workaround" is actually the correct solution for this. Alternatively, you might want to consider storing each thread in a separate collection, so that you can secure access on the collection level and bypass the conditions.

How to obtain a record of a node, according to the value of a property, firebase?

I currently have the following node:
Basically what I want is to search the registry by the uid parameter. What I can not understand is that they tell me that I should not do it by means of a query, so what would be the other way? I have tried with the following:
firebase
.database()
.ref('nuevosUsuario')
.child(user.uid)
.once('value')
.then(snapshot =>
console.log(snapshot.val())
);
pero me imprime en consola null
Thank you in advance, I'm new to firebase.
You JSON structure stores user information, where it stores the information for each user under a so-called push ID (a key generated by calling push() or childByAutoId()). You're trying to query this structure to find the user based on their UID, which is stored in a property for each user. The only way to do this is by using a database query, like:
firebase.database()
.ref('nuevosUsuario')
.orderByChild("uid")
.child(user.uid)
.once('value')
.then(snapshot => {
snapshot.forEach(userSnapshot => {
console.log(snapshot.val())
});
});
You need to perform a loop here, since there may be multiple nodes that have the correct value for their UID property.
If there can logically be only one node for each user under nuevosUsuario, it is better to store the user information under the user's UID as a key, instead of using a push ID.
So you'd get a structure like:
"nuevosUsuario": {
"SYFW1u808weaGEf3fW...": {
"appellido": "PRUEBA",
"correo": "..."
...
}
}
This has a few advantages:
There can only be one child node for each user, since keys are by definition unique in a collection.
You can now get the user given their UID without a query, which is both faster and simpler in code. As in: the code in your question would work for this structure.

Categories