Firestore Pagination 获取下一批数据
Firestore Pagination get next batch of data
我有这个功能可以从集合中获取文档。我从 Firestore 文档中得到了这个逻辑,但我不明白 addSnapshotListener
.
的目的
在真实场景中我需要:
首先 运行 第一个查询,获取最后一个文档并将其传递给下一个查询等等,对吗?
所以这意味着在每次查询之后我还需要 return 最后一个文档?
如果是这样,为什么我需要 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)
}
}
}
}
问题是您在实现时将基本游标分页与查询游标分页混合在一起。
您的代码的基本光标分页部分(直到 first.addSnapshotListener
)是正确的,每次调用该函数并解析它时,每次都会执行返回越来越多的数据到您的地图,但是查询Cursor Pagination 部分将永远不会被调用,因此您可以从您的实现中完全删除该部分。
因此您的代码应如下所示:
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 对于侦听器的实时更新,您可能会找到更多相关信息 link。
我有这个功能可以从集合中获取文档。我从 Firestore 文档中得到了这个逻辑,但我不明白 addSnapshotListener
.
在真实场景中我需要:
首先 运行 第一个查询,获取最后一个文档并将其传递给下一个查询等等,对吗?
所以这意味着在每次查询之后我还需要 return 最后一个文档?
如果是这样,为什么我需要 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)
}
}
}
}
问题是您在实现时将基本游标分页与查询游标分页混合在一起。
您的代码的基本光标分页部分(直到 first.addSnapshotListener
)是正确的,每次调用该函数并解析它时,每次都会执行返回越来越多的数据到您的地图,但是查询Cursor Pagination 部分将永远不会被调用,因此您可以从您的实现中完全删除该部分。
因此您的代码应如下所示:
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 对于侦听器的实时更新,您可能会找到更多相关信息 link。