Firebase function access not related data [duplicate] - firebase

This question already has an answer here:
How to read any data from database on its write trigger on one of its child in firebase functions?
(1 answer)
Closed 5 years ago.
I'm searching for a way when I can access database elements not related to my trigger data in firebase functions.
As I see I can set(...) things on other elements but unfortunately I can't just read the values out of the elements, for example this doesn't work:
event.data.adminRef.root.child('bettings/'+matchId+'/begin_at').val()
I know about the 'proper way' to get those things, which is like:
var match = event.data.adminRef.root.child('bettings/' + matchId + '/begin_at');
match.once('value',function(snapshot){
var alma = snapshot.val();
console.log('alma val');
console.log(alma);
});
But I hope there is a better/easier way then write such codes all the time I need something out of my current scope.
Update: it is not a duplicate of How to read any data from database on its write trigger on one of its child in firebase functions? !
I know about ....once(..), and that I can access the values through promises but I need a much more quicker way, because sometimes it takes about 10 seconds, which is really slow and the user won't get the response in time!

Here is an example where you access data from a specific database node and perform a task with the value jobowner:
let jobRef = admin.database().ref(`/jobs/${jobid}`);
const jo = jobRef.child("owner").once('value');
return Promise.all([jo]).then(r => {
const jobowner = r[0];
const promises = [];
let key = admin.database().ref(`/jobowners/${jobowner}`).set({
value: 'test',
other: false
});
promises.push(room);
return Promise.all(promises);
});

Related

How to query an array of objects in a Firebase Cloud Function, to get a matching object and then update

I am using a scheduled task in a Firebase Cloud Function to query an array which contains a number of objects that need to be updated if a matching condition exists. My current attempt is using the 'array-contains' method to get the objects, then loop over them to find a matching condition which will then batch update the items. This is my data structure:
I need to find an object that is <= the current time, and also if the 'active' value = false.
export const liveMeetingsTrigger = functions.runWith( { memory: '1GB' }).pubsub
.schedule('every 1 minutes').onRun(async context => {
const now = admin.firestore.Timestamp.now();
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'liveMeetingDate').get();
const batch = admin.firestore().batch();
liveMeetings.forEach(doc => {
if(doc.data().liveMeetingDate <= now && doc.data().active == false){
batch.update(doc.ref,'active',true);
}
});
return await batch.commit();
});
I have also tried using an exact object in the query instead of just using 'liveMeetingDate', but still get no results back, any help would be great - thanks.
Debugging: As the array I am trying to reach is inside of the (map) object 'liveMeetings' i have tried the dot notation (liveMeetings.meeting) with no success. Also trying a new collection with the the 'meeting' array at top level has provided no success.
Simple logging in the console (liveMeetings.size) shows that nothing is being returned on the query, so therefore the logging does not even reach the loop in the code.
As explained in this anwser the following query will not work:
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'liveMeetingDate').get();
because the meetings array contain some objects, instead of "simple" or primitive data (e.g. string, number...).
You could query it with the exact objects, like:
const obj = {active: false, liveMeetingDate: ..., meetingId: ..., ....};
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'obj').get();
Another approach would be to create a new collection which contains the similar documents (same Document ID) but with a meeting Array that contains only the liveMeetingDate property.
Finally, note that since your Array is within a map, you need to do
await admin.firestore().collection('fl_content').where('liveMeetings.meeting', 'array-contains', ...).get();
(PS: I don't mark this question as duplicate since you expressly ask for more help in the comments of the duplicate question/answer)

Cloud Functions retrieve List from Firebase Database

I'm triggering a Cloud Function using Http Request.
The issue is to retrieve the entire List of Objects I have, without an event to then loop through them.
The List is under the account/userId Node.
Here is what I use but I get nothing:
return admin.database().ref('/account/' + userId).once('value').then(function (snap) {
let data = snap.val();
}
Without seeing your database structure it is a bit difficult to write an answer and be 100% sure it is a correct one, but the following should do the trick:
return admin.database().ref('/account/' + userId).once('value').then(function (snap)
snap.forEach(function(child) {
const childKey = child.key; // <- here you get the key of each child of the '/account/' + userId node
console.log(childKey);
const childVal = child.val(); // <- and here you get the values of these children as JavaScript objects
console.log(childVal);
});
});
In case this is not exactly what you are looking for, please update you Question with your database structure and the entire code of your Cloud Function.

Good way to delete all data according to criteria/child's value in Firebase database admin?

I want to clean up this userPublic by deleting all of its child node which has isTesting == true. I am using Firebase's cloud function. My approach would be :
const userPublic = admin.database().ref("/userPublic")
const testsInUserPublic = userPublic.orderByChild("isTesting").equalTo(true)
testsInUserPublic.once("value", dataSnapshot => {
// ???
})
Since I can only call .remove() on reference and not snapshot but to filter the child I want it returns snapshot, how can I get the reference from snapshot? (I would like to know the key XXX-XXX-XXX of each filtered child, so I can concatenate with userPublic and .remove() them one by one)
Also, even if I can get all the references that I want to remove I think deleting them one by one by calling .remove() then wait for promise, then call the next one does not sounds like an optimal way. Are there any way to remove all of them in one go?
If it involves calling .update() on the top userPublic node, I would have to fetch everything, remove the one with isTesting and then put the remaining back for update. This sounds like it is not efficient compared to the filtering way. As eventually the one with .isTesting is only about 5% of all data. Or is this actually the approach everyone is using?
You're almost there. All that's left is to create a single multi-location update from the results of your query:
const userPublic = admin.database().ref("/userPublic")
const testsInUserPublic = userPublic.orderByChild("isTesting").equalTo(true)
testsInUserPublic.once("value", snapshot => {
var updates = {};
snapshot.forEach(function(child) {
updates["/userPublic/"+child.key] = null;
});
userPublic.update(updates);
})
Doing this with promises would not be too different:
testsInUserPublic.once("value", snapshot => {
var promises = [];
snapshot.forEach(function(child) {
promises.push(child.ref.remove());
});
Promise.all(promises); // this returns a promise that resolves once all deletes are done, or that rejects once one of them fails
})
Performance of this will be very similar, since Firebase pipelines the requests over a single connection. See http://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786

Firebase trouble reading lists

The tutorial I'm following here https://www.firebase.com/tutorial/#tutorial/basic/5 says that for lists you can push a new item like this...
var myDataRef = new Firebase("https://.../items"
myDataRef.push({name: "Sally"});
The docs say here https://www.firebase.com/docs/managing-lists.html that to read list data just do something like this
myDataRef.on('value', function(snapshot) {
console.log(snapshot.val());
});
snapshot.val() gives back an object with random keys that contain my objects. Do I really have to do something like
arr =[] Object.keys(snapshot.val()).forEach(function(key) { arr.push(snapshot.val()[key])})
to get a normal looking array of objects? or am I missing something here.. Thanks a ton.
I'm not entirely sure what you're asking, but see if this helps to retrieve the data you're looking for with snapshot.val().Use child_added, this will go down the endpoint("firebase_url/items") and show each immediate object.
myDataRef.on('child_added', function(snapshot) {
console.log(snapshot.val());
});
Check out this link to get more info on reading from firebase, http://www.firebase.com/docs/reading-data.html
JavaScript objects are not ordered. So the keys may appear in different order than your data (JSON can put them anywhere it pleases). To iterate them in order, try forEach:
myDataRef.on('value', function(snapshot) {
snapshot.forEach(function(ss) {
console.log(ss.name());
});
});
For more real-time results, Ron's example using child_added is ideal.

How to tell which descendants are changed with on("child_changed")

For example, I have following database structure:
/
+ users
+ 1
+ items
+ -xxx: "hello"
+ 2
+ items
Then;
var usersRef = new Firebase("https://mydb.firebaseio.com/users");
usersRef.on("child_changed", function(snapshot) {
utils.debug(JSON.stringify(snapshot.exportVal()));
});
If a value, "world", is pushed to "/users/1/items", I may get:
{"items": {"-xxx": "hello", "-yyy": "world"}}
So, how to tell which one is changed?
I need to on("child_added") every single ref to "/users/$id/items"?
NOTE: I'm trying to write an admin process in node.js.
The child_changed event only provides information on which immediate child has changed. If a node deeper in a data structure changed, you'll know which immediate child was affected but not the full path to the changed data. This is by design.
If you want granular updates about exactly what changed, you should attach callbacks recursively to all of the elements you care about. That way when an item changes, you'll know what the item was by which callback is triggered. Firebase is actually optimized for this use case; attaching large numbers of callbacks -- even thousands -- should work fine. Behind the scenes, Firebase aggregates all of callbacks together and only synchronizes the minimum set of data needed.
So, for your example, if you want to get alerted every time a new item is added for any user, you could do the following:
var usersRef = new Firebase("https://mydb.firebaseio.com/users");
usersRef.on("child_added", function(userSnapshot) {
userSnapshot.ref().child("items").on("child_added", function(itemSnapshot)
utils.debug(itemSnapshot.val());
});
});
If you are working with a very large number of users (hundreds of thousands or millions), and synchronizing all of the data is impractical, there's another approach. Rather than have your server listen to all of the data directly, you could have it listen to a queue of changes. Then when clients add items to their item lists, they could also add an item to this queue so that the server becomes aware of it.
This is what the client code might look like:
var itemRef = new Firebase("https://mydb.firebaseio.com/users/MYID/items");
var serverEventQueue = new Firebase("https://mydb.firebaseio.com/serverEvents");
itemRef.push(newItem);
serverEventQueue.push(newItem);
You could then have the server listen for child_added on that queue and handle the events when they come in.
Andrew Lee gave a nice answer, but I think you should try to use cloud functions. something like this should work:
exports.getPath = functions.database.ref('/users/{id}/items/{itemId}')
.onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
console.log('user id', event.params.id);
console.log('item id', event.params.itemId);
});

Resources