I'm trying to check if certain field in a collection has a value.
I have a collection of users and that collection has a documents by their uid than that document has a field bio.
I want to check if that field is empty or not.
This is where i'm at at the moment...
const docRef = db.collection('users').doc(auth.user.uid).get()
docRef.then((querySnapshot) => {
if(querySnapshot.docs[0].data() != ''){
//there is value in field
}
})
By doing db.collection('users').doc(auth.user.uid).get() you define a Promise that resolves with a DocumentSnapshot (and not a QuerySnapshot). A DocumentSnapshot does not have any docs property.
So you need to do as follows:
const docRef = db.collection('users').doc(auth.user.uid).get();
docRef.then((docSnapshot) => {
if (docSnapshot.exists) {
if (docSnapshot.data().bio === '') {
// Bio is empty
}
} else {
// docSnapshot.data() will be undefined in this case
console.log("No such document!");
}
}).catch((error) => {
console.log("Error getting document:", error);
});
Note that from a naming convention perspective, it would make more sense to write:
const docRef = db.collection('users').doc(auth.user.uid); // This is a DocumentReference
docRef.get().then((doc) => {...});
Related
I have some code that gets a collection reference to the users collection and then queries an animeID field whether it contains a certain value or not. I want to change this and only query inside the document with the id i pass. So if you look at the picture of my firestore you can see that i have two documents inside the users collection each with their unique id. I want to query for the animeID field only in the document that i want. Not all the documents as it does right now. How would i go about doing this? I have tried using doc and then passing in the id of the document but i don't think query works on doc as it gives me an error. Thanks
const docRef = collection(db, 'users')
const q = query(docRef, where('animeID', 'array-contains', parseInt(id)))
onSnapshot(q, (snapshot) => {
let results = []
snapshot.docs.forEach((doc) => {
results.push({...doc.data(), id: doc.id})
})
if(results.length > 0){
console.log(true)
}
else{
console.log(false)
}
}, (error) => {
console.log(error)
})
Firestore structure:
You need to do as explained in the doc:
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "users", "dbmbEiR6....");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const animeID = docSnap.data().animeID;
// Do whatever you want with animeID
// E.g. log its value:
console.log(JSON.stringify(animeID));
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
So you don't need to declare a Query in this case. Just declare a DocumentReference and use the getDoc() method.
I'm trying to add a delete button to my page. the event listener callback is working properly except for the updateDoc function.
const deleteBook = document.getElementsByClassName('deleteBook');
for (let i = 0; i < deleteBook.length; i++) {
deleteBook[i].addEventListener('click', async () => {
//book to delete
const bookToDelete = deleteBook[i].parentElement.firstElementChild.textContent
// collection title to delete the book from
const bookCol = deleteBook[i].parentElement.parentElement.parentElement.firstElementChild.textContent
// get a snap of the database
const docRef = doc(dataBase, 'users', `${auth.currentUser.uid}`)
const docSnap = (await getDoc(docRef)).data();
// loop over the collections and get a match with the bookCol
for (const col in docSnap) {
if (docSnap[col].title === bookCol) {
console.log('col to delete from found')
console.log(`book to delete ${bookToDelete}`)
await updateDoc(doc(dataBase, 'users', `${auth.currentUser.uid}`), {
[`${col}.books`]: arrayRemove(`${bookToDelete}`)
}).then(()=>{
// fullfiled
console.log('book deleted')
}, ()=>{
// rejected
console.log('promis rejected')
})
}
}
})
}
Col is the object that contains the books array. In the console it always prints book deleted, but in the firestore console, nothing changes. this is a screenshot of the database.
I would really appreciate any help and thank you.
I have replicated the behavior that you're experiencing. I tried changing the content of ${bookToDelete} to any word or even ID. It always returns book deleted even if its deleted or not. The line of code below should be changed in order to get the correct output.
.then(()=>{
// fullfiled
console.log('book deleted')
}, ()=>{
// rejected
console.log('promis rejected')
})
I have created a workaround for your use-case with this kind of issue. See snippet below:
const db = getFirestore();
const colName = "users";
const arrayName = "books";
const usersCol = collection(db, colName);
const userRef = doc(db, colName, `${auth.currentUser.uid}`);
const arrayRef = `${col}.${arrayName}`;
const q = query(usersCol, where(arrayRef, "array-contains", `${bookToDelete}`));
const querySnapshot = await getDocs(q)
.then((querySnapshot) => {
// Removal of object will not proceed if the querySnapshot is empty.
if ((querySnapshot.empty)) {
console.log("No object found!");
}
else {
// Proceeds to removal of object.
updateDoc(userRef, {
[arrayRef]: arrayRemove(`${bookToDelete}`)
})
.then(() => {
// Check again if the object was deleted successfully.
const querySnapshot = getDocs(q)
.then((querySnapshot) => {
if ((querySnapshot.empty)) {
console.log("Book Deleted!");
}
else {
console.log("Failed!");
}
})
});
}
})
// Catch if there are any Firebase errors.
.catch(error => console.log('Failed!', error));
The workaround that I created will query the object in the array then remove the object in the array if it exist. After removing, it will query again to check if the object has been deleted and logs Book Deleted!. Vise versa for checking if the object doesn't exist on the 1st query, it will not proceed on removing them and logs No object found!.
The workaround itself can still be improved. You can add any logic you want for your use-case.
I'd also recommend to create a Feature Request if you want to have this kind of feature together with the arrayRemove Method.
All I want to do is to get a row (so called 'doc') from a data base.
so far, I have tried:
all with the 'aref'
const aref = firebase
.firestore()
.collection("polja")
.where("id", "==", match.params.id);
console.log(aref);
function getIt() {
const item = [];
setLoading(true);
aref.get().then((doc) => {
const data = doc.data();
setItem(item);
console.log(item);
setLoading(false);
});
}
useEffect(() => {
getIt();
}, []);
this gave the following error:
To get a single document, you must specify the document ID:
firebase.firestore().collection("polja").doc(documentId).get().then((snapshot) => {
console.log(snapshot.data())
}).catch((e) => console.log(e))
Also you should not use .where() to get just a single document, but there is an issue I found in your original code.
If you look carefully, the parameter in .where() is a string "match.params.id". That seems to be a dynamic value being fetched from somewhere else. Please remove the quotes and try again.
firebase.firestore().collection("polja").where("id", "==", match.params.id).get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
Try adding a catch block as shown which might help catch any errors. Make sure your security rules also allow you to fetch the data.
Also if any error is logged in the console, share a screenshot of it.
To get single document form firebase firestore you should first know if you are using the modular firebase 9.+ version or firebase version < 9.
In new modular firebase firestore(version 9.+) it should be like this:
import { getFirestore, collection, query, getDocs } from 'firebase/firestore'
async read(id) {
const firestore = getFirestore()
const docRef = doc(firestore, this.collectionPath, id)
const docSnap = await getDoc(docRef)
const data = docSnap.exists() ? docSnap.data() : null
if (data === null || data === undefined) return null
return { id, ...data }
}
If you are using not modular Firebase firestore( < version 9) then the same function should look something like this:
async read(id) {
const result = await (await firestore())
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (data === null || data === undefined) return null
return { id, ...data }
}
I've been searching and looking for an answer everywhere for days and still can't figure this out.
First let me show you what i want to achieve, here is the picture of the program:
And here is the picture of my Firestore database:
I want to be able and check if the user puts in the roomID in this case 5575 like in the picture above, the program should first check if that roomID exists in the firestore database. When i click the button Join i dont get anything in the console.
Here is my code so far:
const name12 = document.getElementById("oponentname") ;
const roomid = document.getElementById("idinput");
const joinButton = document.getElementById("joinButton");
var firebaseConfig = {
my configuration is here
}
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
joinButton.addEventListener("click", function(){
if (roomid.value == db.collection('game').doc(roomid.value).get()) {
console.log("Succes");
}
});
I think you should fix it like this:
db.collection("game")
.doc(roomid.value)
.get()
.then((doc) => {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function (error) {
console.log("Error getting document:", error);
});
You can read more here
You have to query for documents with that roomID and then check if one matches.
const get_room = (id) => {
return db.collection('game')
.doc(id)
.get()
.then(function(doc) {
return doc.exists
})
.catch(function(err) {
console.log(err)
})
Note that this is an async function, so to check if the inputed room id exists you should use a promise or async/await. My implementation uses promises.
joinButton.addEventListener("click", function(){
get_room(roomid.value).then((doc) => { if (doc) console.log("Succes"); })
});
I am trying to run a transaction which requires to get the data from a query:
firestore
.runTransaction((transaction) => {
const query = firestore
.collection("Lottories")
.doc("R3DYubrqFbbVfQNjYXfh")
.collection("sets")
.where("isAvailable", "==", false)
.limit(1);
return transaction.get(query).then((snapshot) => {
const ticketDoc = snapshot.docs[0];
const ticketDocData = ticketDoc.data();
const lottoUpdate = firestore
.collection("Lottories")
.doc("R3DYubrqFbbVfQNjYXfh")
.collection("sets")
.doc(ticketDoc.id);
const ticketUpdate = firestore
.collection("UserLotto")
.doc(userId)
.collection("tickets")
.doc("abc");
const countUpdate = firestore
.collection("UserData")
.doc(userId);
transaction.update(lottoUpdate, { isAvailable: true });
transaction.update(countUpdate, {
ticketCount: ticketCount - 2,
});
transaction.set(ticketUpdate, {
ticketId: ticketDoc.id,
lottoId: "abc",
claimed: false,
});
return ticketDocData;
});
})
.then((ticketDocData) => {
console.log(
"Transaction successfully committed!",
ticketDocData
);
setPopScreen("ticketPurchased");
})
.catch((error) => {
console.log("Transaction failed:", error);
});
For my application I need to run a query in order to complete my transaction. I Get the error:
Transaction failed: [FirebaseError: Function Transaction.get() requires its first argument to be a DocumentReference, but it was: a custom t object]
I understand transaction requires a document reference. Is there a work around this?
The nearest equivalent is to do the query with a higher limit, then in the transaction, get the doc again and check again for the required input condition....
// this will do the OP sets/updates, but *doesn't* assume the input
// doc meets the required condition (isAvailable==true)
// return a bool indicating success
function reserveDoc(lottoDoc) {
return firestore.runTransaction(transaction => {
return transaction.get(lottoDoc.ref).then(ticketDoc => {
// important, check the original condition again here
if (ticketDoc.data().isAvailable) {
// OP code to set/update goes here
return true
} else {
return false
}
})
})
}
// try a transaction on the first doc in the array. return if successful
// otherwise, try recursively on the remainder of the array
// return a bool indicating success
function reserveOneOf(lottoDocs) {
if (lottoDocs.length === 0) return false
return reserveDoc(lottoDocs[0]).then(success => {
// did it work? if not, try another doc
return success ? success : reserveOneOf(lottoDocs.slice(1))
})
}
function originalOPFunction() {
const query = firestore
.collection("Lottories")
.doc("R3DYubrqFbbVfQNjYXfh")
.collection("sets")
.where("isAvailable", "==", true) // note, I assume the OP query had a typo, checking for false
.limit(10);
return query.get().then(snapshot => {
return reserveOneOf(snapshot.docs)
}).then(success => {
// if !success here, the app must deal with NO docs currently meeting the criterion
// the OP needed to handle this circumstance anyway (if the limit(1) query returned no docs
})
}
The first param of Transaction get really is a document reference, not a query. This is confusing because documentReference.get() and transaction.get(documentReference) and `query.get(), kind of look and sound the same, but transaction is only atomic on a single doc, not on a set from a query, even one limited to 1.
transaction.get(query) the query must be a DocumentReference. So you need something like this:
db.collection("Lottories/R3DYubrqFbbVfQNjYXfh/sets")
.where("isAvailable", "==", false)
.limit(1)
.get()
.then((docs) => {
db.runTransaction((transaction) => transaction.get(docs[0]).then((doc) => {
if (doc.exists) //do something
}));
});