4

I'm working on a Flutter app where each user can create projects, and share projects with other users. I've created a 'shares' collection, where each user's ID is a document, and within that document, all project IDs that have been shared with that user are collected like so, with a boolean that represents whether or not the share has been accepted yet:

shares collection

Next, I created a collection of the projects themselves, like so:

projects collection

Now, I'd like to query the 'projects' collection and return only the projects that are in a given user's 'shares' list. First off, how can I get each document in the share list's ID? And secondly, is it possible to compare that ID to the contents of a List using a .where() clause?

I've been trying something like this, but to no avail:

  Stream<List<Map<String, dynamic>>> getListOfProjectsForUser({@required List<String> shares}) {
    var ref = _firestore.collection('projects');
    return ref
        .where(shares, arrayContains: ref.id)
        .snapshots()
        .map((QuerySnapshot snapshot) => snapshot.docs.map((DocumentSnapshot doc) => doc.data()).toList());
  }

I also tried this:

Stream<List<Map<String, dynamic>>> getListOfProjectsForUser({@required List<String> shares}) {
  var ref = _firestore.collection('projects');
  return ref
      .where(shares, arrayContains: FieldPath.documentId)
      .snapshots()
      .map((QuerySnapshot snapshot) => snapshot.docs.map((DocumentSnapshot doc) => doc.data()).toList());
}

Is what I'm trying to do even possible? I've been messing with this for two days and my head's exploding. Any help would be greatly appreciated. Thanks in advance.

ConleeC
  • 337
  • 6
  • 13
  • Doug and Frank got me very close, but I have a follow-up questions here: https://stackoverflow.com/questions/64346614/flutter-firebase-retrieve-a-list-of-documents-limited-to-ids-in-an-array-fo – ConleeC Oct 14 '20 at 04:32

3 Answers3

7

You'll need two operations.

  1. Read the document for the user, to determine the list of project IDs.
  2. Perform a in query for the project documents matching those IDs. The in operator accepts up to 10 IDs, so if you have more than 10 projects you'll need multiple queries and merge the results in your application code.
var citiesRef = db.collection("projects");

citiesRef.where(FieldPath.documentId, arrayContains: ['project1id', 'project2id']);

Also see:

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you Frank. Between you and Doug Stevenson, I was able to get my initial question answered. But you have both brought it to my attention that queries based on Lists are limited to a maximum of 10 entries. Which leaves me with one further question. I've posted my new code and question, if you have any further guidance. Thanks again, in advance. – ConleeC Oct 14 '20 at 01:07
5

First off, how can I get each document in the share list's ID?

For this, you're required to actually query the entire collection. You can iterate the results to collect the IDs of each document. There is no easy way to just get a list of IDs directly from web and mobile client code. See: How to get a list of document IDs in a collection Cloud Firestore?

And secondly, is it possible to compare that ID to the contents of a List using a .where() clause?

If you have a list of document ID strings in memory that could be any length, you will need to perform a query filtering projects for "projOwner" for each individual ID. There are no SQL-like joins in Firestore, so you can't simply join the two collections together with a single query.

Here's how you do a single one - you have to call out the name of the field to filter on:

firestore
    .collection("projects")
    .where("projOwner", isEqualTo: id)

If you have 10 or less share IDs in the list, you can use an "in" query to find matches from projects, and it will not work with any more.

firestore
    .collection("projects")
    .where("projOwner", whereIn: listOfIds)

So, if you think the list could ever be larger than 10, you should just start by performing individual queries for each share ID.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • You got me 90% of the way there, but I have a follow-up question, if you please have any guidance? I've posted my new code, based on your suggestions, with the question at the end. – ConleeC Oct 14 '20 at 01:01
0

if 'arrayContains' is not working try 'whereIn'.

var citiesRef = db.collection("projects");

citiesRef.where(FieldPath.documentId, whereIn: ['project1id', 
'project2id']);
Mudassar Ashraf
  • 734
  • 7
  • 8