Querying Firestore map fields in flutter - flutter

I am trying to retrieve posts in my Firestore database from a field called "bookmarks". The field "bookmarks" is of type map. When a user bookmarks a post, the field is updated with the deviceId being set to true like this:
▼ bookmarks
deviceId : true
When the post is unbookmarked, deviceId : trueis deleted and the bookmarks field remains empty like this:
bookmarks: {}
I am trying to retrieve posts from "bookmarks" where deviceID is set to true. My query however returns all posts including posts where bookmarks is empty.
This is my query :
QuerySnapshot snapshot = await postsRef
.document(postId)
.collection('users_posts')
.where('bookmarks', arrayContains: deviceId)
.orderBy('timestamp', descending: true)
.getDocuments();
Please help figure out what I am doing wrong. Attatched is the structure of my database.
Thank you.

Your bookmarks field is not an array, so you can't query it like an array. It's a map, and you can query for the values of nested fields of a map using dot notation:
.where('bookmarks.${deviceId}', isEqualTo: true)

I tried that notation but didn't work for me, here is the notation I used:
where("bookmarks.deviceId:true"), you can replace true with the value you are looking.

Related

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 filter firebase data using a where clause with FieldPat.documentId

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

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

Query a Map containing an ID saved in Cloud Firestore with Dart/Flutter

How to get a Map stored in Cloud Firestore with Dart/Flutter ?
I tried this but it only works with Array :
Firestore.instance
.collection('posts')
.orderBy('createdAt', descending: true)
.where("userlist", arrayContains: userId)
.snapshots(),
here is the map stored in firebase
It doesn't really make sense for userlist to be an array here. Firestore doesn't let you query for values of maps inside an array. Just store it as a map. If userlist was just a map of uid/value pairs, you could query it like this using dot notation:
Firestore.instance
.collection('posts')
.orderBy('createdAt', descending: true)
.where('userlist.' + userId, isEqualTo: true)
.snapshots(),
Just adding to Doug Stevenson's answer, I had to use the key isGreaterThan: '' in order to not get an empty array.
Firestore.instance
.collection('posts')
.where('userlist.' + userId, isGreaterThan: '')
.snapshots(),
I don't recommend your way of data model. Because of some limitations as below:
You can only have 20000 fields in a firestore document (including array fields).
You can't assign security rules to each field. Security Rules is a thing that you have to think of.
You can't query them.
So, better I would recommend you to use documents for each user.
I've tried both Doug Stevenson's and Dan James's answers and was getting empty array as a result.
I've finally received the correct result by using the where clause with not equal to ''

I am trying to use a field from the collection 'profile' say 'username' to filter the records from the main collection 'orders'

Is there a way to achieve this?
i have tried to assign the entry to a local variable but it doesn't work with crudmethods.
getData() async {
String userId = 'userId';
Firestore.instance.collection('user').document(userId).snapshots();
var snapshot;
var userDocument = snapshot.data;
String _myAddress = userDocument["address"];
return Firestore.instance
.collection('letters')
.where("source Box", isEqualTo: _myAddress)
.snapshots();
}
Yes, you should be able to use a document in the query of another document.
For this, you need to create a reference for one collection and use it in another one.
The below code is an example that you can give it a try adapting further for your case, but that I believe should help you.
// Create references to the profile and orders collections
var profilesRef = db.collection("profile");
var ordersRef = db.collection("orders");
// Create a query against the collection.
var query = ordersRef.where("username", "==", ).doc("username").get();
In the documentation Perform simple and compound queries in Cloud Firestore, there is more information and example of queries that should help you.
In addition to that, this below post from the Community can provide you some insights on how to achieve this type of query as well.
How can I get specific document data from firestore querysnapshot?
Let me know if the information helped you!