Nested firestore query in react - javascript

I have a firestore database with 2 collections: users and products. Users has a 'cart' field (type: array) which stores the uniqueName (field in products) of the products added to cart by a user. I fetched the 'cart' array and now, for each item, I want to fetch the product doc so I can access each product's individual fields.
*currentUserCart stores the elements from the cart array: strings with the unique product name based on which I do the query in fetchProducts().
While fetchUserCart() works, I followed the same logic for fetchProducts() but it doesnt work.
I tried this, but I get the correct products only if I click again wherever on the modal screen
export const Navbarr = ({items}) => {
const [user, error] = useAuthState(auth);
const [cartItems, setCartItems]=React.useState(0);
const [open, setOpen] = React.useState(false);
const [currentUserCart, setCurrentUserCart]=React.useState([]);
const [currentProducts, setCurrentProducts]=React.useState([]);
const handleOpen = () => {
fetchUserCart();
setOpen(true);
fetchProducts();
};
const fetchProducts=async()=>{
const products=[];
currentUserCart.forEach(async(item)=>{
const q = query(collection(db, "products"), where("uniqueName", "==", item));
const doc = await getDocs(q);
const data = doc.docs[0].data();
setCurrentProducts(currentProducts=>[...currentProducts, data]);
})
console.log(currentProducts);
}
const fetchUserCart = async() =>{
const q = query(collection(db, "users"), where("uid", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
setCurrentUserCart(data.cart);
console.log('cart '+currentUserCart);
}
const handleClose = () => setOpen(false);
const fetchUserCartItems=async()=>{
const q = query(collection(db, "users"), where("uid", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
let cartItemsClone=data.cartItems;
setCartItems(cartItemsClone);
}
React.useEffect(() => {
fetchUserCartItems();
fetchUserCart();
fetchProducts();
}, [user], [currentUserCart], [currentProducts]);

Related

How search users form firebase

I want search from firstore database user that displayname or nickname contians the search term. I have following code but don't search.
export const searchUsers = async (queryText)=>{
const searchTerm=queryText.toLowerCase();
const usersRef = collection(db, "users");
const users = [];
// Create a query against the collection.
const q = query(usersRef, where("displayName", "array-contains", searchTerm)||where("nickName", "array-contains", searchTerm) ,orderBy("nickName"));
onSnapshot(q, (querySnapshot) => {
querySnapshot.forEach((doc) => { console.log("docsearch:"+doc)
users.push(doc.data());
});
})
return users;
}
I dscoveret that the best way from this issue is make indexes with algolia following the next video: https://www.youtube.com/watch?v=eD1CUWs_3_k&ab_channel=midudev

Firebase Firestore nested collection in Cloud Functions

I am trying to implement the nested query with Firestore in Cloud Functions but stumbled upon issues with reading values in a for loop. Are there ways to adjust the following code so I could do some operations after reading all records from a collection?
const firestore = admin.firestore();
const today = new Date();
const snap = await firestore
.collection('places')
.where('endDate', '<', today)
.get()
const userIds = [...new Set(snap.docs.map((doc: any) => doc.data().owner))];
const updatePromises = snap.docs.map((d: any) => {
return d.ref.update({
isPaid: false,
isActive: false
})
})
await Promise.all(updatePromises);
const userCol = firestore.collection('users');
const userDocs = await Promise.all(userIds.map(uid => userCol.doc(uid).get()));
const userData = userDocs.reduce((acc, doc) => ({
...acc,
[doc.id]: doc.data()
}), {})
snap.docs.forEach((l: any) => {
const ownerData = userData[l.owner];
const { email, displayName } = ownerData;
console.log(email, displayName);
const message = {
// Some values
}
return sendGrid.send(message);
})
return null;
{ owner: '<firebaseUid'>, address: 'Royal Cr. Road 234' }
{ email: 'asdfa#afsdf.com' }
<firebase_uid>: {
displayName: '',
email: '',
phoneNumber: ''
}
The userIds.push(owner); will keep adding duplicate values in that array and if a single user is owner of multiple locations, you'll end up querying same data multiple times. If you are trying to read owner's data along with a location, then try refactoring the code as shown below:
const firestore = admin.firestore();
const today = new Date();
const snap = await firestore
.collection('locations')
.where('isActive', '==', true)
.get()
const userIds = [...new Set(snap.docs.map(doc => doc.data().owner))];
const updatePromises = snap.docs.map((d) => {
return d.ref.update({
isPaid: false,
isActive: false
})
})
// update documents
await Promise.all(updatePromises);
const userCol = firestore.collection("users")
const userDocs = await Promise.all(userIds.map(uid => userCol.doc(uid).get()))
const userData = userDocs.reduce((acc, doc) => ({
...acc,
[doc.id]: doc.data()
}), {})
// To get data of a location's owner
// console.log(userData[ownerId])
snap.docs.forEach((l) => {
const ownerData = userData[l.owner]
// run more logic for each user
})
return null;

How to get an element inside of an array in a function?

Code below outputs an array of users stored in Firestore db. Each document have the same id of a user.
const [user] = useAuthState(auth);
const [userData, setUserData] = useState([]);
const usersDB = collection(firestore, "Users");
const getUsers = async () => {
const data = await getDocs(usersDB);
setUserData(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
useEffect(() => {
getUsers();
}, []);
I want to access the document with same id as logged in user and check if isAdmin field is true or false using useAuthState to get user uid. How should I write the code for this?
According to your code you are using getDocs instead of getDoc.
Here's a sample code using useEffect and checking boolean isAdmin.
const [userData, setUserData] = useState([]);
const email = "test#xyz.com";
const password = "123Addw1113#";
const getUsers = async () => {
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
setDoc(doc(db, "<collection>", user.uid), {
someField: "<data>",
})
.then(async () => {
const docRef = doc(db, "<collection>", user.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists() && docSnap.data().isAdmin === true) {
setUserData(docSnap.data());
console.log("isAdmin is True");
} else if (docSnap.exists() && docSnap.data().isAdmin === false) {
console.log("isAdmin is False");
} else {
console.log("No such document!");
}
});
})
.catch((error) => {
console.log(error)
});
}
useEffect(() => {
getUsers();
}, []);

React / Firebase : Sort querySnapshot push

I have the following:
const useItems = () => {
const user = firebase.auth().currentUser;
const user1 = user.uid;
const [items, setItems] = useState([]);
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection("items")
.where("User", "==", user1)
.orderBy("Category", "asc")
.get().then(function (querySnapshot) {
querySnapshot.forEach(function(doc) {
options.push({
value: doc.data().Name.replace(/( )/g, ''),
label: doc.data().Name,
Weight: doc.data().Weight
});
});
})
});
}
How can I sort the options.push by label ascending (a-z) in the querySnapshot?

How can I download an array of URLs synchronously to maintain their order in React Native?

I'm using React Native and Firebase V9. I am trying to download URLs from storage, but I'm not able to both download all the URLs in the correct order and have them load the first time I visit the screen. I know that getDownLoadURL() runs asynchronously, which is why the images get downloaded in random order each time. There is something I'm not understanding correctly.
Here are two main ways I've tried:
const [memNameLogs, setMemNameLogs] = useState([]);
const [memIDLogs, setMemIDLogs] = useState([]);
const [memImagesLogs, setMemImagesLogs] = useState([]);
const [memberCount, setMemberCount] = useState(0);
const getGroupInfo = async () => {
let memberIDs = [];
let memberNames = [];
let memberImages = [];
let userGroupsRef = collection(db, "groups", groupID, "members");
onSnapshot(userGroupsRef, (querySnapshot) => {
querySnapshot.forEach((document) => {
memberIDs.push(document.id);
onSnapshot(doc(db, "users", document.id), (snapshot) => {
const one = snapshot.data();
const two = one.firstName;
const three = one.lastName;
const four = two + ' ' + three;
memberNames.push(four);
console.log(memberNames);
});
const pathReference = ref(storage, 'userProfileImage/' + document.id);
// Get the download URL
getDownloadURL(pathReference)
.then((url) => {
memberImages.push(url);
})
});
setMemberCount(memberIDs.length);
setMemIDLogs(memberIDs);
setMemNameLogs(memberNames);
setMemImagesLogs(memberImages);
})
and
const getGroupMembers = async () => {
let memberIDs = [];
let memberNames = [];
let paths = [];
let userGroupsRef = collection(db, "groups", groupID, "members");
onSnapshot(userGroupsRef, (querySnapshot) => {
querySnapshot.forEach((document) => {
memberIDs.push(document.id)
onSnapshot(doc(db, "users", document.id), (snapshot) => {
const one = snapshot.data();
const two = one.firstName;
const three = one.lastName;
const four = two + ' ' + three;
memberNames.push(four);
});
const pathReference = ref(storage, 'userProfileImage/' + document.id);
paths.push(pathReference);
})
setMemberCount(memberIDs.length);
setMemIDLogs(memberIDs);
setMemNameLogs(memberNames);
setImagePathReferences(paths);
})
return Promise.all(imagePathReference)
}
const getGroupPictures = async (file) => {
let downloadURL;
await getDownloadURL(file)
.then((url) => {
downloadURL = (url);
})
.catch((error) => {
// Handle any errors
if (error.code === "storage/object-not-found") {
downloadURL = ('');
}
});
return (downloadURL)
}
const getGroupInfo = async () => {
const references = await getGroupMembers();
console.log(references);
for (let i = 0; i < memberCount; i++) {
let file = references[i];
const references2 = await getGroupPictures(file);
memImagesLogs.push(references2);
}
};
A simple fix is to store the download URLs in an associative array, with the file as the key.
Something like this:
const downloadURLs = {}; // 👈
const getGroupInfo = async () => {
const references = await getGroupMembers();
console.log(references);
for (let i = 0; i < memberCount; i++) {
const file = references[i];
const downloadURL = await getGroupPictures(file);
downloadURLs[file] = downloadURL; // 👈
}
};
With that, the references array determines the order, while the downloadURLs object allows you to look up the download URL for each file.

Categories