Google Cloud Functions Add values to a map in firestore - node.js

This is my code to add values to a map in firestore. At the moment the map gets overwritten everytime
exports.addTrainingtoList = functions.firestore
.document('/users/{uid}/trainings/{trainings}')
.onCreate((change, context) => {
const doc = admin.firestore().collection('users').doc(context.params.uid).collection('user').doc('trainings');
const trainingsID = change.data().trainingsID;
const trainingsName = change.data().training;
const trainingsTimestamp = change.data().timestamp;
doc.update({
Trainings: {
...doc.Trainings,
[trainingsID]: {
'trainingsName': trainingsName,
'trainingsDate': trainingsTimestamp,
},
},
})
});
This is my updated code after Franks answer:
exports.addTrainingtoList = functions.firestore
.document('/users/{uid}/trainings/{trainings}')
.onCreate((change, context) => {
const doc = admin.firestore().collection('users').doc(context.params.uid).collection('user').doc('trainings');
const trainingsID = change.data().trainingsID;
const trainingsName = change.data().training;
const trainingsTimestamp = change.data().timestamp;
const nameField = trainingsID + ".trainingsName";
const timestampField = trainingsID + ".trainingsDate";
doc.update({
Trainings: {
...doc.Trainings,
[nameField]: trainingsName,
[timestampField]: trainingsTimestamp,
},
})
});
Currently the map is overwritten.
Here u can see what I get:
Here u can see what I want to have.
Thanks in advance.

To add nested fields in an object use dot notation like this:
const nameField = trainingsID + ".trainingsName";
const timestampField = trainingsID + ".trainingsDate";
doc.update({
Trainings: {
...doc.Trainings,
[nameField]: trainingsName,
[timestampField]: trainingsTimestamp,
},
})
Since you updated your question to show what you want to get, that can be accomplished with:
const trainingsID = change.data().trainingsID + Date.now();
And then your original code to update the document.

Finally with Franks help I found a solution for my problem.
exports.addTrainingtoList = functions.firestore
.document('/users/{uid}/trainings/{trainings}')
.onCreate((change, context) => {
const doc = admin.firestore().collection('users').doc(context.params.uid).collection('user').doc('trainings');
const trainingsID = change.data().trainingsID;
const trainingsName = change.data().training;
const trainingsTimestamp = change.data().timestamp;
const nameField = `trainings.${trainingsID}.trainingsName`;
const dateField = `trainings.${trainingsID}.trainingsDate`;
doc.update({
[nameField]: trainingsName,
[dateField]: trainingsTimestamp,
})
});
This is the result:
If you finally want to delete values out of the map you can use:
exports.delTrainingtoList = functions.firestore
.document('/users/{uid}/trainings/{trainings}')
.onDelete((change, context) => {
const doc = admin.firestore().collection('users').doc(context.params.uid).collection('user').doc('trainings');
const trainingsID = change.data().trainingsID;
const trainingsName = change.data().training;
const trainingsTimestamp = change.data().timestamp;
const trainingsField = `trainings.${trainingsID}`;
doc.update({
[trainingsField]: FieldValue.delete(),
})
});

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.

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()

TypeError: db.transaction is not a function

const db = admin.firestore();
exports.aggregateRatings = functions.firestore
.document('destinations/{destId}/reviews/{reviewId}')
.onWrite(event => {
const reviewRating = event.data.get('reviewRating');
const destinationId = event.params.destId;
const destRef = db.collection('destinations').doc(destinationId);
return db.transaction(transaction => {
return transaction.get(destRef).then(destDoc => {
const numberOfReviews = destDoc.data('numberOfReviews') + 1;
var oldRatingTotal = destDoc.data('destinationRating') * destDoc.data('numberOfReviews');
var newAvgRating = (oldRatingTotal + reviewRating) / numberOfReviews;
return transaction.update(destRef, {
destinationRating: newAvgRating,
numberOfReviews: numberOfReviews
});
});
});
});
When the firebase function is called i get this error, but I have no idea why this is happening..Could someone help me? Any help would be appreciated!
The method name you're looking for is runTransaction (not "transaction").

Resources