Trying to update value in firebase database while listening for changes on another place (Cloud Functions) - node.js

I am trying to update the count of the participants in a field in firebase realtime database.
My database looks like this:
root
|_course
| |_Course1
| |_descr: "some text"
| |_lect: "someUID"
|_groups
| |_Course1
| |_Group1
| |_current: 5
| |_others -//-
|_participation
| |_Course1
| |_someUID: "Group1"
So now I'm trying to listen for writings on participation/Course1/someUID and when some record shows up I want to update the 'current' field to 6 in groups/Course1/Group1.
So far I've come with:
exports.afterGroupJoined = functions.database.ref('/participation/{courseId}/{uid}').onWrite((change, context) => {
const writtenContent = change.after.val(); // groupId
const courseId = context.params.courseId;
const uid = context.auth ? context.auth.uid : null;
admin.database().ref('/groups/' + courseId + '/' + writtenContent).once('value').then(snapshot => {
const count = snapshot.val();
const current = count+1;
console.log('User ' + uid + ' joined group ' + writtenContent + ' in course ' + courseId);
const promise1 = admin.database().ref('/groups/' + courseId + '/' + writtenContent + '/current').set(parseInt(current));
return Promise.all([promise1]);
}).catch(err => {
console.log(err);
});
});
But what I get is a new child in groups/Course1/Group1 named 'null' with child 'current' having value '0':
root
|_groups
| |_Course1
| |_Group1
| | |_current: 5
| | |_others -//-
| |_null
| |_current: 0
The value of 'current' should've been 6, but I get a new child.
This is from the log:
User fNhAVZUo9QeSbYt0TwBIQmL2mYq1 joined group Group1 in course Course1
Error: Reference.set failed: First argument contains NaN in property 'groups.Course1.Group1.current' at validateFirebaseData
Any help is appreciated.

It seems like you're reading this JSON:
"Group1": {
"current": 5
}
Which means you need this code to get the actual count:
const count = snapshot.val().current;
But you're not using anything else from the group, so you might as well just read the count itself. The Promise.all() call also seems unneeded, since you have only one promise. So the entire block could be:
let ref = admin.database().ref('/groups/' + courseId + '/' + writtenContent+'/current');
ref.once('value').then(snapshot => {
const count = snapshot.val();
return ref.set(count+1);
}).catch(err => {
console.log(err);
});
Finally: this approach is susceptible to race conditions if multiple users run the above at almost the same time. You might want to consider using a transaction.

Related

Update DB with object and array in node js and pg-promise

I am creating a system to switch accounts without the need to log in every time, just once.
For this I have a session variable, but in case there are 4 sessions, the first 2 with variable 1 and the rest with variable 2 and the user tries to connect all 4, the session variables must be updated again.
What I do is obtain the id of the rows that I want to update and I pass the variable that will remain for all the sessions, but I don't know how to do a where in with an object and an array, can you recommend a better solution.
This is an example, please ignore the connection is wrong.
DATABASE TABLE:
| ID | ID_USER | ID_USER_RELATIONATED | ID_SESSION |
| -- | ------- | -------------------- | ---------- |
| 1 | 1 | 3 | 1 |
| 2 | 1 | 4 | 1 |
| 3 | 2 | 5 | 2 |
| 4 | 2 | 6 | 2 |
const pgp = require('pg-promise')({});
const SessionData = {
ID_SESSION: 1,
ID: [3, 4]
};
let conecctionBD;
try {
conecctionBD = await pgp.connect();; //Obtener una conexión a la base de datos POSTGRESQL
const condition = pgp.as.format(' WHERE "ID" IN (${ID:csv})', SessionData);
let query = pgp.helpers.update(SessionData, null, 'SESSION') + condition;
let id = await conecctionBD.result(query, r => r.rowCount)
.then(count => {
return count
})
.catch(error => {
return error
});
if (id.rowCount > 0) {
return true
} else {
return false
}
}catch (error) {
let date = new Date();
console.error(`${date.getDate()}-${date.getMonth() + 1}-${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}
=> Error: `, error);
}

How to concat string in node js to call column

I wish to make call different column if user using different language, example in one collection I have description_en for English and description_ind for Indonesia.
The problem if I concat it is not working.
This is example code
let lang = req.params.lang;
order_status_description:element.orderstatus.description + lang,
this is my complete code
const MyOrderResource = (req,res,next)=>{
let lang = req.params.lang;
let arrayResult = [];
req.order.forEach(element => {
let arrayItem =[];
element.orderitem.forEach(item=>{
arrayItem.push({
_id:item._id,
product_id:item.product_id,
product_name:item.product_name,
description:item.description,
unit:item.unit,
price:item.price,
fprice:'Rp ' + new Intl.NumberFormat().format(item.price),
quantity:item.quantity,
amount:item.amount,
fprice:'Rp ' + new Intl.NumberFormat().format(item.amount),
});
});
arrayResult.push({
_id:element._id,
user_id:element.user_id,
date:element.date,
store_id:element.store_id,
store_name:element.profile[0].name,
store_address:element.profile[0].address,
total:element.total,
ftotal:'Rp ' + new Intl.NumberFormat().format(element.total),
order_status_code:element.orderstatus.code,
order_status_description:element.orderstatus.description + lang,
order_item:arrayItem,
});
});
res.status(201).json({
data:arrayResult,
status : 200,
})
}
module.exports = MyOrderResource;
For this code, I wish to call, column description_en, but the result is undefined
Thanks for helping
You can use something like
order_status_description : element.orderstatus["description" + lang]
In the posted snipet, it instructs to concat the value of key- 'element.orderstatus.description' to the value of key - 'lang'

Is there a way to store the last changes on real time database?

I'm using the real time database with the following data structure. I can add more machines in the webapp that attributes an autogenerated ID and all the machines has the same data structure.
machines
-autogeneratedID1
-id1 : 1
-id2 : 2
-autogeneratedID2
-id1 : 4
-Id2 : 3
I want to track the changes on the id inside the autogeneratedIDs, if the id1 on the autogeneratedID1 changes from 1 to 3, I want something that returns the change with a timestamp.
I'm trying to use cloud functions with the following code:
exports.foo = functions.database.ref("machines/").onUpdate((change) => {
const before = change.before.val(); // DataSnapshot before the change
const after = change.after.val(); // DataSnapshot after the change
console.log(before);
console.log(after);
return null;
but the before and after objects returns me the JSON of all the strucutre of the database.
My first guess was to compare the 2 JSON objects and detect where the changes were and add a timestamp. Then I want to store the changes in the database.
Is there a way to do this?
Best Regards
Found the answer finally, here it is the code that I'm using:
exports.foo = functions.database.ref("machines/{machineID}/{ValueID}").onUpdate((change,context) => {
const before = change.before.val(); // DataSnapshot before the change
const after = change.after.val(); // DataSnapshot after the change
const machineID = context.params.machineID; //Machine ID
const valueID = context.params.ValueID; //Value ID
console.log("MachineID: " + machineID + " " +"IDChanged: " + valueID + " " + "BeforeValue: " + before +" "+ "AfterValue: " + after);
return null;
});

Why does this work? Spotify API call node.js

I am new to node and am working on an API call via node.js and am kinda confused why this works. I have done other API calls via node with ease as it was easy to figure out how to target the various fields etc.. but I never got a link with the spotify API and am confused how data.tracks.items.artists.name gave me the artist name?
I know this is an ignorant question but I really want to understand how this works not just make it work.
function song() {
var nodeArgs = process.argv;
var SongName = "";
for (var i = 3; i < nodeArgs.length; i++) {
if (i > 3 && i < nodeArgs.length) {
SongName = SongName + "+" + nodeArgs[i];
}
else {
SongName += nodeArgs[i];
}
}
var Spotify = require('node-spotify-api');
var spotify = new Spotify({
id: "id",
secret: "secret"
});
spotify.search({ type: 'track', query: SongName, limit: 1 }, function (err, data) {
if (err) {
SongName = "";
console.log("Artist: " + songData.artists[0].name);
console.log("Song Title: " + songData.name);
console.log("Preview Track: " + songData.preview_url);
console.log("Album: " + songData.album.name);
song();
}
for (var i = 0; i < data.tracks.items.length; i++) {
var songData = data.tracks.items[i];
console.log("Artist: " + songData.artists[0].name);
console.log("Song Title: " + songData.name);
console.log("Preview Track: " + songData.preview_url);
console.log("Album: " + songData.album.name);
}
});
}
Short Answer -
the track api endpoint responds with the Object Model which also contains artist objects - which is an array of artist objects, where the artist object contains the key name.
ref: https://developer.spotify.com/documentation/web-api/reference/tracks/get-track/
From their API docs
GET https://api.spotify.com/v1/tracks/{id}
the response object contains
KEY VALUE | TYPE | VALUE DESCRIPTION
---
artists | an array of simplified | The artists who performed the track.
| artist objects | information about the artist.
Artist Object
artist object (simplified)
KEY VALUE | TYPE | VALUE DESCRIPTION
---
external_urls | an external URL object | Known external URLs for this artist.
href | string | A link to the Web API endpoint providing full details of the artist.
id | string | The Spotify ID for the artist.
name | string | The name of the artist
type | string | The object type: "artist"
uri | string | The Spotify URI for the artist.

Cloud Functions get list from Realtime Database path

I'm trying to retrieve a list of objects from a path. The document structure is shown below:
-- device
|
-- {id}
|
...
-- user:
|
-- {pushId}: <user id x>
|
...
|
-- {pushId}: <user id z>
|
...
|
The code below is how I'm trying to get all user IDs, but I'm not getting a list, only getting the snapshot.
const getUserIdsPromise = admin.database().ref(dbVersionRef + `/device/${id}/user/{pushId}`).once('value');
return Promise.all([anotherPromise, getUserIdsPromise]).then((results) => {
const userIds = results[1].child();
// No id is logged
userIds.forEach((id) => {
debug.log('userId: ' + id);
});
You're reading /device/${id}/user/{pushId}, which means that you're reading a single user.
If you want to get all users for a single device, you should read from /device/${id}/user:
admin.database().ref(dbVersionRef + `/device/${id}/user/`)
.once('value')
.then((results) => {
results.forEach((snapshot) => {
debug.log('userId: ' + snapshot.val());
})
});
If you want to get devices, you should read from /device.

Resources