Attach listener to any Firestore collect change

I have a document structure of:

/uploads/${USER_ID}/items/${UUID}

I can successfully listen for changes on the document at the end of the path, for example

firestore
    .collection("uploads")
    .document(configService.getUserId())
    .collection("items")
    .whereEqualTo("owner", configService.getUserId())
    .addSnapshotListener(this::onEvent);
}

But how can I listen to all changes on /uploads/* for changes, without iterating through all documents attaching listeners (Seems very inefficient)?

I naively tried

firestore
    .collection("uploads")
    .addSnapshotListener(this::onEvent);
}

However, it’s not triggered when changes are made down the path.

Answer

What you’re looking for is called a collection group query. With that you can listen to all documents across all items subcollections with:

final Query items = db.collectionGroup("items").whereEqualTo("owner", configService.getUserId());
final ApiFuture<QuerySnapshot> querySnapshot = items.get();
for (DocumentSnapshot document : querySnapshot.get().getDocuments()) {
  System.out.println(document.getId());
}

If you need to know to which upload a specific item belong, you can call document.getReference() to get a reference to the item document, and then follow the getParent trail from there.