How do I get all the comments from the subcollection?
This is mine reusable function to get comments collection.
import { ref, watchEffect } from 'vue';
import { projectFirestore } from '../firebase/config';
const getCollection = (collection, id, subcollection) => {
const comments = ref(null);
const error = ref(null);
// register the firestore collection reference
let collectionRef = projectFirestore
.collection(collection)
.doc(id)
.collection(subcollection);
const unsub = collectionRef.onSnapshot(
snap => {
let results = [];
snap.docs.forEach(doc => {
doc.data().createdAt && results.push(doc.data());
});
// update values
comments.value = results;
error.value = null;
},
err => {
console.log(err.message);
comments.value = null;
error.value = 'could not fetch the data';
}
);
watchEffect(onInvalidate => {
onInvalidate(() => unsub());
});
return { error, comments };
};
export default getCollection;
And this is mine Comments.vue where i passing arguments in setup() function (composition API)
const { comments } = getAllComments('posts', props.id, 'comments');
When i console.log(comments) its null, in snapshot doc.data() is good but somehow results too is empty array even if i push doc.data() to results array and pass it to comments.value.
Can someone help me how to get that subcollection?
This is my Comment.vue component
export default {
props: ['id'],
setup(props) {
const { user } = getUser();
const content = ref('');
const { comments } = getAllComments('posts', props.id, 'comments');
const ownership = computed(() => {
return (
comments.value && user.value && user.value.uid == comments.value.userId
);
});
console.log(comments.value);
}
return { user, content, handleComment, comments, ownership };
},
};
const getCollection = (collection, id, subcollection) => {
const comments = ref(null);
const error = ref(null);
// Firestore listener
return { error, comments };
}
The initial value of comments here is null and since Firebase operations are asynchronous, it can take a while before the data loads and hence it'll log null. If you are using comments in v-for then that might throw an error.
It'll be best if you set initial value to an empty array so it'll not throw any error while the data loads:
const comments = ref([]);
Additionally, if you are fetching once, use .get() instead of onSnapshot()
I have an app that loads some tricks. In the first iteration (next, next, play) the subscription works properly. In the second iteration (end, next, next, play) it doesn't load the tricks anymore.
The Observable is correct, it is from firebase that the tricks don't provide.
Here is some code:
console.log('did enter');
this.startDate = Date.now();
this.params = this.paramsService.get();
console.log(this.params);
console.log(this.trickService.getTricks());
var myTricks = this.trickService.getTricks()
myTricks.subscribe(tricks =>{
console.log(tricks);
this.tricksCollection = db.collection<Trick>('tricks');
this.tricks = this.tricksCollection.snapshotChanges().pipe(
map(actions => {
return actions.map(a => {
const data = a.payload.doc.data();
const id = a.payload.doc.id;
return { id, ...data };
});
})
);
}
getTricks() {
console.log(this.tricks);
return this.tricks;
}```
Anyone got a clue, I would appreciate,
Alex
Hey I couldn't find how can i do nested query in firestore ?
this.match1 = this.MatchCollection1.snapshotChanges().pipe(map(actions => {
return actions.map(a => {
const data = a.payload.doc.data() as Matches;
const uid = data.pairer;
const id = a.payload.doc.id;
const photourl = afs.collection(`users/${uid}/photos`,
ref => ref.where('index', '==', 0).where('deleted', '==', false)).snapshotChanges().pipe(map(action => {
return action.map(p => {
const pdata = p.payload.doc.data() as Photos;
return pdata.url;
});
}));
return {id, uid, url: photourl, ...data};
});
}));
My problem photourl return observable how can i do return object like others
A bit tricky to follow but I think all you need to do is move your return {id, uid, url: photourl, ...data}; From what I can see you are just missing a subscription to unpack your observable and extract the value. EG
return action.map(p => {
const pdata = p.payload.doc.data() as Photos;
return pdata.url;
});
})).subscribe(photo => {
"if you want to set photourl do it here photourl = photo.url;"
return photo; //do what you need to before you return the actual
photo object.
});
You can use toPromise method with async await.
Data structure
users
| userId1
| photos
| photoId1
| url : 'someURL'
| photoId2
| url : 'someURL2'
Service or component file
items;
getPhotos(uid){
return this.db.collection(`users/${uid}/photos`).valueChanges()
.pipe(first()).toPromise() // get first stream and return it as promise
}
async fooFunc() {
let user_id = 'userId1' //TODO: get user uid from somewhere
this.items = await this.getPhotos(user_id) // await getPhotos promise finish
console.log(this.items) // return [Object, Object]
}
component html template
<li *ngFor="let item of items">
{{ item | json }}
</li>
output
{ "url": "someURL" }
{ "url": "someURL2" }
var jobskill_ref = db.collection('job_skills').where('job_id','==',post.job_id);
jobskill_ref.delete();
Error thrown
jobskill_ref.delete is not a function
You can only delete a document once you have a DocumentReference to it. To get that you must first execute the query, then loop over the QuerySnapshot and finally delete each DocumentSnapshot based on its ref.
var jobskill_query = db.collection('job_skills').where('job_id','==',post.job_id);
jobskill_query.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
doc.ref.delete();
});
});
I use batched writes for this. For example:
var jobskill_ref = db.collection('job_skills').where('job_id','==',post.job_id);
let batch = firestore.batch();
jobskill_ref
.get()
.then(snapshot => {
snapshot.docs.forEach(doc => {
batch.delete(doc.ref);
});
return batch.commit();
})
ES6 async/await:
const jobskills = await store
.collection('job_skills')
.where('job_id', '==', post.job_id)
.get();
const batch = store.batch();
jobskills.forEach(doc => {
batch.delete(doc.ref);
});
await batch.commit();
//The following code will find and delete the document from firestore
const doc = await this.noteRef.where('userId', '==', userId).get();
doc.forEach(element => {
element.ref.delete();
console.log(`deleted: ${element.id}`);
});
the key part of Frank's answer that fixed my issues was the .ref in doc.ref.delete()
I originally only had doc.delete() which gave a "not a function" error. now my code looks like this and works perfectly:
let fs = firebase.firestore();
let collectionRef = fs.collection(<your collection here>);
collectionRef.where("name", "==", name)
.get()
.then(querySnapshot => {
querySnapshot.forEach((doc) => {
doc.ref.delete().then(() => {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
or try this, but you must have the id beforehand
export const deleteDocument = (id) => {
return (dispatch) => {
firebase.firestore()
.collection("contracts")
.doc(id)
.delete()
}
}
You can now do this:
db.collection("cities").doc("DC").delete().then(function() {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
And of course, you can use await/async:
exports.delete = functions.https.onRequest(async (req, res) => {
try {
var jobskill_ref = db.collection('job_skills').where('job_id','==',post.job_id).get();
jobskill_ref.forEach((doc) => {
doc.ref.delete();
});
} catch (error) {
return res.json({
status: 'error', msg: 'Error while deleting', data: error,
});
}
});
I have no idea why you have to get() them and loop on them, then delete() them, while you can prepare one query with where to delete in one step like any SQL statement, but Google decided to do it like that. so, for now, this is the only option.
If you're using Cloud Firestore on the Client side, you can use a Unique key generator package/module like uuid to generate an ID. Then you set the ID of the document to the ID generated from uuid and store a reference to the ID on the object you're storing in Firestore.
For example:
If you wanted to save a person object to Firestore, first, you'll use uuid to generate an ID for the person, before saving like below.
const uuid = require('uuid')
const person = { name: "Adebola Adeniran", age: 19}
const id = uuid() //generates a unique random ID of type string
const personObjWithId = {person, id}
export const sendToFireStore = async (person) => {
await db.collection("people").doc(id).set(personObjWithId);
};
// To delete, get the ID you've stored with the object and call // the following firestore query
export const deleteFromFireStore = async (id) => {
await db.collection("people").doc(id).delete();
};
Hope this helps anyone using firestore on the Client side.
The way I resolved this is by giving each document a uniqueID, querying on that field, getting the documentID of the returned document, and using that in the delete. Like so:
(Swift)
func rejectFriendRequest(request: Request) {
DispatchQueue.global().async {
self.db.collection("requests")
.whereField("uniqueID", isEqualTo: request.uniqueID)
.getDocuments { querySnapshot, error in
if let e = error {
print("There was an error fetching that document: \(e)")
} else {
self.db.collection("requests")
.document(querySnapshot!.documents.first!.documentID)
.delete() { err in
if let e = err {
print("There was an error deleting that document: \(e)")
} else {
print("Document successfully deleted!")
}
}
}
}
}
}
The code could be cleaned up a bit, but this is the solution I came up with. Hope it can help someone in the future!
const firestoreCollection = db.collection('job_skills')
var docIds = (await firestoreCollection.where("folderId", "==", folderId).get()).docs.map((doc => doc.id))
// for single result
await firestoreCollection.doc(docIds[0]).delete()
// for multiple result
await Promise.all(
docIds.map(
async(docId) => await firestoreCollection.doc(docId).delete()
)
)
delete(seccion: string, subseccion: string)
{
const deletlist = this.db.collection('seccionesclass', ref => ref.where('seccion', '==', seccion).where('subseccion', '==' , subseccion))
deletlist.get().subscribe(delitems => delitems.forEach( doc=> doc.ref.delete()));
alert('record erased');
}
The code for Kotlin, including failure listeners (both for the query and for the delete of each document):
fun deleteJobs(jobId: String) {
db.collection("jobs").whereEqualTo("job_id", jobId).get()
.addOnSuccessListener { documentSnapshots ->
for (documentSnapshot in documentSnapshots)
documentSnapshot.reference.delete().addOnFailureListener { e ->
Log.e(TAG, "deleteJobs: failed to delete document ${documentSnapshot.reference.id}", e)
}
}.addOnFailureListener { e ->
Log.e(TAG, "deleteJobs: query failed", e)
}
}
I'm trying to fetch some related data based on the values returned by an observable from Firestore.
getChampionshipResults(minimumCompetionCount: number): Observable<User[]> {
const usersWithResults = this.afs.collection<User>('users')
.valueChanges()
.map(users => {
return users.map(user => {
this.afs.collection<Result>('results', ref => ref
.where('userUid', '==', user.uid)
.orderBy('position', 'asc')
.limit(minimumCompetionCount))
.valueChanges()
.take(1)
.subscribe(results => {
user.results = results;
});
return user;
});
})
.do(el => console.log(el));
return usersWithResults;
}
this gives me the correct results:
output
But I want to do some advanced sorting / filtering on the usersWithResults observable. The problem is that the user.results property is undefined at the time I want to sort it:
getChampionshipResults(minimumCompetionCount: number): Observable<User[]> {
const usersWithResults = this.afs.collection<User>('users')
.valueChanges()
.map(users => {
return users.map(user => {
this.afs.collection<Result>('results', ref => ref
.where('userUid', '==', user.uid)
.orderBy('position', 'asc')
.limit(minimumCompetionCount))
.valueChanges()
.take(1)
.subscribe(results => {
user.results = results;
});
return user;
});
})
.do(el => console.log(el))
// add sorting
.map(users => {
return users.sort((a, b) => {
// this is undefined
console.log(a.results);
// simplified dummy sort
return 1;
});
});
return usersWithResults;
}
I think the issue is coming from the fact that the inner observable has not completed, but I can't find the correct rxjs operator to do what I want.
Any advice would be appreciated.
Thanks.