Firestore: Getting a subcollection from a DocumentSnapshot - firebase

I'm using Firestore for backend for a Flutter project, and the database is nested to have sub-collections under documents.Here's the structure:
I have the data found from the Root collection, and from there I'm trying to get the sub-collection, but I can't figure how to.
There should be a get() method available but I'm not getting any.
Here's the code for the function:
void _getNext(DocumentSnapshot doc) async {
int v = doc.data.length;
String id = doc.documentID;
print('length = $v \t id= $id');
await doc.reference.get().then((val){
val.data.forEach((k, v){
print('k = $k & v = $v');
});
});
await doc.data.forEach((k,v){
print("new k = $k, v = $v");
});
await doc.reference.collection(id).getDocuments().then((val){
val.documents.forEach((e){
print("another k = $e");
});
});
}
And the only response I get from pressing the "Literature" document (from the root collection) is this:
I/flutter (13395): length = 0 id= Literature
There are several sub collections to this document but the returned length is zero. The other blocks of code work fine when I added dummy data to the fields, but that's not needed in my case.
Thank You for your help.

collection() returns a CollectionReference. As you can see from the API docs, neither CollectionReference, nor it superclass Query have a method called get(). I suspect you want to use getDocuments() instead.

There is no get() method for CollectionReference, you need a DocumentReference.
There is nothing wrong with the current situation, you probably want to use getDocuments() since there are no subcollections under your collection, but documents
Notice the alternating pattern of collections and documents. Your collections and documents must always follow this pattern. You cannot reference a collection in a collection or a document in a document.
from: Hierarchical Data

Related

Flutter Firestore - How to get data from a Document Reference in a Document Field?

I'm building a Self-learning app with differente questions types. Right now, one of the questions have a field containing a list of DocumentReferences:
In Flutter, I have the following code:
Query<Map<String, dynamic>> questionsRef = firestore
.collection('questions')
.where('lesson_id', isEqualTo: lessonId);
await questionsRef.get().then((snapshot) {
snapshot.docs.forEach((document) {
var questionTemp;
switch (document.data()['question_type']) {
....
case 'cards':
questionTemp = CardsQuestionModel.fromJson(document.data());
break;
....
}
questionTemp.id = document.id;
questions.add(questionTemp);
});
});
Now, with "questionTemp" I can access all the fields (lesson_id,options,question_type, etc..), but when it comes to the "cards" field, how Can I access the data from that document reference?
Is there a way to tell firestore.instance to get the data from those references automatically? Or do I need to make a new call for each one? and, if so, how can I do that?
Thank you for your support in advance!
Is there a way to tell firestore.instance to get the data from those
references automatically? Or do I need to make a new call for each
one?
No there isn't any way to get these documents automatically. You need to build, for each array element, the corresponding DocumentReference and fetch the document.
To build the reference, use the doc() method
DocumentReference docRef = FirebaseFirestore.instance.doc("cards/WzU...");
and then use the get() method on this DocumentReference.
docRef
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document exists on the database');
}
});
Concretely, you can loop over the cards Array and pass all the Futures returned by the get() method to the wait() method which "waits for multiple futures to complete and collects their results". See this SO answer for more details and also note that "the value of the returned future will be a list of all the values that were produced in the order that the futures are provided by iterating futures."

How to query an array of objects in a Firebase Cloud Function, to get a matching object and then update

I am using a scheduled task in a Firebase Cloud Function to query an array which contains a number of objects that need to be updated if a matching condition exists. My current attempt is using the 'array-contains' method to get the objects, then loop over them to find a matching condition which will then batch update the items. This is my data structure:
I need to find an object that is <= the current time, and also if the 'active' value = false.
export const liveMeetingsTrigger = functions.runWith( { memory: '1GB' }).pubsub
.schedule('every 1 minutes').onRun(async context => {
const now = admin.firestore.Timestamp.now();
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'liveMeetingDate').get();
const batch = admin.firestore().batch();
liveMeetings.forEach(doc => {
if(doc.data().liveMeetingDate <= now && doc.data().active == false){
batch.update(doc.ref,'active',true);
}
});
return await batch.commit();
});
I have also tried using an exact object in the query instead of just using 'liveMeetingDate', but still get no results back, any help would be great - thanks.
Debugging: As the array I am trying to reach is inside of the (map) object 'liveMeetings' i have tried the dot notation (liveMeetings.meeting) with no success. Also trying a new collection with the the 'meeting' array at top level has provided no success.
Simple logging in the console (liveMeetings.size) shows that nothing is being returned on the query, so therefore the logging does not even reach the loop in the code.
As explained in this anwser the following query will not work:
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'liveMeetingDate').get();
because the meetings array contain some objects, instead of "simple" or primitive data (e.g. string, number...).
You could query it with the exact objects, like:
const obj = {active: false, liveMeetingDate: ..., meetingId: ..., ....};
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'obj').get();
Another approach would be to create a new collection which contains the similar documents (same Document ID) but with a meeting Array that contains only the liveMeetingDate property.
Finally, note that since your Array is within a map, you need to do
await admin.firestore().collection('fl_content').where('liveMeetings.meeting', 'array-contains', ...).get();
(PS: I don't mark this question as duplicate since you expressly ask for more help in the comments of the duplicate question/answer)

Firebase Firstore subcollection

please how can I get all the value of my IndividualTaxData subcollection in Flutter.
First, you must get the reference to the parent document:
DocumentReference parentRef = Firestore.intances.collection('TaxData').document(taxDataId);
You can do the previous part with a direct reference to the document (like the code above) or with a query. Later, you must get the reference of the subcollection and the document that you get the information:
DocumentReference subRef = parentRef.collection('IndividualTaxData').document(individualTaxId);
And finally, get the data:
DocumentSnapshot docSnap = await subRef.get();
For you to return a simple document, you can use the following code for it.
var document = await Firestore.instance.collection('IndividualTaxData').document('<document_name>');
document.get() => then(function(document) {
print(document('character'));
// you can print other fields from your document
}
With the above code, you will reference your collection IndividualTaxData and then load it's data to a variable that you can print the values.
In case you want to retrieve all the documents from your collection, you can start using the below code.
final QuerySnapshot result = await Firestore.instance.collection('IndividualTaxData').getDocuments();
final List<DocumentSnapshot> documents = result.documents;
documents.forEach((data) => print(data));
// This print is just an example of it.
With this, you will load all your documents into a list that you iterate and print after - or that you can use with another method.
In addition to that, as future references, I would recommend you to check the following links as well.
Query a single document from Firestore in Flutter (cloud_firestore Plugin)
How to use Cloud Firestore with Flutter
Le me know if the information helped you!

Fetch collection startAfter documentID

Is there a way to fetch document after documentID like
private fun fetchCollectoionnAfterDocumentID(limit :Long){
val db = FirebaseFirestore.getInstance()
var query:Query = db.collection("questionCollection")
.startAfter("cDxXGLHlP56xnAp4RmE5") //
.orderBy("questionID", Query.Direction.DESCENDING)
.limit(limit)
query.get().addOnSuccessListener {
var questions = it.toObjects(QuestionBO::class.java)
questions.size
}
}
I want to fetch sorted questions after a given Document ID. I know I can do it using DocumentSnapShot. In order to fetch the second time or after the app is resume I have to save this DocumentSnapshot in Preference.
Can It be possible to fetch after document ID?
startAfter - > cDxXGLHlP56xnAp4RmE5
Edit
I know I can do it using lastVisible DocumentSnapshot . But I have to save lastVisible DocumentSnapshot in sharedPreference.
When app launch first time 10 question are fetched from questionCollection. Next time 10 more question have to be fetched after those lastVisible. So for fetching next 10 I have to save DocumentSnapshot object in sharedPreference. Suggest me a better approach after seeing my database structure.
And one more thing questionID is same as Document reference ID.
There is no way you can pass only the document id to the startAfter() method and simply start from that particular id, you should pass a DocumentSnapshots object, as explained in the official documentation regarding Firestore pagination:
Use the last document in a batch as the start of a cursor for the next batch.
first.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
=// Get the last visible document
DocumentSnapshot lastVisible = documentSnapshots.getDocuments()
.get(documentSnapshots.size() -1);
// Construct a new query starting at this document,
Query next = db.collection("cities")
.orderBy("population")
.startAfter(lastVisible) //Pass the DocumentSnapshot object
.limit(25);
// Use the query for pagination
}
});
See, here the lastVisible is a DocumentSnapshot object which represents the last visible object. You cannot pass only a document id. For more information, you can check my answer from the following post:
How to paginate Firestore with Android?
It's in Java but I'm confident you can understand it and write it in Kotlin.
Edit:
Please consider defining an order of your results so that all your pages of data can exist in a predictable way. So you need to either specify a startAt()/startAfter() value to indicate where in the ordering to begin receiving ordered documents or use a DocumentSnapshot to indicate the next document to receive, as explained above.
Another solution might be to put the document id into the document itself (as a value of a property) and order on it, or you can use FieldPath.documentId() to order by the id without having to add one.
You can also check this and this out.
There is one way to let startAfter(documentID) works.
Making one more document "get", then using the result as startAfter input.
val db = FirebaseFirestore.getInstance()
// I use javascript await / async here
val afterDoc = await db.collection("questionCollection").doc("cDxXGLHlP56xnAp4RmE5").get();
var query:Query = db.collection("questionCollection")
.startAfter(afterDoc)
.orderBy("questionID", Query.Direction.DESCENDING)
.limit(limit)
A simple way to think of this: if you order on questionID you'll need to know at least the value of questionID of the document to start after. You'll often also want to know the key, to disambiguate between documents with the same values. But since it sounds like your questionID values are unique within this collection, that might not be needed here.
But just knowing the key isn't enough, as that would require Firestore to scan its entire index to find that document. Such an index scan would break the performance guarantees of Firestore, which is why it requires you to give you the information it needs to perform a direct lookup in the index.

Get data from a collection with firestore ionic 4

I would like to get unique data from doc from a collection with firebase firestore
So i use to get all data:
ngOnInit() {
return this.firestore.collection('users').snapshotChanges()
.subscribe(data => {
console.log(data);
this.utilisateurs = data;
console.log(this.passided);
});
}
and this to get unique id :
this.passided = this.navParams.get('id');
And i tried to do this :
return this.firestore.collection('users').doc(this.passided).snapshotChanges()
but don't work, can you help me please?
snapshotChanges() is a method inside class AngularFirestoreCollection which returns an Observable of data as a DocumentChangeAction.
If you want to manipulate documents, then you can use the following methods:
set(data: T) - Destructively updates a document's data.
update(data: T) - Non-destructively updates a document's data.
delete() - Deletes an entire document. Does not delete any nested collections.
Therefore, this this.firestore.collection('users').doc(this.passided).snapshotChanges() wont work since snapshotChanges() is not a method in the document.ts
For reference:
https://github.com/angular/angularfire2/blob/master/docs/firestore/documents.md#snapshotchanges
https://github.com/angular/angularfire2/blob/master/src/firestore/collection/collection.ts#L97

Resources