NodeJS, Firestore get field - javascript

I have firestore set up on my nodejs chatbot.
And its successfully copying the Facebook users ID to .doc Name,
and setting there username and other info under there user ID in firestore
How do i go about retrieving this "field" name
to display back to user.
what i have so far is this
const givename = db.collection('users').doc(''+ sender_id).get("name");
How ever firebase is returning nothing.
Iv looked alot online on how to return a field. but with little luck.
Any suggestions?

Calling get() doesn't return the data immediately, since the data is loaded asynchronously. It instead returns a promise, which resolves once the data is loaded. This means that you can use await (as in Francisco's answer) or (if await is not available) implement the then() method:
db.collection('users').doc(''+ sender_id).get().then(function(doc) {
console.log(doc.data().name);
});
A common pitfall to be aware of: the doc.data() is only available inside the callback,

Haven't worked with Firestore yet, but from reading https://firebase.google.com/docs/firestore/query-data/get-data
something like below should work (untested):
const userRef = db.collection("users").doc("1234")
const userDoc = await userRef.get()
const {name} = userDoc.data()
console.log(name)

Another way is:
const givename = await db.collection("users").doc("YOUR DOCUMENT NAME").get();
console.log(givename.data().name);
Remember to put this code in an async function since we are using await here

Related

Issues connecting data stream to users responses in Google Voice App

I am currently developing a voice agent to be used in a smart speaker where users will ask about some items that are being stored in a data stream. The ultimate goal is that users ask about items' names in the stream and google actions through voice will tell them the details about those items as presented in another column in the stream.
To do this, I linked a spreadsheet to Axios to stream the content of the spreadsheet as data to be read in a webhook in google actions. The link to the data stream is HERE.
Honestly, I am new to developing apps for google actions and new to javascript overall so I might be doing silly mistakes.
In the graphical interface for google actions, I am setting a type for the items I want the user to ask about.
Then, I set an intent to recognize the item as a data type and be able to send this to the webhook.
The cloud function in the webhook is as follows:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = app.types.Item;
// conv.add("This test to see if we are accessing the webhook for ${itemParam}");
data.map(item => {
if (item.Name === itemParam)
agent.add('These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}');
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
What the webhook is doing is getting the stream with the getItem function and then mapping the data to find the Name in the stream to match the item parameter (ItemParam) as identified by the user.
However, one of the main problems I have is that when trying to access the item from the user, I am using app.types.Item, but this does not work as when testing I get an error saying: "error": "Cannot read property 'Item' of undefined". I think what is happening is that I am not using the correct way to call the Item in the conversation app.
Also, I am not sure exactly how the linking to the database will work. In other works, I am not sure if
data.map(item => {
if (item.Name === itemParam)
agent.add('These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}');
will work.
I have tried multiple things to solve but I am really struggling so any help with this would be really appreciated. Also, I know that I rushed to explain things, so please let me know if you need me to explain better or clarify anything.
Thank you
There are three points I am seeing that won't work.
First, app.types.Item is not the way to get this parameter. You should instead use conv.intent.params['Item'].resolved to get the user's spoken name.
Second, you are trying to use agent.add to include text, but there is no agent in your environment. You should instead be using conv.add.
Third, the text you are sending is not properly escaped between backticks ``. It is the backtick that allows you to use template literals.
Altogether your code can be rewritten as:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = conv.intent.params['Item'].resolved;
data.map(item => {
if (item.Name === itemParam)
conv.add(`These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}`);
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

Discord.js update cache to pull all members that have role

Currently having issues pulling all members that have a certain role. I can currently pull a list of names but only those that are cached currently. If i leave my bot running for a few hrs it will pull more but it never pulls all members that have that role. Ive read a few different things about using the .fetch() command but still no dice.
const client = new Client({
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_VOICE_STATES,
Intents.FLAGS.GUILD_MEMBERS
]
});
msg.channel.send('exporting all members with delcared role');
msg.guild.roles.fetch('guildId');//cache roles i think?
let role = msg.guild.roles.cache.find(r => r.name === 'Sysadmins');
msg.guild.members.fetch('roleId');//cache members i think?
let list = msg.guild.roles.cache.get(roleID).members.map(m => m.nickname)
console.log(role);
console.log(list);
The console.log(); returns my nickname as expected but only mine and i know there are at least 10 others with this role.
Other questions similar that I have already tried:
Similar Question #1
Similar Question #2
#DanLop your answer along with #MrMythical and a few other stack overflow questions helped me get this one sovled. I was able to use the async functions properly to pull all the data i need below is what i ended up with.
Solution:
client.on('messageCreate', async msg => { //TODO here });
let list = client.guilds.cache.get(tempGuildId);
try {
await list.members.fetch();
let role1 = list.roles.cache.get(roleId).members.map(m => m.displayName);
console.log(role1);
} catch (err) {
console.error(err);
}
Since you're giving the fetch() method a String argument, only the user matched with that String will be fetched, more like a get(). Also, the fetch() method returns a Promise, which means the method must be awaited to actually recieve the data.
Your fetch() method should look like this:
await msg.guild.members.fetch();
This way you tell Node.js it must wait until you recieve the data promised before continue.
It's close, but much simpler, with 3 lines
await msg.guild.members.fetch() // fetch all members and cache them
const role = msg.guild.roles.cache.get("id") // get role from cache by ID (roles are always cached)
const list = role.members.map(m => m.nickname) // map members by nickname

Firebase firestore: how to query relevant documents and then update each of them

I am developing a web app on Firebase/firestore, in which users can sign in and write their own posts. The data are stored in the following way:
-User information are stored as under collection('user').doc('uid').
-Information about posts the user has written are stored in collection('post').doc('postid'), and the doc has 'userinfo' and 'uid' fields. The 'userinfo' field contains exact copy of what is stored in 'uid' doc, just in object format.
Here are the operations that I want to do:
When the user changes the data, the changes are reflected in the document.
Look for the all the posts that the user has written based on 'uid' data, and then update userinfo in those data.
The last part is tricky for me. The Firebase documentations cover situations where the references are pretty much static, i.e. you know the exact path to write/update. What I am trying to do is look for a set of documents that is not necessarily static, and then update each of them.
Here is the code I wrote for this effort. The first part works without any problem. Of course, the second part doesn't work. :) What would be the code to do the do the second part?
const update = () => {
//This part is for updating user information. This works without any problem.
firebase.firestore().collection('user').doc(user.uid).update({
username: username1,
nickname: nickname1,
intro: intro1
})
.then(()=>{
//This part is for updating all of the document that the user has written based on 'uid' value. This doesn't work.
//Below code is probably way off, but it shows where I am going and what I am trying to do.
firebase.firestore().collection('post').where('uid','==',user.uid).get()
.then((querysnapshot)=>{
querysnapshot.forEach((doc)=>{
let ref=firebase.firestore().collection('post').doc(doc.id);
ref.update({
userinfo: {nickname:nickname1,username:username1,intro:intro1}
})
})
})
}).then(()=>{
alert("Successfully updated!");
window.location.href='/'+username1;
}).catch((error)=>{
alert("Error!");
})
}
Thanks a lot in advance!
What's the error that you get running this code? It seems on the right track for me.
But despite that, here are some suggestions to deal with this kind of update:
Don't do the second part on the client side, do it on the server side with a Firestore Trigger (create a onUpdate trigger in the user collection in your case): https://firebase.google.com/docs/functions/firestore-events.
The problem of doing in the client side, is because if the user closes the page/browser or the site goes offline in the middle of the update, you will have inconsistent data.
You don't need to recreate the DocumentReference after getting the query result, the docs returned already have a .ref that you can call .ref.update() directly.
EDIT: If you want to keep your original code (updating on client side), the problem of the navigation occurring before all the updates to conclude is because ref.update() returns a promise.
So the update queue is asynchronous being performed on database when the client navigates away.
To solve this, I would use a Promise.all() to wait all updates being completed.
firebase.firestore().collection('post').where('uid','==',user.uid).get()
.then((querysnapshot)=>{
const promises = [];
querysnapshot.forEach((doc)=>{
promises.push(doc.ref.update({
userinfo: {nickname:nickname1,username:username1,intro:intro1}
});
});
Promise.all(promises).then(()=>{window.location.href='/'+username1;});
});
Or using the await syntax (I think it's easier to maintain and understand):
const querysnapshot = await firebase.firestore().collection('post').where('uid','==',user.uid).get();
const promises = [];
querysnapshot.forEach((doc)=>{
promises.push(doc.ref.update({
userinfo: {nickname:nickname1,username:username1,intro:intro1}
});
});
await Promise.all(promises);
window.location.href='/'+username1;

Firebase function not being triggered when data base is written to

My database is structured as follows Collection("Message").Document("message")
But in reality, I want any change in the database's main collection to be monitored—when a document is added. I added the message document because I thought that maybe the function wasn't being called since my documents are the auto-generated ones. However, the problem persists...
Just for background I am an iOS developer, so perhaps I am doing something wrong here:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendPushNotifications = functions.database.ref('/Messages/{message}').onCreate((snapshot,context) => {
console.log(snapshot);
console.log(context);
var topic = "/topics/sentMessages";
var payload = {
data: {
message : 'You recieved a new message!'
}
}
return admin.messaging().sendToTopic(topic,payload).then((response) => {
return response;
})
})
For additional background: The application receives push notifications fine when using the console whether it be directly to the testing device or using topics. This problem is strictly when writing to firebase Firestore...
When you said "Collection("Message").Document("message")" that suggested to me that you're using Firestore as your database. However, your function is targeting changes to Realtime Database, which is a completely different thing. functions.database builds function for Realtime Database. functions.firestore builds functions for Firestore. You will want to read the documentation on Firetore triggers to learn how to write them.

Getting firebase getDownloadURL returning

According to the documentation I've read ref.getDownloadURL(); should return a link I could use in an img src.
Here is my code:
This is how I fire up firebase:
this.storage = app.storage();
storageRef = img => this.storage.ref(img);
I then call it like so:
const householdPics = (data, props) => {
const ref = props.firebase.storageRef(`images/${data.profilePic}`);
const img = ref.getDownloadURL();
console.log(img);
}
data.profilePic is equal to something.jpg.
I can confirm it's in storage in firebase in a directory called
images/
The error I get in my console is:
"Firebase Storage: Object 'images/NULL' does not exist."
From firebase I can copy the path:
gs://urlstuff.com/images
Then all my images are listed.
What am I doing wrong?
To get result of async method, you have to use then to get final url with access code attached to it.
ex.
storageRef
.getDownloadURL().then(url => { console.log(url) });
Here, your url will be printed at console.
Two things are wrong here.
First of all, the error message is suggesting that your value of data.profilePic is null. That's not valid - be sure to validate your data.
Second of all, as you can see from the API documentation, getDownloadURL() doesn't return the URL directly. It's asynchronous and returns a promise that resolves when the URL is available. This means you have to await it or use then to capture its final value, after the async work is done.

Categories