Querying FireBase FireStore Document in Flutter - firebase

I wants to achieve one query which i'm trying for few hours in FireStore Database, Please check the below image,
The Query I want to do is, In the document which I marked, will have a list of group id (combination of 2 userIds in each , userId1-userId2), I want to query this document by having one userId that contains in the group id (before or after hyphen) and returns a list of snapshots , which should be used for StreamBuilder in a list widget.
For Example:
I may have 'abcdefghijkl' userId, I want to check each document ID that contains this userId, and returns a list of snapshots which matches.
Thanks in Advance!

I don't think the query you want is possible, since there is no operation to check if a document id contains something. I would recommend adding a participants array to the document and than use an array-contains query, see also here.

There are several ways to achieve what you're after - a list of users relating to a given document - but I think you should rethink your data structure first. Each message document should have a users field that is an array of Firestore document IDs which would contain 1 or more document IDs of the users that this message relates to.
With that, you can easily query the database for all messages where a given user is referenced:
final messages = db
.collection('messages')
.where('users', arrayContains: userId)
.get();
BTW, to take things a step further, I would structure my data like this:
user {
displayName: '',
email: '',
messages: [
{
authorId: userId,
content: '',
users: [userId, ...],
sentOn: '',
},
...
]
}
With this you can do two things:
Fetch all messages created by the user:
final userMessages = db.doc(userId).collection('messages').get();
Fetch all messages where user participated:
final allUserMessages = db
.collectionGroup('messages')
.where('users', arrayContains: userId)
.get();
Check out the Firestore docs, as they have plenty of examples there.

Related

Retrieve field information form Firestore database

So I want to retrieve name of a user which is inside a field in firestore.
The whole sequence in given in image below.
I want to get the string value 'a' which is inside (chatroom->a_qaz->users->'a').
I am trying to get it with this code but its not working. How to get the field information.
getOtherUserByUsername() async {
return await FirebaseFirestore.instance
.collection("chatroom")
.doc("chatRoomId")
.get();
First of all, let's get the document from your collection.
collection.doc(), as per reference, gets the actual ID as parameter. In your case, you need to specify "a_qaz". After that, you get the document and then you can read the fields. Your code should look like this:
let chatRoom = await FirebaseFirestore.instance
.collection("chatroom")
.doc("a_qaz")
.get();
let users = chatRoom.get("users");
users will store, then, the list of users that's in that field.

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)

How to filter a stream of snapshots based on multiple values in an array?

I am very new in this firestorm and database management.
In my code, there are plenty of group documents in a groups collection
Now what I want to do is, show a person from users collection the specific groups he is connected to.
here is how the groups uids look:
here is how the document of user looks:
The code I use to create the group document is this
after creating the document I add the uid of the new document to the user's array called groups.
Future createNewGroupData(String groupName) async {
String _userID = await getUID();
return await groupCollection.add({
'creatorUID': _userID,
'groupName': groupName,
'teachers': [_userID],
}).then((ref) => {
userCollection.document(_userID).updateData({'groups': FieldValue.arrayUnion([ref.documentID])})
});
this is the structure of how I create the document for each user,
Future updateUserData(int avatarID, String firstName, String lastName, String status, List groups, String school) async {
return await userCollection.document(uid).setData({
'avatarID': avatarID.round(),
'firstName': firstName,
'lastName': lastName,
'status': status,
'groups': groups,
'school': school,
});
}
How I implemented that was when the user was added to a group. The uid of that group was appended in an array under that user's documents.
Now what I want to do is get a stream of snapshots of the groups only uids are present in that user's array. I can not really find any way to implement that.
If I understand correctly you want to load the group documents whose document ID matches what you find in the groups field of a user. If that is the case, you are looking for a combination of FieldPath.documentId and an in query.
Something like:
Firestore.instance.collection('groups').where(FieldPath.documentId, whereIn: listOfUids)
As shown in the Firestore documentation on query limitations this will only work for up to 10 document IDs:
Cloud Firestore provides limited support for logical OR queries. The in and array-contains-any operators support a logical OR of up to 10 equality (==) or array-contains conditions on a single field. For other cases, create a separate query for each OR condition and merge the query results in your app.
If you have more document IDs, you will need to fire a separate query for each (up to) 10.
Also see:
Flutter Firebase get documents by array of IDs

Firestore query results problem (sometimes results contain continuing docs sometimes irrelevant results)

i have users collection and six docs. Each doc has a String displayname field.
My users displaynames
"aaaa bbbb"
"ahmet mehmet"
"asdf"
"bengu"
"berktug"
"cagdas"
final usersRef = Firestore.instance.collection('users');
handleSearch(String query) {
Future<QuerySnapshot> users = usersRef
.where("displayName", isGreaterThanOrEqualTo: query.toLowerCase())
.getDocuments();
setState(() {
searchResultsFuture = users;
});
}
when the query="aaaa" the users has 6 snapshots.(all users)
when the query="bengu" the users has 3 snapshot("bengu",
"berktug","cagdas")
when the query="mehmet" the users has not any snapshot.(my first user
displayname is "ahmet mehmet")
Firestore query results, sometimes contain continuing docs, sometimes irrelevant results). actually the displaynames contain some turkish chars but i changed it to solve the problem. but i dont.
Thank you
The results you get are the correct ones: your query compares the entire string query.toLowerCase() to the entire values of the displayName field in your Firestore database. The query does not compare any substring of either the query.toLowerCase() value or the displayName field value.
In the case of the mehmet value, there is no document matching the query since, in alphabetical order, mehmet is after cagdas.
If you try with query="ahmet mehmet" you will get 5 results.
If you are looking for a full-text search mechanism, have a look at this documentation item or at this article.

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