Create documents, sub collections in Firestore via flutter on screen loads - firebase

I want to achieve is when flutter screen loads a document should create in firestore in following order.
Document > Sub Collection > Document > Data Fields
I manage to create documents and sub collections in above order, but the first Document appear in italic. That's because the child collection, documents creating before parent document created.
But I couldn't able to fix the issue. I've modified the code now it's not even creating the document. Before this It created in italic mode. Now it's not at all.
Here is the code.
getCurrentUser().then((user) {
DocumentReference todayReference = firestoreInstance.collection('attendance').document(todayDate);
firestoreInstance.collection('profiles').where('user_id', isEqualTo: user).snapshots().listen((onData) {
onData.documents.forEach((f) {
CollectionReference todaySubCollection = todayReference.collection(f.documentID);
DocumentReference attendanceReference = todaySubCollection.document(f["name"].toString().toLowerCase());
Map<String,dynamic> mapData = new Map<String,dynamic>();
mapData['attendance_status'] = true;
mapData['in'] = true;
mapData['out'] = true;
firestoreInstance.runTransaction((transaction) async {
await transaction.set(attendanceReference, mapData);
});
});
});
});
Here getCurrentUser() is returning the logged in user id.
Each profiles assigned to a user.
So, What I'm trying to do is, once user logged in a document should create under attendance collection named today's date.
Then looping through each profiles where user_id is matched with logged in user, the matching results will be store as sub collection under today's date with profiles name field.
Then under the name (document), a transaction needs to run to set details like attendance_status, in & out.
Following images will show how previously documents created.
I need to find a way to create documents, collection without in italic mode. Any help would be appreciated.

"Italicized" documents are virtual/non-existent as mentioned in the docs. If a document only has a sub-collection, it will be a virtual/non-existent document. A workaround for this is by writing fields in the document, like what you've mentioned in the comments.

Related

How to get data from firebase sub collection?

Im trying to get data from firebase but im a bit struggling . I have this videos collection where I saving video ids and thenevery video has documetnfield and also a sub collection called user votes . In side that im saving the user votes from the ratingbarindicator
this is how to collection looks
So what I want is every document of the user votes sub colletion and then the each rating field .
but how can I do that ?What I want is calculating that together Hope anyone can help
To read the data from all (sub)collections with a given name, you can use a collection group query.
FirebaseFirestore.instance
.collectionGroup('uservotes')
.get()
...
Also see:
Is wildcard possible in Flutter Firestore query?
Fetch all the posts of all the users from Cloud Firestore
you can go through collection and document like this with firebase:
final querySnapshot = await FirebaseFiresotre.instance.collection('videos').doc([theVideoDocumentID])
.collection('uservotes').get();
final docs = querySnapshot.docs;
for(final doc in docs) {
final data = doc.data();
//handle each document here
}

Firebase and Kotlin: getting the id of a document issues

I have an interesting issue while playing with Firebase and Kotlin.
val docRef = db.collection("Year")
.document(DB_year.toString())
.collection("Month")
.document((DB_month+1).toString())
.collection("Day")
.document(today)
.collection("write")
.get()
.addOnSuccessListener { result ->
for(document in result) {
println("document_id : " + document.id)
}
}
If you get the document id with this code, you can get it normally.
enter image description here
enter code here
This code cannot get the document id.
val docRef = db.collection("Year")
.document(DB_year.toString())
.collection("Month")
.document((DB_month+1).toString())
.collection("Day")
.get()
Why is it like this?
my firestore collection
enter image description here
enter image description here
As shown in your Firebase console screenshot above, the documents in the Day collection are displayed with an italic font in the Firebase console: This is because these documents are only present (in the console) as "container" of one or more sub-collection but are not genuine documents.
If you create a document directly under the write collection with the full path Year/docYear1/Month/subDocMonth1/Day/subDcoDay1/write/writeDoc, no intermediate documents will be created (i.e. no document in the Month or Day collections).
The Firebase console shows this kind of "container" (or "placeholder") in italic in order to "materialize" the hierarchy and allow you to navigate to the write document but the Day document doesn't exist in the Firestore database. Hence the empty result for your second query
See this answer for more details.
Note that if you want to get the parent ids (docs and collections) for a doc in the write collection, you can use the parent properties of the DocumentReference and CollectionReference.
So you can do something like:
db.collection("Year")
.document(DB_year.toString())
.collection("Month")
.document((DB_month+1).toString())
.collection("Day")
.document(today)
.collection("write")
.get()
.addOnSuccessListener { result ->
for(document in result) {
println("Day_doc_id : " + document.reference.parent.parent?.id)
}
}

Flutter Firebase Cloud Firestore How to filter a stream with where() query using a subcollection

So i have in my Cloud Firestore I have a collection of recipes that contains documents(with casual ids) with different recipes. Every document has a 2 fields with the recipe name and the recipe duration.
Every document has also a collection named likedBy where there is documents that have as ids user ids and have a single field with the date of the like.
Now i want to return all recipes that have in their likedBy subCollection the userId.
i' ll write what i' ve tried with only essential code.
String userId= 'uiuu4fn3fff4fu';
Scaffold(
body:StreamBuilder(
stream: THE STREAM THAT I NEED,
builder:(context,snapshot){
return ListView.builder(
itemCount: snapshot.data.documents.length
itemBuilder:(context,index){
return Column(children:[
Text(snapshot.data.documents[index]['recipeName']),
Text(snapshot.data.documents[index][recipeDuration]),]) } ) } ) )
What i want is to return only documents that have in their likedBy subCollection a specific user uid.
I' ve tried with this stream
Firestore.instance.collection('recipes').parent().collection('likedBy').where(FieldPath.documentId,
isEqualTo,userId).snapshots()
But it doesn' t work and i have no idea what else i can try.
Any help is highly apprecieted.
Items should not be added by the users but by admins, that means that there will be a list of items added by admins and a list of users that can add them in favorites and what i want to achieve is that users can see all their favorites in the order in which they saved them. To be clear i want something like Instagram functionality to save posts.
So you want a way to query subcollection. To do that simply use collectionGroup method:
db.collectionGroup('likedBy').where('userId', '==', '1');
To order by a value use timestamp:
// add to your document
db.collection("items")
.add({...item, created: firebase.firestore.Timestamp.fromDate(new Date()) })
and to orderby this value use orderby:
db.collection("items")
.orderBy("created", "asc")
What you might want to do instead is have a 'likedBy' property on the recipe document instead of its own sub-collection. If it is just a list of userids that should be no problem. Then you can just say recipeCollection.where(likedBy, array contains: userId)

Reducing the number of reads firestore

I have a dating kind of app (without chat support) in which I am showing list of all the profiles that matches certain criteria to user. I have used realtime snapshot listener for this.
query.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
return;
}
if (snapshot != null && !snapshot.isEmpty()) {
List<FeedDetails> feedDetailsList = new ArrayList<>();
for (DocumentSnapshot document : snapshot.getDocuments()) {
FeedDetails feedDetails = document.toObject(FeedDetails.class);
feedDetailsList.add(feedDetails);
}
feedItemAdapter.updateData(feedDetailsList);
}
}
});
In individual profile doc, I have 10-12 field with one online/offline info field. So lets say if no other field changed but online/offline status changed for some profiles then listener is going to read that document again. Is there any way to cut down those reads?
I have a 10-12 field with one online/offline info field. So let's say if no other field changed but online/offline status changed for some profiles then the listener is going to read that document again.
There is no way in Cloud Firestore in which you can listen only to a set of properties of a document and exclude others. It's the entire document or nothing. So if this field online=true is changed into online=false, then you will get the entire document.
Cloud Firestore listeners fire on the document level and always return complete documents. Unfortunately, there is no way to request only a part of the document with the client-side SDK, although this option does exist in the server-side SDK's select() method.
If you don't want to get notified for specific fields, consider adding an extra collection with documents that will contain only those fields. So create that additional collection where each document just contains the data you don't need. In this way, you won't be notified for online/offline changes.

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.

Resources