fiirebase result.val is null with await - javascript

What is the response structure of firebase queries? Is there any documentation? I couldn't find anything. I don't why user is null below. Do I need to run for loop and get the first item?
const result = await firebase.child('users').orderByChild('email')
.equalTo(memberEmail).limitToFirst(1)
.once('value');
const user = result.val();
the following code works, but I don't want to run a loop to get single value. How do I get the user without running a loop?
result.forEach((value) => {
alert(`user = ${JSON.stringify(value.val())}`);
});

When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result. So if you use a query, you will always need a loop.
The way to not require a loop is by directly accessing the child node that you are looking for. In your current data structure you likely store the users by their uid, so you can only access them directly by uid:
const result = await firebase.child('users').child(currentUser.uid).once('value');
const user = result.val();
If you'd store the users by their email address (you'll have to encode it, since a . cannot be used in the key), you could also access the users by their email address.
An idiomatic way to do this with Firebase is to keep a separate node that maps email addresses to uids. You'd then do two direct lookups:
firebase.child('emailToUid').child(email)
firebase.child('users').child(uid)
Each of those would return a single node (or no node if there is no match found), so you could chain the awaits and won't need a loop

Related

Best way to batch create if not exists in firestore

I am working with a project where we create a bunch of entries in firestore based on results from an API endpoint we do not control, using a firestore cloud function. The API endpoint returns ids which we use for the document ids, but it does not include any timestamp information. Since we want to include a createdDate in our documents, we are using admin.firestore.Timestamp.now() to set the timestamp of the document.
On subsequent runs of the function, some of the documents will already exist so if we use batch.commit with create, it will fail since some of the documents exist. However, if we use batch.commit with update, we will either not be able to include a timestamp, or the current timestamp will be overwritten. As a final requirement, we do update these documents from a web application and set some properties like a state, so we can't limit the permissions on the documents to disallow update completely.
What would be the best way to achieve this?
I am currently using .create and have removed the batch, but I feel like this is less performant, and I occasionally do get the error Error: 4 DEADLINE_EXCEEDED on the firestore function.
First prize would be a batch that can create or update the documents, but does not edit the createdDate field. I'm also hoping to avoid reading the documents first to save a read, but I'd be happy to add it in if it's the best solution.
Thanks!
Current code is something like this:
const createDocPromise = docRef
.create(newDoc)
.then(() => {
// success, do nothing
})
.catch(err => {
if (
err.details &&
err.details.includes('Document already exists')
) {
// doc already exists, ignore error
} else {
console.error(`Error creating doc`, err);
}
});
This might not be possible with batched writes as set() will overwrite the existing document, update() will update the timestamp and create() will throw an error as you've mentioned. One workaround would be to use create() for each document with Promise.allSettled() that won't run catch() if any of the promise fails.
const results = [] // results from the API
const promises = results.map((r) => db.doc(`col/${r.id}`).create(r));
const newDocs = await Promise.allSettled(promises)
// either "fulfilled" or "rejected"
newDocs.forEach((result) => console.log(result.status))
If any documents exists already, create() will throw an error and status for that should be rejected. This way you won't have to read the document at first place.
Alternatively, you could store all the IDs in a single document or RTDB and filter out duplicates (this should only cost 1 read per invocation) and then add the data.
Since you prefer to keep the batch and you want to avoid reading the documents, a possible solution would be to store the timestamps in a field of type Array. So, you don't overwrite the createdDate field but save all the values corresponding to the different writes.
This way, when you read one of the documents you sort this array and take the oldest value: it is the very first timestamp that was saved and corresponds to the document creation.
This way you don't need any extra writes or extra reads.

How to get value from firebase without the snapshot key [JS]

Im trying to make friends system but in invite accept i have to delete invite of a guy in his list but i cant do it without the key.
How do i get the snapshot key by value (in this case uid) in database?
Firebase Realtime Database queries work on single path, and then order/filter on a value at a fixed path under each direct child node. In your case, if you know whether the user is active or pending, you can find a specific value with:
const ref = firebase.database().ref("friends");
const query = ref.child("active").orderByValue().equalTo("JrvFaTDGV6TnkZq6uGMNICxwGwo2")
const results = query.get();
results.forEach((snapshot) => {
console.log(`Found value ${snapshot.val()} under key ${snapshot.key}`)
})
There is no way to perform the same query across both active and pending nodes at the same time, so you will either have to perform a separate query for each of those, or change your data model to have a single flat list of users. In that case you'll likely store the status and UID for each user as a child property, and use orderByChild("uid").
Also note that using push IDs as keys seems an antipattern here, as my guess is that each UID should only have one status. A better data model for this is:
friends: {
"JrvFaTDGV6TnkZq6uGMNICxwGwo2": {
status: "active"
}
}

retrieve all customers email address stripe node.js

I have this function that tries to retrieve all the customer's emails in stripe:
const customers = await stripe.customers.list({
});
var customerEmail = customers.data[0].email;
console.log(customerEmail)
I am trying to return all the email addresses of the users from stripe. the problem is, when I log it, it only returns the first one, due to teh fact that I have data[0]. I need all of them, but i can't put it in a loop because what am i looping through. is there any way to do this properly?
The data property you are indexing is an array you can iterate through[1].
const customers = await stripe.customers.list({});
customers.data.forEach(customer => {
console.log(customer.email);
});
[1] https://stripe.com/docs/api/customers/list

Saving a field from firestore that is an array

I am currently working on a mobile app on React and I am having trouble understanding how to save a field from fire store that is an array.
Since I can't post images my database structure is all strings such as username, first name, etc but I have a field called follow list that is an array.
What I want to do is save the usernames from the following list into an array to later search fire store for the username's in the array, this is basically what I want to do so I can render my app's social Feed. I do know that I can probably create another subcollection and write something familiar to what I did to search for users but that was a QuerySnapShot which was overall documents not a specific one and I also know firebase creates an ID for arrays and it increases as the array gets bigger.
I do not want to end up making two more subcollections one for followers and following which I think not ideal right? My current approach is this
export const fetchUserFollowing = async (username) => {
const ref = firebase.firestore().collection('users').doc(username)
let results = []
ref
.get()
.then( doc => {
let data = doc.data()
results = data
})
.catch((err) => {
return 'an error has occurred ', err
})
}
From what I understand is that a DocumentSnapShot .get() function returns an object but what I want is to store follow list into results and then return that but I am not sure how to manipulate the object return to just give me follow List which is an array
https://rnfirebase.io/docs/v5.x.x/firestore/reference/DocumentSnapshot
link to docs

Fetch value from a collection without knowing keywords [Javascript through Discord API]

I'm working with Discord API and node.js. In a Discord server we can assign members to specific "roles". "Members" are one of them, "admin" is another. In this case we are working with a custom one, not that it matters.
When I'm fetching every member of a certain role on the Discord server the API returns a collection looking something like this:
RoleMembers = {userID:{user}, userID:{user}, userID:{user}, userID:{user}}
The problem is that without knowing the keywords (userID) I cannot fetch any data in the {user} object. I've tried treating it like an array and loop through an index, but it just returns undefined.
I have no clue on how to treat this data that the API is feeding me.
The API documentation tells me this is what is returned:
.members READ-ONLY
The cached guild members that have this role
Type: Collection<Snowflake, GuildMember>
Tried with this loop, but it isn't giving me anything.:
var roles = message.channel.guild.roles;
var roleMembers = roles.get(settings.ptID).members;
for (var member in roleMembers) {
var jsonMember = {
ID:member.id,
Name:member.nickname
}
obj.Members.push(member);
}
Is there any way for me to either transform the collection into an indexed array? Or to fetch the keywords so that I can make a loop to fetch the data I need in the user object? I'm honestly not even sure what a "collection" mean in this context, or how to make operations on it.
User /u/samuraijs over at /r/javascript gave me the following code:
Object.keys(roleMembers).map(userId => roleMembers[userId])
But running
Object.keys(roleMembers).map(userId => console.log(userId))
Gives me no console log. Still not sure what I'm doing wrong.

Categories

Resources