Calculating product rating - javascript

I am trying to calculate the product rating for my products in my realtime database using a firebase cloud function. What am I missing since am getting errors in the logs after deploying
I have deployed the code but still on rating added no change happens
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//firebase db calculations for rating average
exports.productAverage = functions.database.ref('/Products/{product_id}/rating')
.onCreate((snapshot, context) => {
return admin.database().ref('/Products/{product_id}/rating').once('value')
.then((snapshot) => {
let sum = 0;
snapshot.forEach(child => {
sum = sum + child.val();
});
let productRating = sum / snapshot.numChildren();
return admin.database().ref('/Products/{product_id}').child('productRating').set(productRating);
});
});
I expect each time a productRating node is added, the average updates a node on the database with productRating

There are a few things that immediately jump out:
You're triggering on onCreate. But you want to recalculate the average whenever any rating is added (or removed or updated), you'll want to trigger on onWrite.
You're reloading the same data in your function that is pass in already. That is wasteful, so let's remove that.
With these two changes, you'd end up with something like:
exports.productAverage = functions.database.ref('/Products/{product_id}/rating')
.onWrite((change, context) => {
let snapshot = change.after;
let sum = 0;
snapshot.forEach(child => {
sum = sum + child.val();
});
let productRating = sum / snapshot.numChildren();
return snapshot.ref.parent.child('productRating').set(productRating);
});

Related

React Native - UseEffect Constantly Updates

I am using useEffect to get the total jigsaw pieces from my async storage to be displayed on the home page. I use async storage to get the number of jigsaws stored in the storage then for each jigsaw, i add the total amount of jigsaw pieces. I do this within useEffect as the total amount of jigsaw pieces may change depending on if the user adds more jigsaw to their collection. However, when i use useEffect, my total amount of jigsaw pieces constantly updates and never stops.
Code:
let [totalPieces, setTotalPieces] = useState(0);
const [numJigsaw, setNumJigsaw] = useState([]);
const getNumOfJigsaw = async () => {
try {
setNumJigsaw(await AsyncStorage.getAllKeys());
} catch (e) {}
return numJigsaw;
};
const getAllJigsaw = async () => {
let jigsaws = await getNumOfJigsaw();
for (let z = 0; z < jigsaws.length; z++) {
let currentJigsaw = JSON.parse(await AsyncStorage.getItem(jigsaws[z]));
setTotalPieces(totalPieces+ parseFloat(currentJigsaw.pieces));
}
};
useEffect(() => {
getAllJigsaw();
}, [totalPieces]);
I believe the issue is because totalPieces is dependant? Im not entirely sure though.
Yes - you've set the effect to run when totalPieces changes (via the dependency).
The effect (when the async stuff resolves) sets totalPieces, so the effect is run again due to that dependency, etc, etc.
It sounds like you're looking for something like
const [totalPieces, setTotalPieces] = useState(0);
const [jigsawKeys, setJigsawKeys] = useState([]);
useEffect(() => {
(async () => {
const jigsaws = await AsyncStorage.getAllKeys();
let total = 0;
for (let z = 0; z < jigsaws.length; z++) {
let currentJigsaw = JSON.parse(await AsyncStorage.getItem(jigsaws[z]));
total += parseFloat(currentJigsaw.pieces);
}
setJigsawKeys(jigsaws);
setTotalPieces(total);
})();
}, [totalPieces]);
all in all (I renamed numJigsaw to jigsawKeys so it makes more sense).

Not getting results from cloud functions by .where()

I am using this code to get data from a collection by comparing
/* eslint-disable */
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
const database = admin.firestore();
exports.checkForPending = functions.pubsub.schedule('* * * * *').onRun(async (context)
=> {
var currentTime = admin.firestore.Timestamp.now();
var total; //getting time from firebase server
var firebase_time = admin.firestore.Timestamp.now(); // {'_seconds': 123456,
_nanoseconds: 123000000}
// Convert map of nanoseconds and seconds to milliseconds
var firebase_seconds_to_milliseconds = firebase_time._seconds * 1000; // 123456000
var firebase_nanoseconds_to_milliseconds = Math.round(firebase_time._nanoseconds /
1000000) //123
var milliseconds = firebase_seconds_to_milliseconds +
firebase_nanoseconds_to_milliseconds;
var firebase_month = firebase_time.toDate().getMonth();
var unix_to_date_before_buffer = new Date(milliseconds);
var unix_to_date_after_buffer = new Date(milliseconds);
// var unix_to_date = new Date(total);
unix_to_date_before_buffer.setSeconds(0); //coverted from 6:38:23 to 6:38:00
unix_to_date_after_buffer.setSeconds(0);
unix_to_date_after_buffer.setMinutes(unix_to_date_after_buffer.getMinutes() + 1);//coverted from 6:38:23 to 6:39:00
//Want to edit month in this timestamp;
const query =
database.collection("users").doc('IELTS').collection('IELTS').where("next", '>=', unix_to_date_before_buffer).where("next", '<=', unix_to_date_after_buffer); // comparing here
const snapshot = await query.get();
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data().name),
queryTest2 = database.collection('rest');
queryTest2.add(
{
'executed': true,
'name': doc.data().name
}
)
});
return null;
});
The am not getting any results in the console. I am wondering why?
I didn't see any new collection 'rest' formed.
The time in the collection of parameter 'next' was 6:38:24 in the collection in the example.
What I am trying to build -
I am making an app for an institute. I am registering users and making a collection which looks like -
collection
(here 'date' is the date of registration and 'next' is the time when The pay will automatically change to 0 because of the starting of next month.)
Now I am running a cloud function that is checking every day if 'next' is equal to the current time of server, then I want to execute something (add a user to a new collection called 'pending'.) and update 'next' to the next month. which should look like this -
edited-paramter

Is it possible to set limit for array in firestore?

i am planning to create an array of only 5 elements max in firestore like this
Array a = [1,2,3,4,5]
then add element 6 it will look like this
Array a = [2,3,4,5,6]
This cloud function (found here: https://github.com/firebase/functions-samples/blob/master/limit-children/functions/index.js) does what you want in Realtime Database:
'use strict';
const functions = require('firebase-functions');
// Max number of lines of the chat history.
const MAX_LOG_COUNT = 5;
// Removes siblings of the node that element that triggered the function if there are more than MAX_LOG_COUNT.
// In this example we'll keep the max number of chat message history to MAX_LOG_COUNT.
exports.truncate = functions.database.ref('/chat').onWrite((change) => {
const parentRef = change.after.ref;
const snapshot = change.after
if (snapshot.numChildren() >= MAX_LOG_COUNT) {
let childCount = 0;
const updates = {};
snapshot.forEach((child) => {
if (++childCount <= snapshot.numChildren() - MAX_LOG_COUNT) {
updates[child.key] = null;
}
});
// Update the parent. This effectively removes the extra children.
return parentRef.update(updates);
}
return null;
});
I believe you can adapt it for Firestore.

Firebase function won't deleted child after certain time

I want to deleted a child after a certain time. I know that you need Firebase function to achief this. This is what I got so far:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.removeOldMessages = functions.https.onRequest((req, res) => {
const timeNow = Date.now();
const Ref = admin.database().ref('/Feed');
Ref.once('value', (snapshot) => {
snapshot.forEach((child) => {
if (1000*(Number(child.val()['timestamp']) + Number(child.val()['duration'])) >= timeNow) {
child.ref.set(null);
}
});
});
return res.status(200).end();
});
I want to deleted the child when the duration is over (the duration is in seconds). This is my structure:
Thanks!
You're sending a response to the caller at the end of the function, which will be executed before the data from the database is returned. And Cloud Functions will stop executing your code straight after that res.status(200).end(), so the database cleanup never happens.
To prevent this, only send a response to the caller after all data has been deleted from the database:
exports.removeOldMessages = functions.https.onRequest((req, res) => {
const timeNow = Date.now();
const Ref = admin.database().ref('/Feed');
return Ref.once('value', (snapshot) => {
let updates = [];
snapshot.forEach((child) => {
if (1000*(child.val().timestamp + child.val().duration) >= timeNow) {
updates[child.key] = null;
}
});
return Ref.update(updates).then(() => {
return res.status(200).end();
});
});
});
I highly recommend storing an additional property in your child nodes though, with the precalculated value of timestamp + duration. By having such a property, you can run a query on the nodes that have expired, instead of having to read all child nodes and then filtering in code.
For an example of this, see my answer to Delete firebase data older than 2 hours, and the Cloud Functions example that was based on that.

Firebase Realtime Database and Cloud Functions -- increment counter based on data nodes

I'm working on a web application that will visualize data from my Firebase database. But first, I want to be able to "count" the total number of users with a given data so that I can then use that count number in my graphs.
For reference, my database looks like this:
Because I expect separate totals for the required keys, I'm guessing that I'll need separate counters for each one. I've started writing a cloud function to keep track of when a new user is created:
import * as functions from 'firebase-functions'
export const onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val()
const afterGrad = userData.afterGrad
const gender = userData.gender
const gradDate = userData.gradDate
const program = userData.program
const race = userData.race
const timeToComplete = userData.timeToComplete
})
But now, I'm extremely lost at how I should go about creating counters. Would something like this suffice, with an individual counter for each constant?
import * as functions from 'firebase-functions'
var counterAfterGrad;
export const onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val()
const afterGrad = userData.afterGrad
var counterAfterGrad++
})
Or should I be thinking about using a transaction in this case? I'm really not sure of the best way, and would really appreciate some help.
Yes, you should use a transaction. See the documentation here: https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions and https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
For counting the overall number of users you could do as follows:
export const onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val()
const afterGrad = userData.afterGrad
const allUsersCounterRef = admin
.database()
.ref('allUsersCounter');
return allUsersCounterRef
.transaction(counter_value => {
return (counter_value || 0) + 1;
})
})
Note that you may have to take into consideration the deletion of a user.
You could very well have several counters, for example by "gender" (male/female) and by "program". You would then use an object in the transaction as follows:
exports.onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val();
const countersRef = admin.database().ref('counters');
return countersRef.transaction(currentData => {
currentData[userData.gender] = (currentData[userData.gender] || 0) + 1;
currentData[userData.program] = (currentData[userData.program] || 0) + 1;
return currentData;
});
});

Categories