How to create custom index for firestore query - firebase

I need to be able to retrieve some data from my cloud firestore database where certain conditions are met and then order that data but I am unable to get my query to work. I've read that if you simply run the query then your log should provide you a link to automatically create the custom index but unfortunately within my flutter logs or the android studio logcat i'm not getting any links. I know you can create the custom indexes manually in the firebase console so i'm happy to do this but I dont know how I would create the index for this.
firestore.collection('users').where('organisation_id', isEqualTo: _authenticatedUser.organisationId)
.orderBy('first_name').getDocuments()
I just need to know how to create the composite index for this query so that it will work in my app

I was able to get a link in my console log by surrounding the firestore query with a try catch and printing out the platform exception which generated the direct link, thanks to Dougs answer
try {
snapshot = await firestore.collection('users').where(
'organisation_id', isEqualTo: _authenticatedUser.organisationId)
.orderBy('first_name').getDocuments();
} catch(e){
print(e);
}

Related

Firebase cross-service Security Rules not working in application

I'm trying to use the new Firebase cross-service Security Rules (https://firebase.blog/posts/2022/09/announcing-cross-service-security-rules) but I having some problems with Storage Rules accessing to Firestore data.
The problem seems to be with userIsCreator() function
match /certification/{certificationId}/{fileId} {
function userIsCreator() {
let certification = firestore.get(/databases/(default)/documents/certifications/$(certificationId));
return firestore.get(certification.data.creatorRef).id == request.auth.uid;
}
allow read, write: if userIsCreator()
}
The content of the Firestore Document is:
{
"data": {
othersValues,
"creatorRef": "/databases/%28default%29/documents/users/CuutSAtFkDX2F9T8hlT4pjMUByS2"
}
"id": "3EhQakDrsKxlacUjdibs"
"__name__":
"/databases/%28default%29/documents/certifications/3EhQakDrsKxlacUjdibs"
}
The creatorRef variable is a reference to a Firestore Document to user. Inside Users collection, the doc id is the UID of an user, so I'm obtaining the creatorRef of an item and then checking if the id of that user collection referenced is the same UID that user logged in.
The same function is working for Firestore Rules to avoid updating certification document if not the creator, without any problem.
It seems to be a problem calling to firestore.get to creatorRef after obtaining it but it not make sense!
Tested:
If I use Firestore Storage Rules validator, it is not failing and it says I have access to that resource from the UID typed in the tester (for other UID is failing as expected). But in my app, even logged in with creator user is getting permission error.
If changing the function to only one call directly to the Users collection id (return firestore.get(/databases/(default)/documents/users/CuutSAtFkDX2F9T8hlT4pjMUByS2).id == request.auth.uid;), it is working in the tester and my app. But it isn't a solution because I need to get first the Users collection ref for the creator!
For original function in the tester It's getting the variables as expected and returning true if simulate the creator UID! But for any reason, in the real app access it is getting unauthorized if making both calls!
Firebaser here!
It looks like you've found a bug in our implementation of cross-service rules. With that said, your example will create two reads against Firestore but it's possible to simplify this to avoid the second read.
Removing the second read
From your post:
return firestore.get(certification.data.creatorRef).id == request.auth.uid;
This line is a bit redundant; the id field is already contained in the certification.data.creatorRef path. Assuming you are indeed using Firestore document references, the format of creatorRef will be /projects/<your-project-id>/databases/(default)/documents/users/<some-user-id>. You can therefore update your function to the following:
function userIsCreator() {
let certification = firestore.get(/databases/(default)/documents/certifications/$(certification));
let creatorRef = certification.data.creatorRef;
// Make sure to replace <your-project-id> with your project's actual ID
return creatorRef ==
/projects/<your-project-id>/databases/(default)/documents/users/$(request.auth.uid);
}
I've tested this out in the emulator and in production and it works as expected. The benefit of doing it this way is you only have to read from Firestore once, plus it works around the bug you've discovered.

How to update a list automatically using Firestore Stream?

In my app, I am now using a "refresh function" to update a list in Provider. When the user swipe, I call Refreshlist in my provider and with NotifyListeners() it updates my UI just fine. (The UI is linked to the list _myEleves).
I am afraid that users might use this "refresh" button too many times making unnecessary calls and "reads" on firebase and so increasing artificially the number of reads so the costs of Firebase.
Here is the code :
Future<void> refreshEleveList() async {
final DocumentSnapshot<Map<String, dynamic>> docInfo =
await FirebaseFirestore.instance
.collection('familyAccounts')
.doc(_accountEmail.toLowerCase())
.get();
_mesEleves = (docInfo['mesEleves'] as List<dynamic>)
.map((data) => Eleve.fromMap(data))
.toList();
notifyListeners();
}
I have been reading about STREAMS a lot, but I just can't get it right on how to start this stream, the listening to the changes on Firebase inside my PROVIDER file, so that changes will be made to "_myEleves" list.
What I want to do is that each time a change on firebase happens, it updates my list "_myEleves". Is there a simple way to do this ?
My Provider covers the whole app (I use it in the MAIN file). I thought of adding a StreamProvider, but the thing is I don't want this stream to start until user is authentified etc... and userInfo is first downloaded.
Right now : when user logs in : it downloads from firebase all necessary info, _mesEleves being one of them (This is for a teacher). Whenever a new student joins the group, it modifies firebase and so it should stream down this info into "myEleves" list on the teacher account.
Anybody can help ?

Flutter Firebase local change doesn't update listener stream

I'm relying on Firebase Firestore offline capabilities, so I'm not using await on my queries as stated on the Access Data Offline Firebase doc. I'm expecting that when I write something I'll get an immediate reflection on my read stream, however, I'm only getting an update when the server/remote has been updated. Basically:
Update something in the DB. Note, I'm not using await
_db.doc(parentDoc).collection(DocInnerCollection).doc(childDoc).update({
"name": value,
});
I expect my listeners to be updated immediately. Note I've set the includeMetadataChanges to true as stated in the above doc.
_db.doc(parentDoc)
.collection(DocInnerCollection)
.orderBy('start_date', 'desc')
.limitToLast(1)
.snapshots(includeMetadataChanges: true)
.map((snapshot) {
print(snapshot.metadata.isFromCache)
});
However, I get no such update and instead I only get an update when the server has been updated.
You're requesting only one document with .limitToLast(1), yet are not providing a sort order for your query. This essentially means that you'll get a random document from your collection, and the chances of that being the newly updated document are close to zero.
If you want the latest (not just last) document, you need some ordering criteria to determine what latest means. Typically you'd do this by:
Adding a lastUpdated field to your documents, and setting that to firebase.firestore.FieldValue.serverTimestamp().
Ordering your query on that timestamp with orderBy('lastUpdated', 'desc').
And then limiting to the first result with limit(1).

Fetching rows with a particular value of child Firebase Flutter

I'm working with Flutter and Firebase (Real-time database). There is some data stored in the db and I want to compare the email (child) of the parent and only want to display the parents containing that particular email. Currently, it is fetching all rows. I think fetching through key value pair would do the work. But I dont know the syntax and unable to find help regarding it. Please help me out.
void myfunc() {
databaseReference.once().then((DataSnapshot snapshot) {
print('Data : ${snapshot.value}');
});
}
try with
yourRef.orderByChild("email").equalTo('abs#abc.com');
Read Query Data

Uploading Set<String> gives error in flutter

Okay, so I'm creating chips tiles that are stored in a Set, but when I try to upload it to firebase I get this error "Unhandled Exception: Invalid argument: Instance of '_CompactLinkedHashSet'"
I isolated the code and simplified it so it looks like this:
...
Set<String> _tags = <String>{};
_tags.add('Test1');
Firestore.instance
.collection('tags')
.document(tagsID)
.setData({
'tags': _tags,
});
...
I've tried debugging it, and I can't get it to upload so is there any other kind of way I can get the data from the set and upload it to Firebase? Another data type I can use that Firebase will accept?
Take a look at the data types that Firestore supports. Sets are not supported. You could convert your set to a JSON string, or to a list:
List<String> tagsList = List<String>.from(_tags);
Adding on to Bryson Thill's answer, if you need to use Sets in your code, I'd recommend you use the toList() method before uploading to Firestore.
I would suggest you following the answer from this question:
Adding an Object to Cloud Firestore using Flutter
And you can basically upload any data you want

Resources