Firestore transaction update multiple document - firebase

I want to update documents in one collection.
my_collection
document_1
field_1
field_2
document_2
field_1
field_2
My code:
exports.aggregateUsers =
functions.firestore.document('users/{userId}').onWrite(function(change,
context) {
const document = change.after.exists ? change.after.data() : null;
const oldDocument = change.before.data() || null;
return firestore.runTransaction(function(transaction) {
var oldInstanceRef;
var instanceRef;
var oldInstanceDoc;
var instanceDoc;
if (document != null) {
instanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(document.instance);
instanceDoc = transaction.get(instanceRef);
var newNumberOfUsers = (instanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newNumberOfUsers });
}
if (oldDocument != null) {
oldInstanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(oldDocument.instance);
oldInstanceDoc = transaction.get(oldInstanceRef);
var newPrevNumberOfUsers = (oldInstanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newPrevNumberOfUsers });
}
}).catch(function(error) {
console.log("invalid-argument", error.code, error.message);
});
});
Errors:
instanceDoc.data is not a function
I am using transcation in cloud function to aggregate number of users, not using distributed counter, because of low traffic app. My question is how to update field's value in each document? Thank you

I guess you forgot the async/await.
Try this:
async before transaction parameter
await before transaction.get(<some_ref_here>)
return firestore.runTransaction(async transaction => {
var oldInstanceRef;
var instanceRef;
var oldInstanceDoc;
var instanceDoc;
if (document != null) {
instanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(document.instance);
instanceDoc = await transaction.get(instanceRef);
var newNumberOfUsers = (instanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newNumberOfUsers });
}
if (oldDocument != null) {
oldInstanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(oldDocument.instance);
oldInstanceDoc = await transaction.get(oldInstanceRef);
var newPrevNumberOfUsers = (oldInstanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newPrevNumberOfUsers });
}
}).catch(function(error) {
console.log("invalid-argument", error.code, error.message);
});

Related

Return created item key

My app creates a new item, and I want to retrieve the key to use in a server script. The data variable returns null though. This is what I have:
function addItem(addButton) {
var addItemPage = addButton.root;
if (!addItemPage.validate()) {
return;
}
var props = addItemPage.properties;
var itemDs = addItemPage.datasource;
props.Creating = true;
itemDs.saveChanges({
success: function(key) {
props.Creating = false;
if (app.currentPage !== app.pages.EditItem) {
return;
}
var newProjectItem = itemDs.item;
newProjectItem._loadHistory();
gotoEditItemPage(newProjectItem._key, true);
return newProjectItem;
},
failure: function(error) {
props.Creating = false;
console.error(error);
}
});
gotoEditItemPage();
var data = app.datasources.ProjectItems.item._key;
google.script.run.withSuccessHandler(function(value){
alert("Created");
}).createDoco(data);
}
This is not neat by any means, but I fixed it by creating a new function:
function addItem(addButton, key) {
var addItemPage = addButton.root;
if (!addItemPage.validate()) {
return;
}
var props = addItemPage.properties;
var itemDs = addItemPage.datasource;
props.Creating = true;
itemDs.saveChanges({
success: function() {
props.Creating = false;
if (app.currentPage !== app.pages.EditItem) {
return;
}
var newProjectItem = itemDs.item;
newProjectItem._loadHistory();
gotoEditItemPage(newProjectItem._key, true);
var key = newProjectItem._key;
value(key);
},
failure: function(error) {
props.Creating = false;
console.error(error);
}
});
gotoEditItemPage();
function value(record){
var data = record;
google.script.run.withSuccessHandler(function(value){
alert("Created");
}).createDoco(data);
}
}

Firebase https cloud function to send notifications always ends in timeout

Function execution took 60002 ms, finished with status: 'timeout'
I have cloud function that collects users product & price data, hits an external API to get the latest prices and sends a notification if the price has changed. I've read several similar questions like this but I am in fact sending a response back to the client. I've tried increasing the timeout to 120 seconds, but it still times out. So, I think there might be an issue with how I'm returning promises? Any suggestions?
Here is my code:
exports.pushTestWithRP = functions.https.onRequest((req, res) => {
var uidsAndTokens = [];
ref.child('tokens').once('value').then(snap => {
snap.forEach(childSnap => {
uidsAndTokens.push({
uid: childSnap.key,
deviceToken: childSnap.val()
});
});
return uidsAndTokens;
}).then((uidsAndTokens) => {
var uidsAndCruises = [];
ref.child('watchlist-items/users').once('value').then(snap => {
snap.forEach(childSnap => {
var uid = childSnap.key;
childSnap.forEach(childChildSnap => {
var product = childChildSnap.key;
var productWatchInfo = childChildSnap.val();
uidsAndProducts.push({
uid: uid,
product: product,
watchInfo: productWatchInfo
});
}); // end childChildSnap
}); // end childSnap
return uidsAndProducts;
}).then((uidsAndProducts) => { // end snap watchlist-items/users
var uidsOnly = [];
for (var i=0; i<uidsAndTokens.length; i++) {
uidsOnly.push(uidsAndTokens[i].uid);
}
// user has a FCM token
var uidsAndProductsWithTokens = [];
for (var i=0; i<uidsAndProducts.length; i++) {
//check if exists in tokens array
var currUid = uidsAndProducts[i].uid;
if (uidsOnly.includes(currUid)) {
//console.log('this uid has a token: ', currUid);
uidsAndProductsWithTokens.push(uidsAndProducts[i]);
} else {
//this uid does NOT have a token
}
}
function getTokenForUid(uid) {
for (var i in uidsAndTokens) {
if (uidsAndTokens[i].uid == uid) {
var deviceToken = uidsAndTokens[i].deviceToken;
break;
}
}
return deviceToken;
}
var allPromises = [];
// call API only for uids with tokens
for (var i=0; i<uidsAndProductsWithTokens.length; i++) {
const product = uidsAndProductsWithTokens[i].product;
const uid = uidsAndProductsWithTokens[i].uid;
const deviceToken = getTokenForUid(uid);
const pDates = uidsAndProductsWithTokens[i].watchInfo.pDates;
const pName = uidsAndProductsWithTokens[i].watchInfo.pName;
getCurrentPricesFromAPI(product).then((response) => {
if (typeof response.response != 'undefined') {
const productId = response.response.product.product_id;
const allPrices = response.response.prices;
const promises = [];
// parse thru prices and send notifications
for (var date in pDates) {
// get all current prices and sort by price to get cheapest
var cheapest = [];
for (var i = 0; i < allPrices.length; i++) {
if (allPrices[i].data[productId][date] && allPrices[i].data[productId][date].hasOwnProperty('Inside')) {
const iPrice = allPrices[i].data[productId][date].Inside;
cheapest.push(iPrice);
}
}
if (cheapest[0] > 0) {
cheapest = cheapest.sort(function (a, b) { return a - b; });
if (sDates[date].hasOwnProperty('Inside')) {
const priceDiff = cheapest[0] - sDates[date].Inside.price;
if (priceDiff < -10) {
const payload = {
notification: {
title: pName + ' Price DROP Alert!',
body: 'prices for the ' + date + ' are $' + cheapest[0] + ' (DOWN $' + Math.abs(priceDiff) + ')',
sound: 'default'
}
};
promises.push(admin.messaging().sendToDevice(deviceToken, payload));
}
else if (priceDiff > 10) {
const payload = {
notification: {
title: pName + ' Price Hike Alert',
body: 'prices for the ' + date + ' are $' + cheapest[0] + ' (UP $' + priceDiff + ')',
sound: 'default'
}
};
promises.push(admin.messaging().sendToDevice(deviceToken, payload));
}
}
}
}
allPromises = allPromises.concat(promises);
}
}) // end handle API response
} // end for loop
return allPromises;
}).then((allPromises) => {
return Promise.all(allPromises);
res.send('got tokens and ids');
}).catch(error => {
res.send('there was an error');
});
}); // end uidsAndTokens
}); // end function
I can't figure this out and would appreciate any help!

Cordova SQLite class - cannot return value from database

So, I'm building an app with Cordova and SQLite and below is the object Storage I created to handle the database properties and methods.
function Storage(connection, platform)
{
if (platform == 'mobile') {
this.db = window.sqlitePlugin.openDatabase({name: connection.name, location: connection.location});
} else if (platform == 'web') {
this.db = window.openDatabase(connection.name, connection.version, connection.mode, -1);
}
this.read = function(sql, vals) {
var rows = null;
this.db.transaction(function(tx) {
tx.executeSql(sql, vals, function(tx, res) {
rows = res.rows;
});
}, function(error) {
console.log('Transaction error: '+error.message);
});
return rows;
};
};
var connection = {
name: 'database.db',
version: '1.0',
mode: 'Development'
};
var db = new Storage(connection, 'web');
var sql = '';
sql += 'SELECT *';
sql += ' FROM countdown';
sql += ' WHERE countdown_status != ?;';
var rows = db.read(sql, [1]);
console.log(rows);
This code logs 'null' and I don't know why since I have data into my database. And when I try to log from within the tx.executeSql method, it logs the data from database.
Any ideas on why I cannot get this data out of that function?
Thanks a lot.
the db read is asynchronous - it needs to return the data to a callback function like so:
getHighScore: function (type,callback) {
var query = "SELECT Value FROM HighScore where Type = '" + type + "';";
playedDb.transaction(function (tx) {
tx.executeSql(query, [], function (tx, res) {
var out;
if (typeof res.rows.item(0) === "undefined") {
if (typeof callback === "function") {
callback(-1);
return;
};
} else {
out = res.rows.item(0).Value
//task 301 only allow alphanumeric and decimal point (for version)
out = String(out).replace(/[^0-9a-z\.]/gi, '');
if (typeof callback === "function") {
callback(out);
return;
};
}
}, function (e) {
console.log("getHighScore FAILED: " + e.message);
if (typeof callback === "function") {
callback(-2);
return;
};
});

NIghtmare error in web scraping course details

Hi i am using nightmare for scrape data from website and also course details. I occur a issue :-
err: { message: 'navigation error',
code: 0,
details: 'OK',
url: 'https://www.myskills.gov.au/courses/details?Code=CHC14015' }
on each url traversal. Please suggest me to resolve this:
var Nightmare = require('nightmare')
var vo = require('vo')
var fs = require('fs')
var filesystem = require('file-system')
// var nightmare = Nightmare({show:true});
var sleep = require('sleep');
vo(run)(function(err, result) {
if (err) throw err
console.log("Hi");
})
function *run() {
var nightmare = Nightmare({show:true});
console.log("1st step *run");
yield nightmare
.goto('https://www.myskills.gov.au/courses/search/')
.wait(12000)
//.click('#select3 value="100")
.evaluate(function () {
var hrefs = [];
$('.search-result h4 a').each(function()
{
var course = new Object();
course.title = $(this).html();
course.link = $(this).attr('href');
course.code = $('.search-result .col-md-2.text-small
span').html();
hrefs.push(course);
});
return hrefs;
})
.then(function(str){
console.log("2nd step evaluate then on page load");
console.log(str);
for(var i=0; i< 8; i++)
{
console.log(i);
sleep.sleep(1);
var link = "https://www.myskills.gov.au";
var coursetitle = str[i].title
var courselink = link +str[i].link+"\n";
var coursesinglelink = link +str[i].link;
var courseData = coursetitle+"\n"+str[i].code+"\n"+courselink;
fs.appendFile('getcourselink.txt', courseData, function (err) {
if(err) console.log(err);
});
vo(run1)(function(err, result) {
if (err) console.log ("err: ",err);
console.log("Hi in run1");
//console.log(result);
});
function *run1() {
console.log("I 2nd time:-"+i);
console.log(coursesinglelink);
sleep.sleep(2);
var nightmare1 = Nightmare({show:true});
yield nightmare1
.goto(coursesinglelink)
.wait(9000)
.evaluate(function () {
var str="Hi";
var CourseDetails = $('#details #courseStructureDiv
#packagingrules').text();
str = str+"\n"+CourseDetails;
return str;
})
.then(function(str){
console.log("Run inner then",str);
fs.appendFile('getcourselink.txt', str, function (err) {
if(err) console.log(err);
});
// nightmare1.end();
});
}
//nightmare.end();
// nightmare.proc.disconnect();
// nightmare.proc.kill();
// nightmare.ended = true;
// nightmare = null;
}
});
}

How to update a document in Documentdb using queries?

How to update a document in Document db using queries ( basically want to update a document using a stored procedure)?
The following sample might be what you need: https://github.com/aliuy/documentdb-serverside-js/blob/master/stored-procedures/update.js.
Here's a simplified version:
function updateSproc(id, update) {
var collection = getContext().getCollection();
var collectionLink = collection.getSelfLink();
var response = getContext().getResponse();
tryQueryAndUpdate();
function tryQueryAndUpdate(continuation) {
var query = {query: "select * from root r where r.id = #id", parameters: [{name: "#id", value: id}]};
var requestOptions = {continuation: continuation};
var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, documents, responseOptions) {
if (err) throw err;
if (documents.length > 0) {
tryUpdate(documents[0]);
} else {
throw new Error("Document not found.");
}
});
}
function tryUpdate(document) {
var requestOptions = {etag: document._etag};
var fields, i;
fields = Object.keys(update);
for (i = 0; i < fields.length; i++) {
document[fields[i]] = update[fields[i]];
}
var isAccepted = collection.replaceDocument(document._self, document, requestOptions, function (err, updatedDocument, responseOptions) {
if (err) throw err;
response.setBody(updatedDocument);
});
}

Resources