1
votes

I´ve a role based data model on Firestore according to googles suggestion here: https://firebase.google.com/docs/firestore/solutions/role-based-access

Security rules are set up correctly and work fine. But now I´ve the problem on how to query for the roles.

This is my data model (one sample document):

id: "1234-5678-91234",
roles:
    userId_1:"owner",
    userId_2:"editor
title: "This is a sample document"

And this is my Firestore Query in Flutter which gets all documents for a specific user by its ID if the user has assigned the role "owner" for the document:

return firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "owner")
.snapshots().map((snapshot) {
  return snapshot.documents.map((catalog) {
    return SomeDocumentObject(...);
  }).toList();
});

My problem now is, that I need some kind of "OR" clause - which does not exist as far as I know. The query above only retrieves documents for users with role "owner" but I need a query that also retrieves the document if the userId is associated with the role "editor".

I´ve tried "arrayContains:" which also doesn´t seem to work (cause it´s a map).

I´ve read about solutions with two independent queries which doesn´t sound like a good solution due to a lot of overhead.

Maybe someone of you have a hint for me? :)

Thanks & best, Michael

2

2 Answers

2
votes

Firestore doesn't currently have any logical OR operations. You'll have to perform two queries, one for each condition, and merge the results of both queries in the client app.

2
votes

This is the final solution using RxDart, Observables and .combineLatest() - maybe it helps someone out there:

@override
Stream<List<Catalog>> catalogs(User user) {

    // Retrieve all catalogs where user is owner
    Observable<QuerySnapshot> ownerCatalogs = Observable(firestore
        .collection(path)
        .where("roles.${user.firebaseUserId}", isEqualTo: "owner")
        .snapshots());

    // Retrieve all catalogs where user is editor
    Observable<QuerySnapshot> editorCatalogs = Observable(firestore
        .collection(path)
        .where("roles.${user.firebaseUserId}", isEqualTo: "editor")
        .snapshots());

    // Convert merged stream to list of catalogs
    return Observable.combineLatest([ownerCatalogs, editorCatalogs],
        (List<QuerySnapshot> snapshotList) {
      List<Catalog> catalogs = [];

      snapshotList.forEach((snapshot) {
        snapshot.documents.forEach((DocumentSnapshot catalog) {
          catalogs.add(Catalog(
            id: catalog.documentID,
            title: catalog.data['title'],
            roles: catalog.data['roles'],
          ));
        });
      });
      return catalogs;
    }).asBroadcastStream();
  }