Error with Firestore update in a Cloud Function - javascript

I try to put a listener on Firebase that will replicate a value in the matching element in Firestore.
exports.synchronizeDelegates = functions.database.ref(`delegates/{userId}/activities`).onUpdate((event) => {
const userKey = event.data.ref.parent.key
console.log("User Key:" + userKey)
return admin.database().ref(`delegates/${userKey}/email`).once('value', snapshot => {
let email = snapshot.val()
console.log("Exported Email:" + email)
const userRef = admin.firestore().collection('users')
const firestoreRef = userRef.where('email', "==", email)
firestoreRef.onSnapshot().update({ activities: event.data.toJSON() })
}).then(email => {
console.log("Firebase Data successfully updated")
}).catch(err => console.log(err))
}
)
This function is able to retrieve and locate the elemnt needed to target the right document in firestore, but the .update()function still error firestoreRef.update is not a function
I try several ways to query but I still have this error.
How to properly query then update a document in this scenario?

The onSnapshot() method of Query introduces a persistent listener that gets triggered every time there's a new QuerySnapshot available. It keeps doing this until the listener is unsubscribed. This behavior is definitely not what you want. Also, there's no update() method on QuerySnapshot that your code is trying to call.
Instead, it looks like you want to use get() to fetch a list of documents that match your query, then update them all:
exports.synchronizeDelegates = functions.database.ref(`delegates/{userId}/activities`).onUpdate((event) => {
const userId = event.params.userId
console.log("User Key:" + userKey)
return admin.database().ref(`delegates/${userId}/email`).once('value', snapshot => {
let email = snapshot.val()
console.log("Exported Email:" + email)
const usersRef = admin.firestore().collection('users')
const query = usersRef.where('email', "==", email)
const promises = []
query.get().then(snapshots => {
snapshots.forEach(snapshot => {
promises.push(snapshot.ref.update(event.data.val()))
})
return Promise.all(promises)
})
}).then(email => {
console.log("Firebase Data successfully updated")
}).catch(err => console.log(err))
}
Note that I rewrote some other things in your function that were not optimal.
In general, it's a good idea to stay familiar with the Cloud Firestore API docs to know what you can do.

Related

array.prototype..forEach function skipped with values in variable

I have a problem, i have a Firebase Firestore Database connected to my React.JS Project, where users can enroll to courses. Now if i'm trying to load the users collection from the DB it returns 1 entry.
const fetchAthletes = async () => {
debugger
try {
const athletes: Array<any> = [];
const athleteRef = collection(db, COLLECTION_NAME_ATHLETE);
const getAthleteQuery = query(athleteRef, where('user', '==', userAuthToken.accessToken));
const querySnapshot = await getDocs(getAthleteQuery)
if (querySnapshot.docs) {
//this for each gets skipped, even when querySnapshot.doc has values in it
querySnapshot.forEach((doc) => {
athletes.push({
id: doc.id,
...doc.data()
});
setAthletes(athletes as Array<Athlete>);
})
}
} catch (error: unknown) {
enqueueSnackbar((error as string), { variant: 'error', autoHideDuration: 3000 })
}
}
But when i want to loop over it via array.prototype.map it always skips it.
I debbuged through the funtion and found out that docs from Firestore is set with values tbat i wanna recieve.
Data returned by Firestore
I have no clue why it doesn't work. Any idea or help is appreciated
Rather than attempt to individually set each doc into state, build up your array and set the entire thing into state
const querySnapshot = await getDocs(getAthleteQuery);
setAthletes(querySnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));

Get the var value out of IF statement

I'm trying to do a comparison between user session email and an email in an array in firestore. That is, I want to search for the login email within the database and if any email is found, bring some information to the screen, such as name and surname.
I even managed to get inside the array and make this comparison, but I can't make the "var UserName" leave the { } of the IF
Can someone help me?
my code is:
const [data, setData] = useState([]);
const getUsers = () => {
firestore()
.collection("users")
.get()
.then((querySnapshot) => {
let userFirestore = [];
querySnapshot.forEach((doc) => {
const usuario = {
id: doc.id,
nome: doc.data().nome,
sobrenome: doc.data().sobrenome,
email: doc.data().email,
profissao: doc.data().profissao,
}
userFirestore.push(usuario);
});
userFirestore.forEach(function (item, indice, array) {
if (item.email === user.email){ //user.email brings the email of the logged in user
var nomeUsuario = item.nome
console.log(nomeUsuario) //UserName brings the result I expected
}
});
})
.catch((error) => console.log(error));
}
You can use a query to get a document with current user's email that instead of fetching the whole collection as shown below:
firestore()
.collection("users")
.where("email", "==", user.email)
.get().then((querySnapshot) => {
if (querySnapshot.empty) {
console.log("User not found")
return;
}
const user = querySnapshot.docs[0].data();
console.log(user)
})
Even better, if you use user's UID as the Firestore document ID (highly recommended), then you can get that single document by ID as shown below:
firestore()
.collection("users")
.doc(user.uid) // < user.uid must be defined
.get()
.then((snapshot) => {
console.log(snapshot.data())
})
When you fetch the whole collection, you are charged N read where N is total number of documents in the collection and it'll be slower as you are downloading plenty of data.

How to fetch all the documents with unique id from firestore database using React?

[Firestore SS][1]
[1]: https://i.stack.imgur.com/EI1Dm.png
I want to fetch each document as displayed in SS it's stored as Pets + unique_userId.
I am unable to fetch all data together. Just able to fetch one data of a particular user using the code below.
const [info,setInfo]=useState([]);
useEffect(() => {
db.collection("pets ESYXOPqlJpZ48np8LfNivnh9pvc2").onSnapshot((snapshot) =>
setInfo(snapshot.docs.map((doc) => doc.data()))
);
},[]);
Here ESYXOPqlJpZ48np8LfNivnh9pvc2 this is the userID of each unique user
Please help me out to fetch all the Pets data instead of hardcoding and fetching one particular data.
Try the following code,
const [docs, setDocs] = useState([]);
useEffect(() => {
const querySnapshot = await getDocs(collection(db,"pets ESYXOPqlJpZ48np8LfNivnh9pvc2"));
const document =[];
querySnapshot.forEach((doc) => {
document.push({
...doc.data(),
id: doc.id
});
});
setdocs(document);
}, []);
I'm guessing the appended id is a reference to the owner's id? In this case, would it be an option to fetch the owner list and use everyone's id to build a list of collection ids and then get all of their data?
If not, I only see to options:
Rethink your database structure - maybe use a unified pets collection and have a reference with/to that id in the pet documents.
Create a cloud function in which use #google-cloud/firestore to get the list of collections. There are tons of resources out there to help you get started with firebase cloud functions. Their documentation is pretty good also, and probably the most up-to-date
const functions = require('firebase-functions')
const { Firestore } = require('#google-cloud/firestore');
module.exports = functions
.region('europe-west3') // use the region you want here
.https.onRequest(async (request, response) => {
try {
const firestore = new Firestore();
const collections = (await firestore.listCollections()).map(collection => collection.id)
response.json({ data: collections })
} catch (error) {
response.status(500).send(error.message)
}
})
You'll get and endpoint which you can use to fetch the collection ids (e.g.: https://your-project-name.cloudfunctions.net/collections)
const [pets, setPets] = useState([]);
const [collectionIds, setCollectionIds] = useState([])
useEffect(() => {
fetch('https://your-project-name.cloudfunctions.net/collections')
.then(response => response.json())
.then(({ data }) => setCollectionIds(data))
}, [])
useEffect(() => {
collectionIds.forEach((collectionId) => {
// There are better ways to do this,
// I'm just using your approach so you can focus on the rest of the code
db.collection(collectionId).onSnapshot((snapshot) => {
setPets((currentPets) => [...currentPets, ...snapshot.docs.map((doc) => doc.data())])
})
})
}, [collectionIds])
Please note that these are very high-level implementations, there's no error handling, no teardowns or anything, so keep that in mind. Hope it helps, good luck!

Firebase Firestore is not creating a doc - any idea how to debug?

I've been struggling with a weird error. Can't create a doc in firebase. There are no security rules to speak of, just:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
Firestore is initialised the normal way and is active:
import { Firebase } from "../db";
let firebase = Firebase();
let firestore = firebase.firestore();
But nothing happens after this is run other than printing "here1", the other consoles aren't doing anything and the userid doc is not being created and no collection and doc under it.
export const addEnquiry = async (data) => {
let user = await firebase.auth().currentUser;
data.uid = user.uid;
console.log("here1");
const enquiry = await firestore.collection("users").doc(data.uid).collection("enquiries").doc();
return await enquiry
.set(data)
.then((doc) => {
console.log("here2");
return true;
})
.catch((err) => {
console.log("here3");
console.log(err);
return false;
});
};
The above doesn't print anything other than "here1" and gets stuck on the setting of the doc. The doc isn't created in Firestore either.
Any idea what might be wrong and how to debug it? Wasted a good 4 hours on trying to figure it out and worried if Firestore is so buggy that it's unsafe to use it in production.
First of all, I assure you Firebase is not buggy at all, we have it running on several production applications and they're running fantastic.
Second, I think your issue here is that you're passing a function as the second argument in the set() method, which is nowhere that I can find in the API reference. Instead, it returns a promise. Your code should look like this:
firebase.firestore()
.collection("users")
.doc(uid)
.set({ uid: uid })
.then((doc) => { console.log(doc.id) })
.catch((err) => { console.log(err) })
Cheers.
Here is an example which will work for you:
file test.mjs
import { Firestore } from '#google-cloud/firestore';
const firestore = new Firestore()
export default (uid) => firestore.collection("users")
.doc(uid)
.set({ uid })
.then(() => console.log('success')) // documentReference.set() returns: Promise < void >
.catch(err => console.error(err))
It's super weird, but what solved the issue for me is adding an unnecessary doc.get() like so:
export const addEnquiry = async (data) => {
let user = await firebase.auth().currentUser;
data.uid = user.uid;
console.log("here1");
const enquiry = await firestore.collection("users").doc(data.uid).collection("enquiries").doc();
const x = await firestore.collection("users").doc(data.uid).get();
// ^^^ added the above line I don't actually need, but somehow it
// establishes a connection to firebase or something which allows the
// promise below to resolve, rather than just hang!
// Doesn't resolve without it for whatever reason!
return await enquiry
.set(data)
.then((doc) => {
console.log("here2");
return true;
})
.catch((err) => {
console.log("here3");
console.log(err);
return false;
});
};
When removing the line, the function hangs again. So have to keep it in for now!
A bit worrying that we have to use such workaround hacks to make a simple write to the firestore, to work, Firebase!
Nonetheless, hope it helps someone facing this undebuggable problem.

Unable to retrieve data from Multiple collection firebase

Hope you're in good health.
I have a problem.
export function fetchListing() {
return function (dispatch) {
dispatch(fetchListingRequest());
//Getting Listing where status is in_review
firebase
.firestore()
.collection("listings")
.where("status", "==", "in_review")
.onSnapshot(
snapshot => {
const data = [];
snapshot.docs.forEach(doc => {
const temp = {};
// Getting address of business
firebase
.firestore()
.collection("users")
.doc(doc.data().business_id)
.get()
.then(users => {
temp["address"] = users.data().address;
})
.catch(error => {
dispatch(fetchListingFailed(error));
});
temp["title"] = doc.data().title;
temp["description"] = doc.data().description;
temp["listing_file"] = doc.data().listing_file;
data.push([doc.id, temp]);
});
dispatch(fetchListingSucess(data));
},
error => {
dispatch(fetchListingFailed(error));
}
);
};
}
I am Unable to get address in state but when I log it It displayed in console. I am able to access address when I am retrieving it from firebase and also in reducer I am also getting address.
reasons can be :
The hierarchy of your firestore collection
or you have to know that in js you can't affect the data to a variable that is in the main prog just when it is in a one level functions
for exemple here you can do this
locale = this ;
than you can use all the variables in two other levels

Categories