簡體   English   中英

如何在不重復讀取量的情況下同時訂閱整個集合和其中的文檔?

[英]How to subscribe to an entire collection and a document within it simultaneously without duplicating the amount of reads?

我正在為學習目的編寫一個帶有 Flutter 和 Firestore 的跨平台待辦事項應用程序。 目前,我有以下設計,我想知道是否有更好的選擇。

該應用程序的主屏幕之一顯示所有任務的列表。 它通過訂閱相應的 Firestore 集合來執行此操作,為簡單起見,我們將其稱為/tasks

FirebaseFirestore.instance.collection("tasks").snapshots()

任務列表視圖中的每個磁貼都可以單擊。 單擊一個磁貼會打開一個新屏幕(帶有Navigator.push ),顯示有關該特定任務的詳細信息。

重要的是,這個屏幕也需要實時更新,所以僅僅從主屏幕傳遞(本地,不可變的)任務 object 是不夠的。 相反,此屏幕會訂閱與該任務對應的單個 Firestore 文檔。

FirebaseFirestore.instance.collection("tasks").doc(taskId).snapshots()

這在邏輯上對我來說是有意義的:詳細信息頁面只需要知道那個特定的文檔,所以它只訂閱它以避免接收不必要的更新。

問題是因為在詳細信息屏幕打開時,主屏幕的集合范圍訂閱仍然存在,如果文檔/tasks/{taskId}更新,兩個偵聽器都會觸發。 根據這個這個這個問題中的答案,這意味着我將對對該文檔的任何一次更新的兩次(重復)讀取收費。

此外,每個任務都可以有子任務。 這在 Firestore 中反映為每個任務的tasks子集合。 例如,嵌套任務的路徑可能是: /tasks/abc123/tasks/efg875/tasks/aay789 通過對"tasks"使用集合組查詢,主頁可以顯示所有任務,而不管嵌套。 上述詳情頁面還通過監聽子集合來顯示任務的子任務。 這允許對子任務進行復雜的查詢(過濾、排序等),但同樣的缺點是每次更新子任務都會重復讀取。

我想到的替代設計是:

  • 僅在整個應用程序范圍內訂閱整個任務集(無論是平面集合還是集合組查詢),並在客戶端上進行任何和所有選擇、過濾等。 例如,任務的詳細信息頁面每次都會使用相同的集合范圍訂閱和 select 集合之外的適當任務。 任務/子任務的任何過濾和排序都將在客戶端完成。

    • 優點:無重復讀取,最小化 Firestore 成本。
    • 缺點:對於客戶端來說可能會消耗更多的電池電量,並且代碼會變得更加復雜,因為在每種情況下我都必須 select 從整組任務中提取適當的數據。
  • 打開詳情頁取消全館訂閱,返回主界面重新開啟。 這意味着當詳細信息頁面打開時,只會收到對該特定任務的更新,而不會被復制為兩次讀取。

    • 優點:沒有重復讀取。
    • 缺點:返回主屏幕時重新開始訂閱意味着讀取第一個快照中的所有文檔,即每個任務讀取一次,這實際上可能會使問題變得更糟。 此外,編碼可能非常復雜。

這些設計中的任何一個看起來是最好的嗎? 我還缺少其他更好的選擇嗎?

在你的應用程序中創建一個TaskService或類似的東西來處理監聽FirebaseFirestore.instance.collection("tasks").snapshots()調用,然后在你的應用程序中,訂閱該服務的更新而不是 Firebase 本身(你可以創建兩個Stream對象,一個用於全局更新,一個用於特定更新)。

然后,您的 Firebase 集合中只有一個正在閱讀。 一切都在應用程序端處理。

偽代碼:

class TaskService {  
    final List<Task> _tasks = [];
    final StreamController<List<Task>> _signalOnTasks = StreamController.broadcast();
    final StreamController<Task> _signalOnTask = StreamController.broadcast();
    
    get List<Task> allTasks => _tasks;
    Stream<List<Task>> get onTasks => _signalOnTasks.stream;
    Stream<List<Task>> get onTask => _signalOnTask.stream;
    
    void init() {
        FirebaseFirestore.instance.collection("tasks").snapshots().listen(_onData);
    }
    
    void _onData(snapshot) {
        /// get/update our tasks (maybe check for duplicates or whatever)
        _tasks.addAll(snapshot.documents);
        
        /// dispatch our signal streams
        _signalOnTasks.add(snapshot.documents);
        for(final task in snapshot.documents) {
            _signalOnTask.add(task);
        }
    }
}

您可以讓TaskServiceInheritedWidget在任何地方訪問它(或使用提供程序包),將您的偵聽器添加到您感興趣的任何 stream。您只需要將您的偵聽器簽入onTask ,這是正確的任務在對它做任何事情之前。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM