Reputation: 105
I'm building a project and within this project i have a one-to-one chat. Each chats are stored using the realtime database from firebase (I'm using the react-native-firebase librairy here).
My data tree currently looks like it :
We have something like that : chat/{currentUserID}/{userID} then the messages are stored here and send to the database using the push() method which auto-generate an ID.
Basically what i'm trying to do is to do some cleaning by removing any messages from more than 7 days ago. I already have the logic for that and im doing it for another part of my app here :
const randomTestTimerDB = Date.now() - 1
admin.database()
.ref('/publicChat')
.orderByChild('timestamp')
.endAt(randomTestTimerDB)
.once('value')
.then((snapshot) => {
snapshot.forEach((snap) => {
snap.ref.remove()
})
})
This works just fine. But my problem here is that i'm using a cloud function do to that so i can't follow my tree to reach them as they are nested below and below IDs i don't have from there.
Knowing that inside the chat collection we can find all users that already sent a message. Knowing also each of theses contain the documents (userID) from all users the currentUser interact with and below each of theses documents, attached to the userID document we finally have all the messages shared between these two users.
I tried to reach them using different ways like calling the database directly within a forEach method to grad the snapshot.id of each and maybe get the child().val() afterwards but it doesn'st seem to work.
Here is what the code of what i tried so far :
admin.database()
.ref('/chat')
.once('value')
.then((snapshot) => {
snapshot.forEach((snap) => {
if(snap.child().val().timestamp <= randomTestTimerDB) {
snap.ref.remove()
}
})
})
admin.database()
.ref('/chat')
.once('value')
.then((snapshot) => {
snapshot.forEach((snap) => {
admin.database().ref(`/chat/${snap.id}`).orderByChild('timestamp').endAt(randomTestTimerDB).once('value').then((snp) => {
snp.forEach((eachSnp) => {
eachSnp.ref.remove()
})
})
})
})
How can i grab those nested messages in the easiest way possible ? If this is even possible or should i think of another way to store and organise my data tree to make it easier ?
Thanks for your help !
Upvotes: 0
Views: 631
Reputation: 598797
If you're reading the entire chat
node, you can loop over the various levels like this:
admin.database().ref('/chat').once('value').then((snapshot) => {
snapshot.forEach((user1Snapshot) => {
user1Snapshot.forEach((user2Snapshot) => {
console.log(`Chat between ${user1Snapshot.key} and {$user2Snapshot.key}`);
// TODO: perform a query on user2Snaphot.ref
})
})
})
This reads the entire chats
node though, including messages that you're not going to delete.
If you want to only read the data that you're going to delete, you have to already know all UID pairs that exist under chats
.
The best way to do that is by storing the UID pairs somewhere too, so that you can access them without also downloading all chat messages. So something like:
"existing_chats": {
"$uid1_$uid2": true,
"$uid2_$uid3": true
}
Now you can read the UID pairs from this structure, loop through them, and then perform a cutoff query for each pair.
Finally, you could also consider keeping a single flat list of the paths to all messages and the expiration timestamp as their value:
"message_expirations": {
"$uid1_$uid2_$messageid1": 127864586723459,
"$uid1_$uid2_$messageid2": 127864586723469,
...
}
With this, you can do a single query with orderByValue
on expired messages, and then delete them all. While it leads to the most data duplication, it is the most direct representation of your use-case in the data structure, which I often find easiest to maintain.
If you choose this approach, don't forget to also remove the expired nodes from message_expirations
(preferably with a multi-path update), so that this node also doesn't keep growing infinitely.
Upvotes: 1