【问题标题】:Firestore Pagination get next batch of dataFirestore分页获取下一批数据
【发布时间】:2020-08-10 08:19:21
【问题描述】:

我有这个从集合中获取文档的函数。我从 Firestore 文档中得到了这个逻辑,但我不明白 addSnapshotListener 的目的。

在真实场景中我需要:

首先运行第一个查询,获取最后一个文档并将其传递给下一个查询等等,对吗? 所以这意味着在每次查询之后我还需要返回最后一个文档? 如果是这种情况,为什么我需要addSnapshotListener?我也可以从.getDocuments() 得到lastSnapshot

class HomeViewModel: ObservableObject, LoadProtocol {
    var firestoreService: FirestoreService = FirestoreService()
    @Published var items: [Item] = []

    
    let first: Query = Firestore.firestore().collection("items").limit(to: 1)
    var next: Query = Firestore.firestore().collection("items").limit(to: 1)
    
    init() {
        self.first.addSnapshotListener { (snapshot, error) in
            
            guard let snapshot = snapshot else {
                print("Error retrieving cities: \(error.debugDescription)")
                return
            }

            guard let lastSnapshot = snapshot.documents.last else {
                // The collection is empty.
                return
            }

            // Construct a new query starting after this document,
            // retrieving the next 25 cities.
            self.next = self.firestoreService.db.collection("items")
                .start(afterDocument: lastSnapshot)

            // Use the query for pagination.
 
        }
    }
    
    func refresh() {
        self.firestoreService.fetchCollection(query: self.next) { (result: Result<[Item], Error>) in
            switch result {
            case .success(let items):
                self.items += items
                self.addToCategories()
            case .failure(let error):
                print(error)
            }
        }
    }
}

【问题讨论】:

    标签: ios swift firebase google-cloud-firestore swift5


    【解决方案1】:

    问题是您在实现中将基本光标分页与查询光标分页混合在一起。

    您的代码的基本光标分页部分(直到first.addSnapshotListener)是正确的,每次调用函数并解析它时,每次返回越来越多的数据到您的地图时都会执行,但是查询光标分页部分将永远不会被调用,因此您可以从实现中完全删除该部分。

    所以你的代码应该是这样的:

    func fetchCollection<Collection: CollectionProtocol>(lastSnapshot: DocumentSnapshot?, query: Query, completion: @escaping (Result<[Collection], Error>) -> Void) {
        var first = query.limit(to: 1)
    
        if let lastSnapshot = lastSnapshot {
            first = query.start(afterDocument: lastSnapshot)
        }
    
        first.getDocuments() { (querySnapshot, error) in
            if let snapshot = querySnapshot {
                // print(snapshot.metadata.isFromCache)
                completion(.success(snapshot.documents.map { document -> Collection in
                    return Collection(document: document)
                }))
            } else if let error = error {
                completion(.failure(error))
            }
        }
    }
    

    注意:您也可以将.limit(to:1) 添加到您的 lastSnapshot 检查中,因为现在的方式是检索所有文档,当然,如果这是您的预期逻辑,请忽略这个注释。


    编辑:

    如果你想使用查询游标分页,你可以按照这个例子,监听器位于 fetchCollection 函数之外,每次触发 getDocuments() 并挂载要执行的新查询时都会执行:

    //initial state of your app
    var results = "nil";
    
    //initial query
    var query = db.collection("foo").document("bar").limit(to: 1);
    
    //everytime you need more data fetched and on database updates to your snapshot this will be triggered
    query.addSnapshotListener { querySnapshot, error in
        guard let snapshot = querySnapshot else {
            print("Error fetching snapshots: \(error!)")
            return
        }
        snapshot.documentChanges.forEach { 
            // update data
        }
        let next = db.collection("foo").document("bar").start(afterDocument: result.last!).limit(to: 1);
        query = next;
    };
    
    //initial load of data
    fetchCollection();
    
    //simply executes the query
    func fetchCollection() {
        query.getDocuments { (document, error) in
            if let document = document, document.exists {
                if(result == "nil"){
                    result = document.data().map();
                }else{
                    result.map({ document.data() })
                }
            } else {
                print("Document does not exist")
            }
    
        }
    }
    

    注意:请记住,这是一个未经测试的示例,但它可能是您在应用程序中需要的一个很好的起点,此外,我使用此 documentation 来实时更新听众,您可能会在该链接上找到更多信息。

    【讨论】:

    • 那是我当前的实现,但我正在考虑将其更改为查询光标分页,但我不确定如何。那么第一步是监听我的快照,然后执行下一个查询?这不适用于我的第一个查询,但仅适用于我的下一个查询,请参阅我更新的问题
    • 在这种情况下,您将不得不返回您的听众。我会把它添加到答案中
    • 然后我在哪里使用返回的listener?当我再次调用该函数时?能否请你做一个关于如何调用它的小前任,我有点困惑
    • 我已经编辑了答案,并附上了一个如何使用 snapshotListener 实现它的示例,对前面的陈述感到抱歉,我误解了文档,这对 swift 来说非常令人困惑,但当前的应该可以工作。
    • 是的,你应该在每次需要更多数据的交互上调用fetchCollection()函数,监听器将简单地挂载下一个查询
    猜你喜欢
    • 2021-11-16
    • 1970-01-01
    • 2021-07-17
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    相关资源
    最近更新 更多