在非规范化数据时获取排序的 Firestore 文档



我正在为我的应用程序使用 Firestore,用户可以在其上发布存储在帖子集合中的帖子

posts
{postID}
content = ...
attachementUrl = ...
authorID = {userID}

每个用户还将有一个时间表,其中将显示他们关注的人的帖子。为此,我还保留了一个通过云函数填充的user_timelines集合:

user_timelines
{userID}
posts
{documentID}
postID = {postID}
addedDate = ...

由于数据是非规范化的,如果我想循环访问用户的时间线,我需要执行一个额外的(内部(查询,以通过其 {postID} 获取完整的 Post 对象,如下所示:

db.collection("user_timelines").document(userID).collection("posts")
.orderBy("addedDate", "desc").limit(100).getDocuments() { (querySnap, _) in
for queryDoc in querySnap.documents {
let postID = queryDoc.data()["postID"] as! String
db.collection("posts").document("postID").getDocument() { (snap, _) in
if let postDoc = snap {
let post = Post(document: postDoc)
posts.append(post)
} 
}
}
}

问题是这样做会丢失集合的顺序,因为我们不能保证所有内部查询都将以相同的顺序完成。我需要保持我的收藏顺序,因为这将与时间线的顺序相匹配。

如果我在时间线集合中拥有所有完整的 Post 对象,就不会有问题,并且.orderBy("addedDate", "desc").limit(100)可以很好地保持帖子的排序,但是如果我非规范化,我似乎找不到正确的解决方案。

如何循环访问用户的时间线并确保即使在非规范化数据时也能按 addDate 对所有 Post 对象进行排序?

我在阅读postID时正在考虑创建一个映射字典postID/addDate,然后使用此字典对最后的Post进行排序,但我认为一定有更好的解决方案吗?

我原本以为这是非规范化数据时的常见问题,但不幸的是我找不到任何结果。也许我在这里缺少一些东西。

感谢您的帮助!

您可以做的是枚举执行内部查询的循环,该循环只是对每次迭代进行编号。从那里,您可以展开Post模型以包含此值n,然后在完成后按n对数组进行排序。

db.collection("user_timelines").document(userID).collection("posts").orderBy("addedDate", "desc").limit(100).getDocuments() { (querySnap, _) in
for (n, queryDoc) in querySnap.documents.enumerated() {
let postID = queryDoc.data()["postID"] as! String
db.collection("posts").document("postID").getDocument() { (snap, _) in
if let postDoc = snap {
let post = Post(document: postDoc, n: n)
posts.append(post)
}
}
}
posts.sort(by: { $0.n < $1.n })
}

上面的示例实际上不起作用,因为循环是异步的,这意味着数组将在所有下载完成之前进行排序。为此,请考虑使用调度组来协调此任务。

db.collection("user_timelines").document(userID).collection("posts").orderBy("addedDate", "desc").limit(100).getDocuments() { (querySnap, _) in
let dispatch = DispatchGroup()
for (n, queryDoc) in querySnap.documents.enumerated() {
dispatch.enter() // enter on each iteration
let postID = queryDoc.data()["postID"] as! String
db.collection("posts").document("postID").getDocument() { (snap, _) in
if let postDoc = snap {
let post = Post(document: postDoc, n: n)
posts.append(post)
}
dispatch.leave() // leave no matter success or failure
}
}
dispatch.notify(queue: .main) { // completion
posts.sort(by: { $0.n < $1.n })
}
}

最新更新