Firestore retrieve single document by field value and update - javascript

I'm trying to retrieve a single document by a field value and then update a field inside it.
When I do .where("uberId", "==",'1234567'), I am getting all the docs with field uberId that matches 1234567.
I know for sure there is only one such document. However, I don't want to use uberId as the document's ID, otherwise I could easily search for the document by ID. Is there another way to search for a single document by a field ID?
So far, reading the docs, I could see this:
const collectionRef = this.db.collection("bars");
const multipleDocumentsSnapshot = await collectionRef.where("uberId", "==",'1234567').get();
Then I suppose I could do const documentSnapshot = documentsSnapshot.docs[0] to get the only existing document ref.
But then I want to update the document with this:
documentSnapshot.set({
happy: true
}, { merge: true })
I'm getting an error Property 'set' does not exist on type 'QueryDocumentSnapshot<DocumentData>'

While you may know for a fact there's only one document with the given uberId value, there is no way for the API to know that. So the API returns the same type for any query: a QuerySnapshot. You will need to loop over the results in that snapshot to get your document. Even when there's only one document, you'll need that loop:
const querySnapshot = await collectionRef.where("uberId", "==",'1234567').get();
querySnapshot.forEach((doc) => {
doc.ref.set(({
happy: true
}, { merge: true })
});
What's missing in your code is the .ref: you can't update a DocumentSnapshot/QueryDocumentSnapshot as it's just a local copy of the data from the database. So you need to call ref on it to get the reference to that document in the database.

async function getUserByEmail(email) {
// Make the initial query
const query = await db.collection('users').where('email', '==', email).get();
if (!query.empty) {
const snapshot = query.docs[0];
const data = snapshot.data();
} else {
// not found
}
}

Related

Firestore setDoc Function not merging new items on React JS

so I'm trying to have this function "Walletwrite" check if a document already exists, then if document doesn't exist, creates a new one with the values I want to add, or updates an existing document by adding a new field with those new values. All of this on React JS.
However, my setDoc function is actually overwriting existing data if document already exists..
Any ideas on where the problem is?
async function Walletwrite() {
//These first 2 consts check if the entry is already in that specific document to prevent duplicates.
const marketRef = db.collection("marketplace");
const query = marketRef.where("wallet", "array-contains", account).where("item", "==", item.id).limit(1);
query.get().then((snapshot) => {
if (snapshot.empty == false) {
console.log(snapshot)
return
}
else{
//This is where it gets tricky and merge: true is not working
const walletRef = doc(db, 'marketplace', item.id);
setDoc(walletRef, {item: item.id, wallet: account} , { merge: true });
}
});
}
Trying different firestore functions but none of them seem right to my use case, except this setDoc with merge: true..

firebase Functions TimeStamp convert it to string

const signupTime =admin.database.ServerValue.TIMESTAMP
database.ref(`/admin/vendorOrder/${vendor}/${userID}`).set(signupTime);
console.log(signupTime);
i want the time value
while in database it returns correctly
in consol log it returns
how do i make it return value in string
also
database.ref(`/admin/vendorOrder/${vendor}/${userID}/${signupTime}/order`).set(order);
returns error
kindly help
As explained in the doc, with ServerValue.TIMESTAMP you get "a placeholder value for auto-populating the current timestamp as determined by the Firebase servers".
In other words signupTime does not contain the value of the timestamp, since this one is set on the server (the back-end) when you write to the database.
If you want to get this value in your Cloud Function (or in a front-end), you need to query the newly set database node, as follows, for example:
const signupTime = admin.database.ServerValue.TIMESTAMP
const database = admin.database();
const ref = database.ref(`/admin/vendorOrder/${vendor}/${userID}`);
ref
.set(signupTime)
.then(() => {
return ref.get();
})
.then((snap) => {
console.log(snap.val());
})
.catch((error) => {
console.log(error);
});

Firebase array of references

When trying to deal with an array of references I get the error "FirebaseError: Function Firestore.doc() requires its first argument to be of type non-empty string, but it was: a custom t object"
My user doc has an array of references called reviews and I am trying to get the data of each of the references.
const handleFetch = async () => {
let db = firebase.firestore();
let userRef = await db
.collection("users")
.doc(props.user.id.uid) //<- doc works and returns correctly
.get();
userRef.data().reviews.forEach((ref, indx) => {
let hold = db.doc(ref).get(); //<- error occurs here
});
}
In firestore, a Reference object is constructed into a document or collection reference and you can invoke any of the document methods on it as needed.
const ref = snapshot.data().reviews[0];
ref.get();
ref.remove();
The error in particular is saying that the item is not a string, which if it is a firestore reference, is an object and thus, not compatible.
A great video to watch: https://www.youtube.com/watch?v=Elg2zDVIcLo&t=276s

Firestore can't get empty docs

I have collection of documents where the id of the doc is the users id.
Lets call these user documents.
Each "user document" contains a subcollection of chat messages. But not all "user documents" contains any fields (data, other than the subcollection).
I wan't to return all the doc in the collection that don't have any fields, but have a subcollection, but I seems this is not possible?
var allUserDocs = {},
count = 0,
users = firestore.collection("users");
users.get().then(snapshot => {
snapshot.forEach(doc => {
count++;
allUserDocs[count] = doc.data();
});
allUserDocs.count = count;
res.status(200).send(allUserDocs);
})
this code only returns the docs that contains fields, not the docs that only have a subcollection? Is there a way to return all?
How can i get a list of all document ids in the collection? both empty and non-empty ones? or how can I add a field to all the docs without fields if i cant access them?
There is a listDocuments method that retrieves all documents, missing or not, that have a subcollection. Here's the page in the docs that explains it.
Something like this might be what you are looking for:
let collectionRef = firestore.collection('col');
return collectionRef.listDocuments().then(documentRefs => {
return firestore.getAll(...documentRefs);
}).then(documentSnapshots => {
for (let documentSnapshot of documentSnapshots) {
if (documentSnapshot.exists) {
console.log(`Found document with data: ${documentSnapshot.id}`);
} else {
console.log(`Found missing document: ${documentSnapshot.id}`);
}
}
});
You would not care whether the docRef exists or not.
Nevertheless, it does not sound like a good solution to have empty documents. What is the logic you were pursuing with an architecture where users can be empty, but messages underneath them still matter? Maybe if you still need to access them you can add a boolean variable to determine if the user is active or not, instead of leaving a blank document.
Use doc.id
users.get().then(snapshot => {
snapshot.forEach(doc => {
count++;
allUserDocs[doc.id] = doc.data();
});
allUserDocs.count = count;
res.status(200).send(allUserDocs);
})

Can I get the generated ID for a document created with batch().set using Firestore?

Is there a way that I can get the auto-generated ID for a document created as part of a batch using Firestore?
When using .add() I can easily get an ID:
db.collection('posts')
.add({title: 'Hello World'})
.then(function(docRef) {
console.log('The auto-generated ID is', docRef.id)
postKey = docRef.id
});
Using .batch() I can add a document with .doc() and .set():
const batch = db.batch();
const postsRef = db.collection('posts').doc(postKey);
batch.set(postsRef, {title: 'Hello Again, World'});
const votesRef = db.collection('posts').doc(postKey)
.collection('votes').doc();
batch.set(votesRef, {upvote: true})
batch.commit().then(function() {
});
Can I get the auto-generated ID of the document that was added to votes?
Update:
Doug Stevenson is correct - I can access the ID at votesRef.id
When you call doc() without any arguments, it will immediately return a DocumentReference that has a unique id, without writing anything to the database - the id is generated on the client. So if you want that id, simply use the id property on that DocumentReference. That id will become visible in the database after you've written that document.
I had similar issue, And I thing there were changes at the API of firestore.
I was getting an error for code like :
const postsRef = db.collection('posts').doc(postKey);
batch.set(postsRef, {title: 'Hello Again, World'});
The change I found that was necessary is to take the ref of the doc object:
const postsRef = db.collection('posts').doc(postKey).ref;
I hope this helps you all !
In order to generate the uid automatically beforehand the creation of document, you can make use of createID() function from angularFireAuth as follows:
`
constructor(
private angularFireStore: AngularFirestore,
) {}
const batch = this.angularFireStore.firestore.batch();
const autogenUid = this.angularFireStore.createId();
const collectionReference = this.angularFireStore.collection
('collection_name').doc(autogenUid).ref;
const docData = {first_field:'value', second_field:'value2'};
batch.set(collectionReference, docData);

Categories