How to filter firebase data using a where clause with FieldPat.documentId - flutter

Im trying to retrieve data with
Stream<List<User>> getUsers(User user) {
List<String> userFilter = List.from(user.swipeLeft!)
..addAll(user.swipeRight!)
..add(user.uid!);
return _firebaseFirestore
.collection('users')
.where('interestedIn', isEqualTo: 'HIRING')
.where(FieldPath.documentId, whereNotIn: userFilter)
.snapshots()
.map((snap) {
return snap.docs.map((doc) => User.fromSnapshot(doc)).toList();
});
}
I get an error
An error occurred while parsing query arguments, this is most likely an error with this SDK.
Invalid query. When querying with FieldPath.documentId() you must provide a valid document ID, but it was an empty string.
My data in firebase is structured as follows
How can I fix this?

In firebase you cannot filter with the document id, you can only filter with the fields in the document, a way a simple solution will be to generate a unique id by your self using packages like uuid and then save the document with the id and also save the id inside the document fields then you can filter with the id in the document field

Related

How to get the List of Document Reference from a Snapshot Document resulting from a Firestore query

I can't seem to figure out how to get a List after querying a specific Firestore collection.
I want the function to:
Query the 'chat' collection on the field 'users'.
Retrieve only the document (should be only one but could be an error and there's more than one) where users, which is a LIST of Document Reference, matches two specific references: chatUserRef and authUserRef
The function should return a list of the Document References referring to this chat collection
This is what I am trying:
import 'package:cloud_firestore/cloud_firestore.dart';
Future<List<ChatsRecord>> getChatDoc(
DocumentReference chatUserRef,
DocumentReference authUserRef,
) async {
// Add your function code here!
final firestore =
FirebaseFirestore.instance; // Get a reference to the Firestore database
final collectionRef =
firestore.collection('chats'); // Get a reference to the collection
final filteredDocuments = collectionRef.where('users', isEqualTo: [
authUserRef,
chatUserRef
]); // Use the `where` method to filter the list of documents
final queryDocuments = await filteredDocuments
.get(); // You can then use the get method on this Query object to retrieve the list of documents AS a Snapshot Document (this is NOT a list of the Documents themselves).
List<ChatsRecord> listChatDocs = [];
// Cast the Snapshot Documents to a map
// Extract the Document Reference ID
// cast the query document to a map for e
// (should I forEach?)
List<ChatsRecord> listChatDocs = queryDocuments.docs.map((e) {
return FirebaseFirestore.instance.doc('chats/$e.id');
}).toList();
return listChatDocs;
}
Try using the arrayContainsAny instead of EqualTo in your where clause.
Like this:
final filteredDocuments = collectionRef.where('users', arrayContainsAny: [
authUserRef,
chatUserRef
]);

How to check if data matches field in array in flutter

I have a Firebase Firestore configuration as show below:
How would I check if eventParticipants's contains a uid matching the current user's uid? The below code used to work when eventParticipants used to be an array of user id's, but since creating more detailed array objects, the code seems to not work:
data["eventParticipants"]
.contains({
"uid": FirebaseAuth
.instance.currentUser!.uid
.toString()
})
Usually the above code would return a bool, and I would use the result in a ternary operator to load a widget, however, I am unable to rework the logic with the new array structure. Subsequently, how would I remove an array object if it's uid field matches the current user's id?
FirebaseFirestore
.instance
.collection(
"events")
.doc(document.id)
.set(
{
"eventParticipants":
FieldValue
.arrayRemove([
{
"uid": FirebaseAuth
.instance
.currentUser
?.uid
}
])
},
SetOptions(
merge: true),
);
Any pointers would be appreciated!
The arrayRemove (and arrayUnion and arrayContains) operators expect you to pass the complete, exact contents of the item in the array. In your case it looks for an item in the array with a single field uid with the value you pass.
So unless you know the values of all properties of the array item that you want to remove, you'll have to:
Read the document with the array in it.
Manipulate the array in your application code.
Write the entire array back to the database.
Also see:
Firestore, how to structure a "likedBy" query
Firestore conditional array query
Firestore array-contains-any is not working properly

How to correctly fetch data from firebase using where clauses and custom filters from firebase using flutter

Im trying to fetch data using
Stream<List<User>> getUsers(User user) {
return _firebaseFirestore
.collection('users')
// .where('interestedIn', isEqualTo: _selectInterest(user))
.snapshots()
.map((snap) {
return snap.docs.map((doc) => User.fromSnapshot(doc)).toList();
});
}
The filter used in the where clause is as follows
_selectInterest(User user) {
if (user.interestPreference == null) {
return ['HIRING', 'WORK'];
}
return user.interestPreference;
}
In firebase I store interestPreference as an Array with 'HIRING' as the only element in the current user's data, when I try to fetch users with 'HIRING' in their interestedIn which is a string I dont get any data. But when I hardcode the where clause as
.where('interestedIn', isEqualTo: 'HIRING')
I get the data, Can anyone help me solve my dilemma?
From that last query, it sounds like the interestedIn field in your database is a single string value, like interestedIn: "HIRING".
Your current query returns documents where interestedIn is an array with exactly the two values you specify, so interestedIn: ['HIRING', 'WORK']
If you want to return all documents where interested in is either "HIRING" or "WORK", you can use an IN condition:
.where('interestedIn', whereIn: ['HIRING', 'WORK'])
Or with your helper function:
.where('interestedIn', whereIn: _selectInterest(user))

How to arrange documents in Firestore using Flutter through custom document IDs?

I want to order the documents in Firestore. The default Firestore documents list consist of alphabetic characters which get created automatically. But I don't want that. I just want to see my newly added document added at the top of my documents list. How do I do that in flutter? It would be very helpful if you provide me with a code for that. The code I use to create a collection is:
Future<void> userSetup() async {
String user = FirebaseAuth.instance.currentUser?.displayName as String;
CollectionReference users = FirebaseFirestore.instance.collection(user);
final hours = time?.hour.toString().padLeft(2, '0');
final minutes = time?.minute.toString().padLeft(2, '0');
users.add({
"customerId": FirebaseAuth.instance.currentUser?.uid.toString(),
"customerName": FirebaseAuth.instance.currentUser?.displayName,
"customerEmail": FirebaseAuth.instance.currentUser?.email,
"selectedTime": '${hours}:${minutes}',
"selectedDate": DateFormat('dd/MM/yyyy').format(date!),
});
return;
}
But I am unable to set my own document id. Please help me with the issue. Thanks in Advance
From the Flutterfire documentation, the set() method is the one you should be using to be able to specify your own document IDs instead of add(). Keep in mind that if the document ID you specify already exists in your database, the whole existing document will be replaced. This is a sample usage as found in the documentation:
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser() {
return users
.doc('ABC123')
.set({
'full_name': "Mary Jane",
'age': 18
})
.then((value) => print("User Added"))
.catchError((error) => print("Failed to add user: $error"));
}
It seems that documents are ordered alphabetically in the Firestore console, so your custom document IDs should follow alphabetical order as you require. Not to be confused with retrieving documents from Firestore in a particular order, which is done with the orderBy() method.

Using fields in a document for another query

I have 2 collections, one called Timeline and one called Posts. The first one is very simple, having 2 fields: 'PostId' and 'OwnerId', while the second one is a little bit more complex but it is not important for the purpose of my question.
Using 'OwnerId' and 'PostId' I can get a specified post in the collection Posts.
What I want to do is getting all the docs in timeline of a specified user, for each doc use it to get the post infos in Posts collection, and order the posts in descending timestamp, but I can't find a smart and effective way to do so.
To get all the docs of a specified user in Timeline I write:
QuerySnapshot snapshot = await timelineRef
.doc(currentUserID)
.collection('timelinePosts')
.get();
And to get a specified post from Posts collection I write:
QuerySnapshot snapshot = await postsRef
.doc(ownerId)
.collection('userPosts')
.doc(postId)
.get();
How can I mix these two to get the result I want? Thank you
There is no concept of a server-side join in Firestore, nor is there a way to filter the documents returned based on information in documents in another collection. All Firestore queries can do is evaluate the literal data in the candidate documents (through an index) and filter based on that.
So you will either have to duplicate the data to filter on in each userPosts document, or perform a so-called client-side join - with the latter being the most reasonable option for this use-case as far as I can see.
You'll end up with individual get() calls for the documents, or a bunch in in queries on the FieldPath.documentId() you get from timelinePosts, and then merge the results in your application code.
At the moment I found a solution that is not very elegant but at least is working:
QuerySnapshot snapshot = await timelineRef
.doc(widget.currentUser.userID)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.get();
List<TimelineItem> timelineItems =
snapshot.docs.map((doc) => TimelineItem.fromDocument(doc)).toList();
List<PostWidget> postsTemp = [];
for (var element in timelineItems) {
DocumentSnapshot documentSnapshot = await postsRef
.doc(element.ownerId)
.collection('userPosts')
.doc(element.postId)
.get();
postsTemp.add(PostWidget(Post.fromDocument(documentSnapshot)));
}
I added timestamp field to my timelinePosts, created a class to contain the data from the first query, and then I did a second query based on the parameters I got on the first one for each doc.
Hopefully I'll find a more efficient solution but at the moment I use this