I have this function:
firestore.collection('customers').doc(userID).collection('subscriptions')
.where('status', 'in', ['trialing', 'active']).get()
.then(activeSubscriptions => {
// if this is true, the user has no active subscription.
if (activeSubscriptions.empty === true) {
console.log("line 31")
{signOut(props.history)}
subStatus = "inactive"
}
});
Basically, in firestore, I have a customers collection, a doc with the user id, and then the subscriptions collection in which is created upon a user processing stripe. In the subscriptions collection, it has a doc which is the subscription id, and I have some fields I want to grab. See the attached picture:
I want to grab the current_period_end data so I can put it out on screen. how would I do this?
If you are looking to access fields from a firestore document, you can do it by specifying the field in square brackets.
With a firestore structure like /customer/cus123/subscriptions/sub123 I was able to obtain a timestamp field of sub123 with this code:
let cusRef = db.collection('customer').doc('cus123').collection('subscriptions').doc('sub123');
cusRef.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
}
else
{
console.log('Name: ',doc.data()['name']);
console.log('Tmsp: ',doc.data()['tmsp123']);
}
});
I hope you find this useful.
Related
I have in my Firestore database a list of documents that include this field 'Participants', as a nested object.
I want to make a query that gets only one document from the database (to see if it exists or not) that has (for example) user id 5 and 6.
This is what my code looks like
const chatsCollection = db.collection('chats');
async function createChat(myId, otherUserId){
chat = await chatsCollection
.where(`participants.${myId}`, "==", true)
.where(`participants.${otherUserId}`, "==", true)
.limit(1).get();
if(!chat.exists){
alert('chat doesnt exist')
//create chat
} else {
alert('chat exists')
//do something else
}
}
However, even if the chat with the participants object does indeed exist in the database, the result of the code indicates that it doesn't.
Here is the structure of the data when it is added to the database
var chat_key = (Math.random() + 1).toString(36).substring(2);
chatData = {
key: chat_key,
created_at: new Date(),
participants: {
myId: true,
otherUserId: true,
}
}
chatsCollection.doc(chat_key).set(chatData);
I appreciate any help on how to solve this problem.
Thanks :)
on method collection, you can use empty property to see if the query getting data or not
if(chat.empty){
alert('chat doesnt exist')
//create chat
} else {
alert('chat exists')
//do something else
}
to get only one document from your query, you can use
chat.docs[0].data();
Introduction
In order to listen all the docs of my collection that have beed removed, without downloading the entire collection, I am doing the same as commented here.
Rather than deleting docs completely, just add a "deleted: true" property and then listen on the query "db.collection("cities").where("deleted", "==", true)"
Problem
For some reason I don't understand, I am receiving all the "modified" events as "added" from my Firestore listener.
Chat Room Deletions Code
I am implementing a chat list screen with pagination, so I am just fetching old docs when the screen is mounted, and subscribing to all changes that happens on each chat room doc after new Date() (the time in which the screen is mounted).
For "removing" the chat room docs, I am doing the following:
async function deleteChatRoom(chatRoomId, userId) {
const chatRoomRef = firestore.collection("chats").doc(chatRoomId);
const chatRoomDoc = await chatRoomRef.get();
const chatRoomData = chatRoomDoc.data();
const messagesPath = `${chatRoomRef.path}/messages`;
// Check that the user is member of the room
if (!chatRoomData.members[userId]) {
throw chatErrors.notChatRoomMember();
}
// Delete all the chatroom messages
await deleteCollection(messagesPath);
// Delete the chat room
await chatRoomRef.update({
deleted: true, <----
lastMessage: admin.firestore.FieldValue.delete(), // Delete denormalized sensitive data
modifiedAt: admin.firestore.FieldValue.serverTimestamp(), <----
read: admin.firestore.FieldValue.delete(), // Delete denormalized sensitive data
});
}
As you can see, I am updating the "modifiedAt" and "deleted" fields.
Listener Code
With this, what I am trying to reach is being able to listen to all the chat room changes ("lastMessage" field updates, "read" field updates, "deletions"...) that happens after new Date() in the client side, as I commented before. As follows:
export function listenMyChats(
startAt = undefined,
onNext,
onError
) {
const currentUserId = getCurrentUser()?.uid;
if (!currentUserId) {
throw authErrors.authenticationRequired();
}
let query = firestore
.collection("chats")
.where("membersArray", "array-contains", currentUserId)
.orderBy("modifiedAt");
if (startAt) {
query = query.startAt(startAt);
}
return query.onSnapshot(onNext, onError);
}
Something that has sense to me, as I am avoiding reading the entire collection when using the field "startAt" and "orderBy(modifiedAt)".
Subscribing code
Then, to handle all and subscribe to the chat room changes, I am doing the following:
const handleChatsChanges = async (querySnapshot) => {
const changes = querySnapshot.docChanges();
await Promise.all(
changes.map(async (change) => {
if (change.type === "added") {
// NEW INCOMING CHATS
console.log("Added");
} else if (change.type === "modified") {
// MODIFIED CHATS ("deleted", "read", "lastMessage" fields...)
console.log("Modified");
}
});
);
};
useEffect(() => {
const listener = listenMyChats(
startAfter.current,
handleMyChatsChanges,
handleOnListenMyChatsError
);
return () => {
listener();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
The main problem I am experiencing is that, instead of getting the document modifications in the "modified" scope, I am getting "added" events... Why is this happening?
I thought that the listeners behavior were to get all the docs that match the query condition (as "added") when subscribing. And that, after this, the "added" scope was only executed when new docs were added to the target collection (matching the query conditions, of course).
So, why am I receiving the docs fields modifications as "added"?
As detailed in the comments above, the problem comes form the fact that the Firestore documents that are modified where not present in the first snapshot, because your listener is applied on the following query:
firestore
.collection("chats")
.where("membersArray", "array-contains", currentUserId)
.orderBy("modifiedAt");
And when you modify them they are returned by the above query and therefore they are considered as added by the listener.
They would have been considered as modified if, and only if, they were included in the first snapshot returned after setting the listener.
I have a chat app build in react native. When a user decides to delete their profile, I want to remove all references of them from the database.
The DB has references to their user id in the "matches" table, the "chat" table, and the "messages" table for each of the people the deleted user was chatting with.
I am using firebase functions to handle the deletion of the user doc data and auth but I am not sure what the best way to go about removing all of these references would be. My question is: what is the best way to remove all references of an ID out of a somewhat complex database? I assume this will be taxing to loop through every single user in the DB to search for this one ID.
deleteAccount = () => {
var db = firebase.firestore();
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
//delete user data
db.collection("Users")
.doc(user.uid)
.delete();
} else {
console.log("user needs to reauth");
return false;
}
});
};
firebase functions
exports.deleteUser = functions.firestore
.document("Users/{userID}")
.onDelete((snap, context) => {
const deletedValue = snap.data();
// Delete the images
try {
admin.auth().deleteUser(deletedValue.id);
const imgRef1 = firebase.storage().ref(user.uid + "/images/0")
? firebase.storage().ref(user.uid + "/images/0")
: null;
const imgRef2 = firebase.storage().ref(user.uid + "/images/1")
? firebase.storage().ref(user.uid + "/images/1")
: null;
const imgRef3 = firebase.storage().ref(user.uid + "/images/2")
? firebase.storage().ref(user.uid + "/images/2")
: null;
imgRef1.delete().then(function() {
imgRef2.delete().then(function() {
imgRef3.delete().then(function() {});
});
});
} catch (e) {
console.log("no images to delete");
}
});
Firebase products such as the databases and storage have no implicit knowledge of what data belongs to what user. That relation only exists because your application code made it.
For that reason you will also have to look up/traverse the relations when deleting the user, to find (and delete) their data. There are no shortcuts in the product here, although there is a open-source library that contains an implementation that works from a configuration file: user-data-protection
Edit: I just realized there's actually an Extension to Delete User Data, which does pretty much the same as the library linked above. It might be worth to have a look if that suits your needs
I am struggling how to retrieve data from firebase having a child key, such as uid.
here is the structure of my firebase.
Currently I am making an admin panel which can read the order placed by each user and render it through flatlist in react native, but it seems that I can't access their order because every time the user places an order it is stored on their unique User.id
I don't know how to make a reference to the User.id child like firebase.database().ref(orders/${AllUserId}/products)
You can use forEach loop to fetch ids and can get values as so
firebase.database().ref('order').on('value', (snapshot) => {
snapshot.forEach((child) => {
uid = child.key; // gives uid1
child.forEach((snap) =>{
var id = snap.key; // first iteration gives uid2
firebase.database().ref('order/'+uid+'/'+id).on('value', (snapchild) => {
snapchild.forEach((snapshotchild) =>{
console.log(snapshotchild.val());
});
});
});
});
});
This could be more insightful.
I have collection of documents where the id of the doc is the users id.
Lets call these user documents.
Each "user document" contains a subcollection of chat messages. But not all "user documents" contains any fields (data, other than the subcollection).
I wan't to return all the doc in the collection that don't have any fields, but have a subcollection, but I seems this is not possible?
var allUserDocs = {},
count = 0,
users = firestore.collection("users");
users.get().then(snapshot => {
snapshot.forEach(doc => {
count++;
allUserDocs[count] = doc.data();
});
allUserDocs.count = count;
res.status(200).send(allUserDocs);
})
this code only returns the docs that contains fields, not the docs that only have a subcollection? Is there a way to return all?
How can i get a list of all document ids in the collection? both empty and non-empty ones? or how can I add a field to all the docs without fields if i cant access them?
There is a listDocuments method that retrieves all documents, missing or not, that have a subcollection. Here's the page in the docs that explains it.
Something like this might be what you are looking for:
let collectionRef = firestore.collection('col');
return collectionRef.listDocuments().then(documentRefs => {
return firestore.getAll(...documentRefs);
}).then(documentSnapshots => {
for (let documentSnapshot of documentSnapshots) {
if (documentSnapshot.exists) {
console.log(`Found document with data: ${documentSnapshot.id}`);
} else {
console.log(`Found missing document: ${documentSnapshot.id}`);
}
}
});
You would not care whether the docRef exists or not.
Nevertheless, it does not sound like a good solution to have empty documents. What is the logic you were pursuing with an architecture where users can be empty, but messages underneath them still matter? Maybe if you still need to access them you can add a boolean variable to determine if the user is active or not, instead of leaving a blank document.
Use doc.id
users.get().then(snapshot => {
snapshot.forEach(doc => {
count++;
allUserDocs[doc.id] = doc.data();
});
allUserDocs.count = count;
res.status(200).send(allUserDocs);
})