Loop through a firebase realtime database - javascript

I am new to programming.
I have a firebase realtime database. My intent is to get data from the realtime database when a condition is met. That means using a loop statement.
Here is the code:
function firestore(agent) {
var firebaseEntity = agent.parameters.firebase;
return admin.database().ref('questions').once("value").then((snapshot) => {
var questionList = snapshot.child("Entity").val();
for (const i in questionList) {
if (questionList[i].entity == firebaseEntity);
var response = questionList[i].response;
agent.add(`${response}`);
}
});
}
I want to return 'response' when the user's input matches the 'question' column:

There are a few problems with your code:
There is no Entity node under /questions, so your questionList will always be empty.
There is not even an Entity node under each individual question, as it's called entity with a lowercase e.
I recommend using Firebase's built-in forEach function to iterate over the child nodes, instead of for... in.
So with those:
function firestore(agent) {
var firebaseEntity = agent.parameters.firebase;
return admin.database().ref('questions').once("value").then((snapshot) => {
snapshot.forEach((questionSnapshot) => {
let question = questionSnapshot.val();
if (question.entity == firebaseEntity) {
cont response = question.response;
agent.add(`${response}`);
}
});
});
}

Related

Trouble with Addition Assignment in Array from Firebase

I have a scenario where i need to query multiple collections at once and retrieve the values based on the collection name. I use Promise.all to do so and it works accordingly like so
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("collection1").where("user_id", "==", uid).get(),
admin.firestore().collection("collection2").where("user_id", "==", uid).get(),
admin.firestore().collection("collection3").where("user_id", "==", uid).get(),
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "collection1") {
qs.docs.map((doc) => {
valuesArr1.push(doc.data().arr);
});
} else if (qs.query._queryOptions.collectionId == "Collection2") {
qs.docs.map((doc) => {
valuesArr2.push(doc.data());
});
} else if (qs.query._queryOptions.collectionId == "collection3") {
qs.docs.map((doc) => {
valuesArr3.push(doc.data());
});
}
} else {
return
}
});
for (var i=0; i < valuesArr1.length; i++) {
if (valuesArr1[i].desiredData) {
console.log('datas from for loop on datas array', valuesArr1[i].desiredData)
globalVariable += `<img src="${valuesArr1[i].desiredData}">`;
}
}
Once I do this I map the query snapshot I get and am able to retrieve the values up to this point like so
From the first collection I retrieve an array from a firestore document and then the following collections i just retrieve all documents from the collections. This all 'works' in that when I console.log into the functions console the data shows up exactly as expected. It's only when I want to iterate over the data and assign the results to a global variable to use elsewhere that strange behavior occurs.
The console.log shows the desired data in the functions console with no issues, but the output when I interpolate that data into the html and send it off in nodemailer I get the following result
undefined is always the first in the response when i use the += addition assignment operator, but if i just use the = assignment operator there's no undefined but I obviously don't get all the data I'm expecting.
There are no undefined values or documents in the collections that I'm retrieving, I've checked thoroughly and even deleted documents to make sure of it. After days of researching I've come to the conclusion it has to do with the asynchronous nature of the promise I'm working with and the data not being immediately ready when I iterate it.
Can someone help me understand what I'm doing wrong and how to fix it in node?
I figured out a solution to my problem and would like to share it in hopes it saves a future viewer some time.
Before, I was storing the results of the array from Firebase inside a global variable. To save some head scratching I'll post the code again below.
var globalVariableArray = []
var globalVariable
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("DataCollection").where("user_id", "==", uid).get()
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "DataCollection") {
Promise.all(
qs.docs.map(doc => {
globalVariableArray = doc.data().arrayWithDesiredData;
})
);
}
else {
return
}
});
globalVariableArray.map(gv => {
globalVariable += `<p>gv.desiredData</p>` // <--- Right here is where the problem area was
})
var mailOptions = {
from: foo#blurdybloop.com,
to: 'bar#blurdybloop.com
subject: 'Almost but not quite',
html: `${globalVariable}`
};
The above code give the expected output, but the output would always have undefined first before the data showed. This happened no matter how the array from Firebase was iterated over.
After strengthening my Google-Fu, I worked out the following solution
var globalVariableArray = []
var globalVariable
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("DataCollection").where("user_id", "==", uid).get()
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "DataCollection") {
Promise.all(
qs.docs.map(doc => {
globalVariableArray = doc.data().arrayWithDesiredData;
})
);
}
else {
return
}
});
var mailOptions = {
from: foo#blurdybloop.com,
to: 'bar#blurdybloop.com
subject: 'It works!!',
html: `${globalVariableArray.map(dataIWantedAllAlong => <p>dataIWantedAllAlong.desiredData</p> )}` <--- Here I simply loop through the array inside the interpolation blocks and voila! no more undefined showing up in the results
};
I perform the loop inside the brackets where I interpolate the dynamic data and am no longer getting that pesky undefined showing up in my emails.
Safe travels and happy coding to you all!

How to know when real time update ends - Cloud Firestore ionic 4 with angular

I'm using cloud firestore for my DB, having no problems with read and writes. My issue is that i don't understand how to know when real time update has ended!
I'm using real time update as described in official cloud firestore documentation. It returns a function instead an observable. Now i'm not sure how to use it correctly.
I need to execute some code after data is loaded, but i have no subscribe((data) => {...}) to put it there!!
How to do it?
If i show lack of knowledge, please guide me to some documentation.
Thanks
This code is working fine and i'm using it directly on html like ListService.places$ to access data array.
public places$: BehaviorSubject<Array<Place>> = new BehaviorSubject<Array<Place>>([]);
private unsubscribe: Function;
public list(city: string, country: string) {
return this.unsubscribe = this.firestore.firestore.collection('places')
.where("location.country", "==", country)
.where("location.city", "==", city)
.orderBy("startDate", "desc")
.onSnapshot(querySnapshot => {
const list: Place[] = [];
if(!querySnapshot.docs.length) {
this.places$.next(list);
} else {
querySnapshot.docs.forEach(doc => {
let places = new Place();
place = doc.data() as Place;
places.push(place);
if(list.length === querySnapshot.docs.length) {
this.places$.next(list);
}
});
}
});
}
You need to subscribe to this.places$
this.places$.subscribe((data) => {
console.log(data);
});
What the real time function returns does not metter, because the real time function adds the output to the BehaviorSubject.
See here
let places = new Place();
place = doc.data() as Place;
places.push(place);
if(list.length === querySnapshot.docs.length) {
this.places$.next(list);
}
That means if you want to do something with the data you just need to subscribe to places$.
Also i think you have a small typo there, you never pushed values to the list array.
It should be something like:
...
.onSnapshot(querySnapshot => {
const list: Place[] = [];
if(!querySnapshot.docs.length) {
this.places$.next(list);
} else {
querySnapshot.docs.forEach(doc => {
let place = new Place();
place = doc.data() as Place;
list.push(place);
if(list.length === querySnapshot.docs.length) {
this.places$.next(list);
}
});
}
});

How to use a value received from one service in another service within the same Angular 6 component?

The following component's code is from an Angular 6 web application that I am creating. The app displays a table with CRUD functionality. I have an Angular service called GetDBValuesService that is connected to a database and uses DBValues() to retrieve an array of arrays (each inner array contains the values of a given row in the database). My code then collects rows whose 'Number' attribute is equal to 10. These rows are then used by my EventEmitter dataItems, which allows them to be displayed in my web page's CRUD table.
I have created another Angular service called DataService that receives an integer value from another component and sends that value to the shown component (after being subscribed to). I subscribed to this service in the following code and let an instance of gotdata (a public var declared in this component) receive the service's value. However, when I try to use this instance outside of that subscription (to replace the hardcoded 10 described above), this.gotdata is undefined.
How can I modify my code so that I can use the value given by the DataService service in the GetDBValuesService service? Currently, the below code does work due to the hardcoded 10, but does not if I remove that line. Thank you for taking the time to read this.
This is the portion of my CRUD component:
refresh = () => {
this.DataService.DataID$.subscribe((data) => {
this.gotdata = data;
console.log(this.gotdata); //10 (value from console)
});
console.log(this.gotdata); //undefined (value from console)
this.gotdata = 10; //hardcoded value allows further functionality, will be removed when this.gotdata retains its value from the above subscription
if (this.gotdata != null) {
this.GetDBValuesService.DBValues().subscribe((result) => {
var a = 0;
for (var i = 0; i < result.length; i++) {
if (result[i].Number == this.gotdata) {
this.info[a] = result[i];
a = a + 1;
}
}
this.dataItems.next(this.info); //sets rows to be displayed in the web page's table (used by component's HTML file)
});
}}
this.gotdata is undefined because the data is not resolved yet.
refresh = () => {
this.DataService.DataID$.subscribe((data) => {
this.gotdata = data;
console.log(this.gotdata); //10 (value from console)
if (this.gotdata != null) {
this.GetDBValuesService.DBValues().subscribe((result) => {
var a = 0;
for (var i = 0; i < result.length; i++) {
if (result[i].Number == this.gotdata) {
this.info[a] = result[i];
a = a + 1;
}
}
this.dataItems.next(this.info); //sets rows to be displayed in the web page's table (used by component's HTML file)
});
}}
});
Or you can put it inside subscription on complete callback:
this.service.subscribe((data) => {
// code here
},
(error) => console.error(error),
() => {
// do stuff.
});
The problem is that, at the time when you are calling the console.log(...) and the code below, data from the dataID$observable are still on way to you. ( Why do u need to work with observables?)
The best approach for this would to be use the RXJS switchMap operator (what is switchMap?). Because as I see you want to subscribe to the first observable and after that subscribe to another observable. So it can be done this way:
refresh = () => {
this.DataService.DataID$.pipe(switchMap(data: any) => {
if (data) {
this.gotdata = data;
return this.GetDBValuesService.DBValues();
} else {
return of(null); // if 'data' are null, return "empty" observable
}
})).subscribe((result: any) => {
if (!result) {
return; // return if 'result' is null (so the code bellow won't be executed)
}
var a = 0;
for (var i = 0; i < result.length; i++) {
if (result[i].Number == this.gotdata) {
this.info[a] = result[i];
a = a + 1;
}
}
this.dataItems.next(this.info);
});

Object created from a loop in JavaScript, how to analyse them in a json

I'm a begginer in Javascript and I need to analyse a JavaScript Object generated in a loop to keep one parameter and to save this parameter for all object generated in the loop.
This is my program
var onvif = require('onvif');
var fs = require('fs');
var nombrecamera=0;
var taille=0;
var test ='';
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
var STREAM = fs.createWriteStream('STREAM.txt',{flags:'r+'});
onvif.Discovery.on('device', function(cam,rinfo,xml){
// function will be called as soon as NVT responses
nombrecamera+=1;
console.log(cam);
test += cam;
cam2= JSON.stringify({cam}, null , ' ');
//console.log(cam2);
STREAM.write(cam2);
console.log(test);
});
onvif.Discovery.probe({timeout:1000,resolve:false});
And in output in my example i've got 4 of these:
{ probeMatches:
{ probeMatch:
{ endpointReference: [Object],
types: 'tdn:NetworkVideoTransmitter',
scopes: ' onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/location/country/china onvif://www.onvif.org/type/network_video_transmitter onvif://www.onvif.org/hardware/IPC-122 onvif://www.onvif.org/Profile/Streaming onvif://www.onvif.org/name/IPC-BO',
XAddrs: 'http://192.168.1.81:10004/onvif/device_service',
metadataVersion: 1
}
}
}
And I want to keep only the XAddrs for all object generated and then put these in a json.
My first idea was to stringify this object then create a writable stream and put all json together but in this case there are no coma between the json so it doesn't create a big json with the whole data.
Thank you for your help
Jules
The easiest way to know how many addresses you have is the .length function of an array.
As I don't know whether you need a list with unique addresses or the same address can show up multiple times, I'm gonna show you both solutions.
Unique Addresses Only
function extract() {
test.forEach(cam => {
const deviceAddress = cam.probeMatches.probeMatch.XAddrs;
// only if the xaddrs is not in list yet, add it
if(test.filter(xad => xad === deviceAddress).length <= 0) {
xaddrs.push(cam.probeMatches.probeMatch.XAddrs);
}
});
// show the number of addresses
const listCount = xaddrs.length;
console.log('listCount: ', listCount);
}
No Unique Address
function extract() {
test.forEach(cam => {
xaddrs.push(cam.probeMatches.probeMatch.XAddrs);
});
// show the number of addresses
const listCount = xaddrs.length;
console.log('listCount: ', listCount);
}
Make testan array and push()the camobjects into it. Also define an array for your XAddrs-values.
var test = [];
var xaddrs = [];
// your other code
...
onvif.Discovery.on('device', function(cam,rinfo,xml){
// function will be called as soon as NVT responses
nombrecamera+=1;
console.log(cam);
// push cam object into array
test.push(cam);
cam2= JSON.stringify({cam}, null , ' ');
//console.log(cam2);
STREAM.write(cam2);
console.log(test);
});
Then extract XAddrs and push it into xaddrs array.
function extract() {
test.forEach(cam => {
xaddrs.push(cam.probeMatches.probeMatch.XAddrs);
});
// now you have an array containing only the XAddrs elements
console.log(xaddrs);
}

Meteor: Underscore _findWhere iteration through loop objects only works in chrome console, in app it says undefined

I'm trying to fetch an object 'single Post' within an object 'Posts' from a json file within meteor, which looks like this.
I found an effective way of doing it, using underscore findWhere to get to it. this is the code
_.findWhere(_.findWhere(CategoryCollection.find().fetch(),
{"_id":"CategoryPublication-5"}).posts,{"ID":46});
however when i put this into meteor, i'm getting undefined
this is the code i used
Template.CategoryArticleSingle.helpers({
articles: function () {
var id = FlowRouter.getParam('ID')
var category = FlowRouter.getParam('category')
console.log(CategoryCollection.find().fetch());
let match = _.findWhere(_.findWhere(CategoryCollection.find().fetch(), {"_id":category}).posts,{"ID": id});
console.log("match",id,category,match);
return match;
}
});
Why am i getting undefined
update.
would this be correct? i substituted the 47 id, with just id so i can use it for any link.
Im getting "category" is read-only error.
Template.CategoryArticleSingle.helpers({
articles: function () {
var id = FlowRouter.getParam('ID')
var category = FlowRouter.getParam('category')
console.log(CategoryCollection.find().fetch());
const category = CategoryCollection.find().fetch().find(c => c._id === id);
let post = null;
if (category) {
post = category.posts.find(p => p.ID === id);
}
console.log("post",id,category,post);
return post;
}
});
There's no need to use lodash/underscore's findWhere. This functionality is built into ES2015. Also, you may consider breaking up the code into a few lines to make it more legible.
const category = CategoryCollection.find().fetch().find(c => c._id === 'CategoryPublication-5');
let post = null;
if (category) {
post = category.posts.find(p => p.ID === 47);
}

Categories

Resources