Firestore-Cloud Functions get a top layer file name - node.js

Have a nice day. First;
I would like to thank this user.
https://stackoverflow.com/a/52982781/11320646
I want you to know that I'm new to writing code. I have to train a project.
The function of these codes(include link, "solution" tittle) is this:
customers/{id} counts the consumers here and write count here:
metadatas/customers, {count:123456}.
But i want to add counter here:
groups/{groupID}/posts/{postID}
and ı should write count here:
metadatas/{groupID}/counts, {count:123456}
But these codes write:
metadatas/posts, {count:123456}
How to add the groupID layer?
I've tried many things, but I haven't succeeded.
I have successfully done counting with these codes, I have activated the retry option if it fails. I got a great result. At the same time more than 100 post was added and the result is excellent.
my codes are here:
const executeOnce = (change, context, task) => {
const eventRef = firestore.collection('events').doc(context.eventId);
return firestore.runTransaction(t =>
t
.get(eventRef)
.then(docSnap => (docSnap.exists ? null : task(t)))
.then(() => t.set(eventRef, { processed: true }))
);
};
const documentCounter = collectionName => (change, context) =>
executeOnce(change, context, t => {
// on create
if (!change.before.exists && change.after.exists) {
return t
.get(firestore.collection('counts')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: ((docSnap.data() && docSnap.data().count) || 0) + 1
}));
// on delete
} else if (change.before.exists && !change.after.exists) {
return t
.get(firestore.collection('counts')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: docSnap.data().count - 1
}));
}
return null;
});
exports.customersCounter = functions.firestore
.document('denemeicerikler/{grupID}/icerikler/{postID}')
.onWrite(documentCounter('icerikler'));

Related

Node.js: Multiple inputs from user in an interactive CLI

I'm trying to create a simple interactive CLI with Node.js, that collects input from the user by "talking" to them asking questions.
I'm working with the 'readline' module that forces me to use the callback mechanism, currently results in the weird cascade below.
As I'm new to Node.js and to the callback idea, I really feel I'm doing something wrong but not sure how to make it better.
How can I reformat my function so it will be more elegant?
(I know the function below won't return anything, it's just for my temp debugging)
function getUserInput(dbData) {
readline.question('What would you like to edit?\n\n\t(1) Cars\t(2) Data owners\n', choice => {
if (choice == 1) {
readline.question('Choose operation:\n\n\t(1) Add new\n', op => {
if (op == 1) {
let newCar = {};
console.log("--> Please fill in the required details (with Capital First Letter):\n\n");
readline.question("Car name: ", carName => {
newCar.name = carName;
readline.question("Car make: ", carMake => {
newCar.make = carMake;
readline.question("Team (DC/CSF): ", team => {
newCar.team = team;
readline.question("TC (1/2): ", tc => {
newCar.tc = tc;
readline.close();
console.log(newCar);
});
});
});
});
}
else {
console.log("Invalid choice!\n");
getUserInput(dbData);
}
});
}
else {
console.log("Invalid choice!\n");
getUserInput(dbData);
};
});

NodeJS/MongoDB function returning array with wrong data

so I have a collection where I will have a lot of documents, but for now lets suppose it has about 100. (It had less than that when I was testing).
So, I need to get all the documents of the collection, put in a array, sort that and then send it to the websocket for the frontend. But the array is going with wrong data.
This is my code:
const emitSales = async (socket) => {
let salesArray = [];
const saleExists = (contract) => {
return salesArray.some(element => element.contract === contract);
}
const addSale = (contract) => {
const element = salesArray.find(e => e.contract === contract);
element.sales = element.sales+1;
}
const sales = await fiveMinSchema.find({}).lean();
if(sales) {
for await (x of sales) {
if(saleExists(x.contract)) {
addSale(x.contract);
continue;
}
const collection = await Collection.findOne({
contract: x.contract
});
let newsale = {
contract: x.contract,
title: collection.title,
description: collection.description,
image: collection.image,
sales: 1,
}
salesArray.push(newsale);
}
socket.emit("5min", salesArray.sort((a,b) => {
return b.sales-a.sales;
}).slice(0,10));
}
}
So, when I execute this function only once, for example, the array returns the correct values. But if I execute the function like 2 times in a row (like very fast), it starts returning the array with wrong data. (like mixing the data).
And as I using websocket, this function will execute like every 2 seconds (for example). How can I fix this problem? Like it seems to be executing more than one time simultaneously and mixing the data, idk..

Calling Match Template in Promise.All No Performace Improvement?

I am using OpenCV4NodeJS-prebuilt for my project to use match template.
I Created Two Files one being Index.js and other named matchTemlate.js
In Index.js i call match template:
const { matchTemplate } = require("./matchTemplate");
...
let a = async function () {
let tm = performance.now();
try {
await Promise.all([
matchTemplate(baseImage, templateR),
matchTemplate(baseImage, templateL)
]).then(result => {
const c = result.map((ob) => (ob.C)) // confidence
top = c[0] > c[1] ? result[0].Y + 8 : result[1].Y + 11
})
} catch (error) {
console.log(error)
}
tm = performance.now() - tm;
console.log(tm)
}
and this is matchTemplate.js
const cv = require('opencv4nodejs-prebuilt')
exports.matchTemplate = async function (inputFile, templateImage) {
// eslint-disable-next-line no-unused-expressions
const matS = await cv.imdecodeAsync(templateImage)
console.time('templateMatching')
const matched = inputFile.matchTemplate(matS, 3)
console.timeEnd('templateMatching')
const minMax = matched.minMaxLoc()
return ({ Y: minMax.maxLoc.y, C: minMax.maxVal })
}
The log output of matchTemplate is:
templateMatching: 892.648ms templateMatching: 890.387ms
and The Log output of index.js is:
TemplateMatching: 1824.8019220000133
Why there is no improvement is speed ?
while the execution is done in parallel why it's still taking time equal to time taken by both ?
I tried Promise.all method to call ghostscript via gs4fb npm package and convert PDF to Image and the time improvement was there.
By time improvement i mean the difference of total time taken by Promise.all method and calling the functions one by one.

Using batch to recursively update documents only works on small collection

I have a collection of teams containing around 80 000 documents. Every Monday I would like to reset the scores of every team using firebase cloud functions. This is my function:
exports.resetOrgScore = functions.runWith(runtimeOpts).pubsub.schedule("every monday 00:00").timeZone("Europe/Oslo").onRun(async (context) => {
let batch = admin.firestore().batch();
let count = 0;
let overallCount = 0;
const orgDocs = await admin.firestore().collection("teams").get();
orgDocs.forEach(async(doc) => {
batch.update(doc.ref, {score:0.0});
if (++count >= 500 || ++overallCount >= orgDocs.docs.length) {
await batch.commit();
batch = admin.firestore().batch();
count = 0;
}
});
});
I tried running the function in a smaller collection of 10 documents and it's working fine, but when running the function in the "teams" collection it returns "Cannot modify a WriteBatch that has been committed". I tried returning the promise like this(code below) but that doesn't fix the problem. Thanks in advance :)
return await batch.commit().then(function () {
batch = admin.firestore().batch();
count = 0;
return null;
});
There are three problems in your code:
You use async/await with forEach() which is not recommended: The problem is that the callback passed to forEach() is not being awaited, see more explanations here or here.
As detailed in the error you "Cannot modify a WriteBatch that has been committed". With await batch.commit(); batch = admin.firestore().batch(); it's exactly what you are doing.
As important, you don't return the promise returned by the asynchronous methods. See here for more details.
You'll find in the doc (see Node.js tab) a code which allows to delete, by recursively using a batch, all the docs of a collection. It's easy to adapt it to update the docs, as follows. Note that we use a dateUpdated flag to select the docs for each new batch: with the original code, the docs were deleted so no need for a flag...
const runtimeOpts = {
timeoutSeconds: 540,
memory: '1GB',
};
exports.resetOrgScore = functions
.runWith(runtimeOpts)
.pubsub
.schedule("every monday 00:00")
.timeZone("Europe/Oslo")
.onRun((context) => {
return new Promise((resolve, reject) => {
deleteQueryBatch(resolve).catch(reject);
});
});
async function deleteQueryBatch(resolve) {
const db = admin.firestore();
const snapshot = await db
.collection('teams')
.where('dateUpdated', '==', "20210302")
.orderBy('__name__')
.limit(499)
.get();
const batchSize = snapshot.size;
if (batchSize === 0) {
// When there are no documents left, we are done
resolve();
return;
}
// Delete documents in a batch
const batch = db.batch();
snapshot.docs.forEach((doc) => {
batch.update(doc.ref, { score:0.0, dateUpdated: "20210303" });
});
await batch.commit();
// Recurse on the next process tick, to avoid
// exploding the stack.
process.nextTick(() => {
deleteQueryBatch(resolve);
});
}
Note that the above Cloud Function is configured with the maximum value for the time out, i.e. 9 minutes.
If it appears that all your docs cannot be updated within 9 minutes, you will need to find another approach, for example using the Admin SDK from one of your server, or cutting the work into pieces and run the CF several times.

onWrite Aggregate Function

In my very simple app, I keep a running aggregate each time a new transaction is added/changed/deleted. I realised that I could simply loop through each transaction every time but this seems expensive. As such, I switch on the event type and apply a different logic depending if its a create/update/delete.
exports.aggregateTransactions = functions.firestore
.document('budgets/{budgetId}/transactions/{transactionId}')
.onWrite(async (change, context) => {
adjustment = 0;
if(change.after.data == undefined){
adjustment = -1 * change.before.data().amount;
}
if(change.before.data && change.after.data){
adjustment = change.after.data().amount - change.before.data().amount;
}
if(!change.before.data && change.after.data){
adjustment = change.after.data().amount;
}
const budgetRef = db.collection('budgets').doc(context.params.budgetId);
await db.runTransaction(async (transaction) => {
const budgetDoc = await transaction.get(budgetRef);
const newBalance = budgetDoc.data().balance + adjustment;
transaction.update(budgetRef, {
balance: newBalance
});
});
});
This feels like a lot of code each time just to work out what kind of event is happening. Is there a better way to handle this?
Most of the code looks fine to me. I'd just use a FieldValue.increment() operation for updating the budget, instead of a transaction:
const budgetRef = db.collection('budgets').doc(context.params.budgetId);
budgetRef.update({ balance: admin.firestore.FieldValue.increment(adjustment) });
For more on this operation, see the documentation on incrementing a numeric value.

Resources