I'm looking at the firestore docs for hours and still didnt found solution for this case.
I need to add an ammout feature to product my E-commerce app.
data structure :
the main collection is "cart" and the doc is the user email.
here is the current code for add or set a product:
import firebase from 'firebase';
export async function addToCart(userMail, itemId, name, url, price, category, type, description) {
const ammout = 1
const cartItem = { itemId, name, url, price, category, type, description, ammout }
if (userMail === undefined) throw console.error('please Login');
const userDoc = await firebase.firestore().collection("cart").doc(userMail)
await userDoc.get().then((doc) => {
if (doc.exists) {
(async () => {
await userDoc.update({
item: firebase.firestore.FieldValue.arrayUnion(cartItem),
})
})()
}
else {
(async () => {
await userDoc.set({
item: firebase.firestore.FieldValue.arrayUnion(cartItem)
})
})()
}
})
}
The issue could be that you are using both await and Promise.then() in the same function, this could be creating some synchonicity issue, also you don't need to await for the userDoc variable to be populated, as you can see on the firestore documentation example.
So basically I would remove all the async/await from your code as they are not really needed and the Promise.then() on the .get() will be enough to ensure synchronicity in your code which could look something like this:
export function addToCart(userMail, itemId, name, url, price, category, type, description) {
const ammout = 1
const cartItem = { itemId, name, url, price, category, type, description, ammout }
if (userMail === undefined) throw console.error('please Login');
const userDoc = firebase.firestore().collection("cart").doc(userMail)
userDoc.get().then((doc) => {
if (doc.exists) {
userDoc.update({
item: firebase.firestore.FieldValue.arrayUnion(cartItem),
})
}
else {
userDoc.set({
item: firebase.firestore.FieldValue.arrayUnion(cartItem)
})
}
})
}
Related
I have a collection of Posts that are added to my db via this method:
const sendPost = async (event: any) => {
event.preventDefault();
if (loading) return;
setLoading(true);
const docRef = await addDoc(collection(db, "Posts"), {
id: tokenId,
postId: UID,
username: username,
profilePic: uri,
bio: bio,
likes: 0,
text: input,
timestamp: serverTimestamp(),
});
I have created a delete button with an onClick handler with the following code:
const handleDelete = async (e:any) => {
e.stopPropagation();
deleteDoc(doc(db, "Posts", post.postId));
console.log(post.postId);
}
The console.log in the above code matches the UID of the Document that im trying to delete, but nothing happens. Note: I added delete to the rules, and still no luck.
Does anybody know if I'm missing a step or can point me in the right direction??
Visual
Thanks!
You just don't know what is doc ID. Using addDoc(collection(db, "Posts"), data) function, you generate random document ID, and it is not an UID! To get that ID, you need to show how you get data from Firebase.
Using getDocs function, you can get doc ID like this:
async function getDocuments() {
const ref = collection(db, 'Posts')
const result = await getDocs(ref)
let data = []
if(result.exists()) {
result.foreach(docSnap => {
const doc = docSnap.data()
doc.docID = docSnap.id // here you getting real document ID
data.push(doc)
}
)
}
}
You have document ID above + Start Collection blue button, not in postID field!
This is what I have now and I would like to have this kind of method in some other file and import it from that file.
useEffect(async ()=> {
recipesRef
.orderBy('name')
.onSnapshot(
QuerySnapshot => {
const recipes = []
QuerySnapshot.forEach((doc) =>{
const{name, category,cookTime, prepTime, servingSize} = doc.data()
firebase.firestore().collection('recipes').doc(doc.id)
.collection('ingredients')
.onSnapshot(
QuerySnapshot => {
const ingredients = []
QuerySnapshot.forEach((doc2) =>{
const{name, amount, unit } = doc2.data()
ingredients.push({
id : doc2.id,
name,
amount,
unit,
})
})
setIngredients(ingredients)
recipes.push({
id : doc.id,
category,
cookTime,
name,
prepTime,
servingSize,
ingredients: ingredients,
recipeID: doc.id
})
setRecipes(recipes)
setFilteredData(recipes);
})
})
})
}, []);
I tried wrapping that code inside export async getAllFood() and when I try to console.log the data, it does show, but when I try to return it, it comes out as undefined.
I am trying to retrieve specific data from firebase, in my redux store I have uniq id that I can get in any page like this
const currentUser = useSelector(selectLoggedInUser);
console.log(currentUser.id) // "71dc954d-d2a4-4892-8257-98696fe776cd" this is peace of doc name in "dms" collection
I want all doc-s that contains this ID "71dc954d-d2a4-4892-8257-98696fe776cd", how can I query it???
This is how I'm setting "dms" messages
export const sentDirectMsg = async ({ toUid, currentUid, message, name }) => {
const collecitonRef = await dmCollection(toUid, currentUid);
await addDoc(collecitonRef, {
timestamp: serverTimestamp(),
message,
name,
});
};
const dmCollection = async (toUid, currentUid) => {
const idPair = [currentUid, toUid].sort().join("_");
return collection(db, "dms", idPair, "messages");
};
I'm not enough clear sorry for that(just don't have enough experience), I'll try my best.
I'm trying to create Slack like app(I have many pages and function that I exporting from one place to another), I will show how I implement the channels messages sent & get from firebase, then explain how I make direct messages
//Function that sent message to exact channelId /channels/someChannelId/messages
// channelId is literal with dynamic id
export const sentMsg = async ({ name, message, channelId }) => {
await addDoc(collection(db, "channels", channelId, "messages"), {
timestamp: serverTimestamp(),
message,
name,
});
};
//Getting data from channel
const messagesRef = query(
collection(db, `channels/${channelId}/messages`),
orderBy("timestamp")
);
onSnapshot(messagesRef, (snapshot) => {
setMessages(snapshot.docs);
});
Now as I need DM I can't make it same way because it need some privacy, only 2 person should see the messages, so I need 2 uniq person that has uniq id and their collection of messages also is uniq(so that only they can see each other messages),in general when I register the users in my app I also save with them uniq ID for example this "71dc954d-d2a4-4892-8257-98696fe776cd",
//This is how I sent direct messages
// toUid - to whom I should sent
// currentUid - is who is sent
const sentDirectMsg = async ({
toUid,
currentUid,
message,
name,
}) => {
const collecitonRef = await dmCollection(toUid, currentUid);
await addDoc(collecitonRef, {
timestamp: serverTimestamp(),
message,
name,
});
};
const dmCollection = async (toUid, currentUid) => {
const idPair = [currentUid, toUid].sort().join("_");
return collection(db, "dms", idPair, "messages");
};
// As I'm sorting this two uniq ID-s from where person sent-s the message it is always same collection reference. My question is can I somehow by "query" or by "where" get all docs that contains current user ID???
Edited:
If I understood correctly, you want to get a document which id contains a part of the id you are looking for.
Using array-contains should do the trick:
const dmsRef = collection(db,"dms");
const docRef = query(dmsRef, where("idPair", "array-contains", id)); //for example id = "71dc954d-d2a4-4892-8257-98696fe776cd"
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
I based my example on this link from the official documentation.
If your data model is centered toward users being identified through their unique IDs, then you can store your data first hand to reflect his model:
const userData = {
name: 'Marouane',
state: 'BN',
country: 'TUN'
};
// Add the user document in collection `dms`, with the id being the user ID
const res = await db.collection('dms').doc('71dc954d-d2a4-4892-8257-98696fe776cd').set(userData);
You can then query the user document using its unique identifier:
Firebase v8
const userRef = db.collection('dms').doc('71dc954d-d2a4-4892-8257-98696fe776cd');
const doc = await userRef();
if (!doc.exists) {
console.log('No such user!');
} else {
console.log('User dms data:', doc.data());
}
EDIT (Added firebase v9 - modular):
import { getFirestore, ref, onValue } from "firebase/firestore";
const db = getFirestore(firebaseApp);
const userRef = ref(db, 'dms/71dc954d-d2a4-4892-8257-98696fe776cd');
onValue(userRef, (snapshot) => {
const data = snapshot.val();
console.log(data);
});
In case your document id is not known in advance, you can query for all available documents and filter out ones that does not match your user id:
import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";
const db = getFirestore(firebaseApp);
const q = query(collection(db, "dms"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
if (doc.id.startsWith('71dc954d-d2a4-4892-8257-98696fe776cd')) {
console.log(doc.data());
}
});
Meanwhile, this approach will cause great performance degradation and would be better traded with a storage re-design.
I'm trying to build compound query in Expo react native - firestore.
I have 2 collections in firebase. First "node" is userID and second are IDs of places that had been discovered by this user. Then, I need to take this array of place IDs and pass it as parameter in 2nd query where I got name of each place stored in collection named "databaseOfPlaces". (I want to make scrollable view with names, so maybe I should add listener later on?)
My solution is not working very well. Can you help me? Is this the right way, or is there another way how to save DB call?
Thank you very much.
This is my code:
async componentDidMount() {
db.collection("placesExploredByUsers") // default
.doc("mUJYkbcbK6OPrlNuEPzK") // default
.collection(auth.currentUser.uid)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const users = [];
snapshot.forEach((doc) => {
const data = doc.data();
users.push(data);
});
this.setState({ users: users });
})
.catch((error) => alert(error));
db.collection("databaseOfPlaces")
.where('placeID','in',this.state.users)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const places = [];
snapshot.forEach((doc) => {
const data = doc.data();
places.push(data);
});
this.setState({ places: places });
})
.catch((error) => alert(error));
}
Data is loaded from Firestore (and most modern cloud APIs) asynchronously. By the time your second query now runs, the results for the first query are not available yet.
Because of this, any code that needs the results from the first query, will need to be inside the then() callback of that query.
So:
async componentDidMount() {
db.collection("placesExploredByUsers") // default
.doc("mUJYkbcbK6OPrlNuEPzK") // default
.collection(auth.currentUser.uid)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const users = [];
snapshot.forEach((doc) => {
const data = doc.data();
users.push(data);
});
this.setState({ users: users });
db.collection("databaseOfPlaces")
.where('placeID','in', users)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const places = [];
snapshot.forEach((doc) => {
const data = doc.data();
places.push(data);
});
this.setState({ places: places });
})
})
.catch((error) => alert(error));
}
I am trying to upload an image to Firebase storage. The problem is that since the user has not signed up yet, I don't have their uid.
I depend on onAuthStateChanged to get the user id and upload an image to their bucket, but so far it hasn't turned out well.
const { userObject } = useContext(Context) //trying to get the uid from here
onSubmit={(values, { setSubmitting }) => {
async function writeToFirebase() {
firebaseService.auth.createUserWithEmailAndPassword(values.email, values.password)
await firebaseService.firestore.collection('businessesPendingAdminApproval').add(values)
}
writeToFirebase()
async function sendToFirebaseImageBucket(photo, uid) {
const businessRef = await firebaseService.firestore.doc(
`businessesPendingAdminApproval/${uid}`,
)
firebaseService.storage
.ref()
.child('businesses')
.child(uid)
.child('avatar-image')
.put(photo)
.then(response => response.ref.getDownloadURL())
.then(photoURL => businessRef.update({ avatarImage: photoURL })) //try to update avatarImage
}
const uid = userObject.uid //undefined, can't get uid
sendToFirebaseImageBucket(values.avatarImage, uid) //uid gets passed as undefined
}}>
The way I am setting the userObject which is where I'm trying to get the uid from.
Setting the userObject eventually works but maybe not fast enought for me to be able to pass it to a function (as in the code above).
useEffect(() => {
firebaseService.auth.onAuthStateChanged(async function (userAuth) {
if (userAuth) {
const user = await firebaseService.createUserProfileDocument(userAuth)
setUserObject(user) //set userObject which has an uid field.
} else {
console.log('no one signed in')
}
})
}, [])
Just add your image to cloud storage right after you have logged in and was able to get uid. the following code can help you, it works for me as well. put the following code inside useEffect.
const unsubscribe = auth().onAuthStateChanged(user => {
if (user.uid){
const ref = storage.ref(`images/${user.uid}`);
const task = ref.putFile(_image, { contentType: 'image/jpeg' });
task.on(firebase.storage.TaskEvent.STATE_CHANGED, snap => {
setState({ type: 'cents', value: snap.bytesTransferred / snap.totalBytes * 100 });
}, err => { console.log('Error in help:persisAppState: ', err) }, async () => {
const image = await ref.getDownloadURL();
if (image) await db.collection("imagelinks").doc(user.id).set({ image });
});
}
});