Cloud function query with typescript - node.js

I am trying to query a firestore field (dropdown) 'Title'. I am using following code to get the total number of Title for a given user but i want to apply filter and get count only when Title == Game. Please help. How can i modify this function to set counter only when this condition is met.
Currently it is getting me number of title.
import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions'
admin.initializeApp()
export const updateCharts =
functions.firestore.document('users/{UserId}/count/{uid}')
.onWrite(async(change, _) => await updateStats(change))
async function updateStats (change:
functions.Change<functions.firestore.DocumentSnapshot>){
const chartRating = change.after.ref.parent
let Title = 0
const docRefs = await chartRating.listDocuments()
for (const docRef of docRefs) {
const snapshot = await docRef.get()
const data = snapshot.data()
if (data !== undefined) {
Title++
}
}
const restaurantRef = chartRating.parent!
console.log('{restaurantRef.path} now has ${Title}')
await restaurantRef.update({
Title: Title,
})
}

Replace this
let Title = 0
const docRefs = await chartRating.listDocuments()
for (const docRef of docRefs) {
const snapshot = await docRef.get()
const data = snapshot.data()
if (data !== undefined) {
Title++
}
}
with
let title = (await chartRating.where('Title', '==', 'Game').get()).size;
replace the where statement with the condition you need.

Related

Firebase Function onCreate add to new collection works, but update does not

I have this onCreate Trigger, I am using it to aggregate and add record or update record. First it takes minutes to add the record and then the update never runs just keeps adding, not sure why my query is not bringing back a record to update.
Any suggestion would be great.
exports.updateTotals = functions.runWith({tinmeoutSeconds: 540})
.firestore.document("user/{userID}/CompletedTasks/{messageId}")
.onCreate(async (snap, context) => {
const mycompleted = snap.data();
const myuserid = context.params.userID;
console.log("USER: "+myuserid);
const mygroup = mycompleted.groupRef;
const myuser = mycompleted.userRef;
const newPoints = mycompleted.pointsEarned;
console.log("POINTS: "+newPoints);
const data = {
groupRef: mygroup,
userRef: myuser,
pointsTotal: newPoints,
};
const mytotalsref = db.collection("TaskPointsTotals")
.where("groupRef", "==", mygroup)
.where("userRef", "==", myuser);
const o = {};
await mytotalsref.get().then(async function(thisDoc) {
console.log("NEW POINTS: "+thisDoc.pointsTotal);
const newTotalPoints = thisDoc.pointsTotal + newPoints;
console.log("NEW TOTAL: "+newTotalPoints);
if (thisDoc.exists) {
console.log("MYDOC: "+thisDoc.id);
o.pointsTotal = newTotalPoints;
await mytotalsref.update(o);
} else {
console.log("ADDING DOCUMENT");
await db.collection("TaskPointsTotals").doc().set(data);
}
});
});
You are experiencing this behavior because while querying for updates you are getting more than 1 document and you are using thisDoc.exists on more than one document. If you have used typescript this could have been catched while writing the code.
So for the update query, if you are confident that only unique documents exist with those filters then here’s the updated code that I have recreated using in my environment.
functions/index.ts :
exports.updateTotals = functions.runWith({timeoutSeconds: 540})
.firestore.document("user/{userId}/CompletedTasks/{messageId}")
.onCreate(async (snap, context) => {
const mycompleted = snap.data();
const myuserid = context.params.userID;
console.log("USER: "+myuserid);
const mygroup = mycompleted.groupRef;
const myuser = mycompleted.userRef;
const newPoints = mycompleted.pointsEarned;
console.log("POINTS: "+newPoints);
const data = {
groupRef: mygroup,
userRef: myuser,
pointsTotal: newPoints,
};
const mytotalsref = admin.firestore()
.collection("TaskPointsTotals")
.where("groupRef", "==", mygroup)
.where("userRef", "==", myuser);
await mytotalsref.get().then(async function(thisDoc) {
if (!thisDoc.empty) { // check if the snapshot is empty or not
const doc = thisDoc.docs[0];
if(doc.exists){
const newTotalPoints = doc.data()?.pointsTotal + newPoints;
const id = doc.id;
await db.collection("TaskPointsTotals").doc(id).update({pointsTotal: newTotalPoints});
}
} else {
await db.collection("TaskPointsTotals").doc().set(data);
}
});
});
For more information about QuerySnapshot methods check this docs

How I can add new field in coming firebase snapshot of onWrite() firebase cloud method

I have tried with set and setvalue() methods. I want to add document_id field in coming snapshot here is my code.
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
admin.initializeApp();
const db = getFirestore();
exports.locationUpdateListener = functions.region('asia-south1').firestore
.document('location/{locationId}')
.onWrite((change, context) => {
const data = change.after.data();
const timeMilli = process.hrtime.bigint();
const id = data.mobile + '_' + timeMilli;
data.set({ 'document_id': id });//here I need to add document_id field into data.
db.doc('history_location/' + id).set(data);
});
If you set the new field to data it just exists in the current code. To persist the value, you will have to write it back to Firestore with something like this:
exports.locationUpdateListener = functions.region('asia-south1').firestore
.document('location/{locationId}')
.onWrite((change, context) => {
const data = change.after.data();
const timeMilli = process.hrtime.bigint();
const id = data.mobile + '_' + timeMilli;
// 👇
if (data.document_id !== id) {
change.after.ref.update({ 'document_id': id });
}
db.doc('history_location/' + id).set(data);
});
Don't forget the condition I added, as otherwise you'll end up with an endless loop.

'Unexpected token' when recursively calling async function in Nodejs

My app contains posts with nested comments in Firebase Firestore structured such that each post/comment with docID has a sub collection postComments. Thus, a given post/comment can have an infinite number of nested comments.
comments
- docID
postComments
- docID
- docID
- docID
- docID
postComments
- docID
- docID
I am currently writing a Firebase cloud function to recursively query all documents and sub collection documents of a given docID and return all of those documents in an array. My plan was to define the getChildComments async function which takes in a docID and returns all of the documents in that document's postComments sub collection. I would then recursively call getChildComments until I have built an array with all of the nested comments in a thread.
exports.loadWholeCommentThread = functions.https.onCall(async (data, context) => {
let comments = await getChildComments(data.rootID);
return comments;
});
async function getChildComments(docID) {
try {
const db = admin.firestore();
const commentsRef = db.collection('comments').doc(docID).collection('postComments');
var comments = [];
const commentsQuerySnap = await commentsRef.get();
commentsQuerySnap.forEach((comment) => {
let commentData = comment.data();
comments.push(commentData);
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID);
comments.concat(childComments);
}
});
return comments;
} catch (error) {
functions.logger.log(error);
throw new functions.https.HttpsError('unknown', 'ERROR0', { message: error.message } )
}
}
Unfortunately, when I try to deploy my code, I get the error Parsing error. Unexpected token getChildComments on the line where I recursively call getChildComments inside of getChildComments. Removing the await from this line fixes the build issue but then the recursive call doesn't finish.
How should I fix my issue? Or is there a better way to query all nested documents?
This is because you have used await outside of an async function (note that it is inside an arrow function!).
const comments = [];
const commentsQuerySnap = await commentsRef.get();
commentsQuerySnap.forEach((comment) => {
let commentData = comment.data();
comments.push(commentData);
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID); // the keyword "await" here is invalid
comments = comments.concat(childComments);
}
});
But you can't just add async to this arrow function, because then your code won't properly wait for the comments array to be filled.
To properly fix this, you need to use .map() on the commentsQuerySnap.docs array in addition to using Promise.all to wait for each comment (and its children) to be retrieved.
const comments = [];
const commentsQuerySnap = await commentsRef.get();
await Promise.all(
commentsQuerySnap.docs.map(
async (comment) => {
let commentData = comment.data();
comments.push(commentData);
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID);
comments = comments.concat(childComments);
}
})
)
);
While that above block works, the comments array may be out of order to what you were expecting. If you must maintain order of the comments fetched so they are in the same order as the query, you should return the built comments array for each document and then flatten them after they all have been retrieved.
// removed const comments = []; here
const commentsQuerySnap = await commentsRef.get();
const arrayOfCommentThreads = await Promise.all(
commentsQuerySnap.docs.map(
async (comment) => {
let commentData = comment.data();
const commentThread = [commentData];
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID);
commentThread = commentThread.concat(childComments);
}
return commentThread;
})
)
);
const comments = arrayOfCommentThreads.flat();
Personally, I prefer the spread operator to using .concat like so:
const commentsQuerySnap = await commentsRef.get();
const arrayOfCommentThreads = await Promise.all(
commentsQuerySnap.docs.map(
async (comment) => {
const commentData = comment.data();
if (commentData.commentCount === 0) {
return [commentData];
}
const childComments = await getChildComments(commentData.commentID);
return [commentData, ...childComments];
})
)
);
const comments = arrayOfCommentThreads.flat();

Can I use #google-cloud/logging Node.js library to getEntries filtering by date?

I've got this code to getEntries from my project's cloud-logging.
import { Logging } from "#google-cloud/logging";
const PROJECT_ID = "XXXXX";
const logging = new Logging({ projectId: PROJECT_ID });
const getAdminLogEntries = async () => {
const result = await logging.getEntries({
filter: `logName="projects/XXXXX/logs/my-custom-log-name"`,
});
const entryList = result[0];
for (const entry of entryList) {
console.log(`entry.metadata: ${JSON.stringify(entry.metadata)}`);
console.log(`entry.data: ${JSON.stringify(entry.data)}`);
console.log(`---`);
}
};
getAdminLogEntries();
But I'm only getting 6 results (the oldest one is from yesterday). And I guess it's because the query is not going too far back in time. Can it filter it by date? Ex: from 2021-01-01 to 2021-01-31?
Here is what I've found out.
Reference:
https://cloud.google.com/logging/docs/view/advanced-queries
https://cloud.google.com/logging/docs/reference/libraries#list_log_entries
I was able to filter by date with the following code:
import { Logging } from "#google-cloud/logging";
const PROJECT_ID = "XXXX";
const logging = new Logging({ projectId: PROJECT_ID });
const filterItems = [
`logName="projects/XXXXX/logs/admin-logs"`,
`timestamp >= "2021-02-01T00:00:00Z"`,
`timestamp < "2021-03-01T00:00:00Z"`,
`severity="WARNING"`,
];
// JOINING FILTERS WITH "AND" OPERATOR
const filters = filterItems.join(" AND ");
const getAdminLogEntries = async () => {
const result = await logging.getEntries({
filter: filters,
});
const entryList = result[0];
for (const entry of entryList) {
console.log(`entry.metadata.severity: ${JSON.stringify(entry.metadata.severity)}`);
console.log(`entry.metadata.timestamp: ${JSON.stringify(entry.metadata.timestamp)}`);
console.log(`entry.data.message: ${JSON.stringify(entry.data.message)}`);
console.log(`---`);
}
};
getAdminLogEntries();

data is not showing in Dynamodb table

i am working on lex chatbot and want to store user data in dynamodb.
here is my databaseManager.js file code
'use strict';
const { v1: uuidv1 } = require('uuid');
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();
module.exports.saveBookingToDatabase = async function(Arrival_city, Departure_city, Flight_type, Phone_number){
console.log('saveBookingToDatabase');
const item = {};
item.bookingId = uuidv1();
item.arrivalCity = Arrival_city;
item.departureCity = Departure_city;
item.classType = Flight_type;
item.phone = Phone_number;
const params = {
TableName: 'air_stallion',
Item: item
};
try {
let result = await dynamo.put(params)
console.log(`Saving ticket ${JSON.stringify(item)}`);
return item;
} catch(e) {
throw (e)
}
}
Table has been created but data is now showing in table
The values should not be empty, give some default values to prevent null or empty values.
For Example:
const item = {};
item.bookingId = uuidv1();
item.arrivalCity = Arrival_city || "Arr";
item.departureCity = Departure_city || "Dept";
item.classType = Flight_type || "Type";
item.phone = Phone_number || "Phone";
If the values are okay, then try with
let result = await dynamo.put(params).promise()

Resources