我遇到了 firestore 问题,希望有人能帮我解决。
我有一个似乎破坏了排序行为的活动文档快照侦听器,但我不知道为什么。
在我的组件的构造函数中,我初始化了文档快照侦听器一次:
this.listen = this.fs.collection('users').doc('0EwcWqMVp9j0PtNXFDyO').ref.onSnapshot(t => {
console.log(t)
})
每当我单击“排序”按钮时,它都会初始化一个按名字排序的新集合查询。
async onSort() {
if (this.first) {
this.first();
}
this.sort = this.sort === 'asc'? 'desc' : 'asc';
alert(this.sort)
let arr = []
let lastDoc = null
let queryRef = this.fs.firestore.collection('users').orderBy('firstName', this.sort as any).limit(20);
this.users$ = this._users$.asObservable()
// initialize a blank list
this._users$.next([])
// subscribe to user list snapshot changes
const users1: { users: any, last: any } = await new Promise((resolve, reject) => {
this.first = queryRef.onSnapshot((doc) => {
console.log("Current data: ", doc);
const users = doc.docs.map(t => {
return {
id: t.id,
...t.data() as any
}
})
console.log(users)
resolve({
users: users,
last: doc.docs[doc.docs.length -1]
})
});
})
this._users$.next(this._users$.value.concat(users1.users))
}
然而,这并没有按预期工作。排序返回一条记录(根据限制,应该有 20 条记录)。
有趣的是,取消订阅文档快照,修复了排序。正在返回 20 条记录,并且排序行为正确。
有人可以解释一下我做错了什么吗?
这是重现该问题的最小 Stackblitz。
[编辑]
越来越接近解决方案和理解... 感谢@ZackReam 的解释以及@Robin 和@Zack 的规范答案。
似乎 this._firestore.firestore.collection('users').orderBy('firstName', this.sort as any).limit(20).onSnapshot(...)
确实发出了两次 - 一次用于活动文档侦听器(缓存中有 1 条记录),第二次用于排序集合(自 {{ 1}} 将在每次执行 switchMap
时取消订阅)。
我们可以在 Zack 发布的功能演示中简要地看到——它闪烁着一个与文档侦听器对应的记录,然后一瞬间,列表中填充了排序的集合。
您拥有的听众越活跃,结果的闪烁就越奇怪。这是设计使然吗?对我来说似乎有缺陷......
直觉上,我希望 sort
查询中的 snapshotChanges()
或 valueChanges()
返回 20 个结果(排序时的前 20 个),独立于任何其他文档侦听器。这个查询的第一个快照是来自文档查询的初始活动文档,这是违反直觉的 - 这应该与排序查询无关。
答案 0 :(得分:2)
我在简化的环境中处理了您的示例数据,我想我知道发生了什么。
Here is my playground,只需更新 AppModule
以指向您的 Firebase 项目。
当您调用 onSort()
时,您正在重新订阅 queryRef.onSnapshot
。此回调触发两次:
在 Playground 中,确保订阅 Document,然后订阅 Sorted Collection。在控制台中,您将看到两次触发:
不幸的是,由于您将这个侦听器包装在 Promise 中,您只会得到第一次触发,因此只会得到缓存中的单个记录。
本地缓存似乎是根据当前打开的 onSnapshot
订阅填充的。因此,在您的情况下,您有一个监听单个记录快照的侦听器,因此这是缓存中唯一的内容。
在操场上,尝试使用各种订阅状态点击日志缓存。
例如,虽然只有 Document Subscribed
为真,但缓存只有一条记录。
当两个订阅都处于活动状态时,您会在缓存中看到大约 20 条记录。
如果您取消订阅所有内容并点击日志缓存,则缓存为空。
事实证明,如果在上面的第 1 步中没有要从缓存中返回的数据,它会完全跳过该步骤。
<块引用>在 Playground 中,确保 Document 已取消订阅,然后订阅 Sorted Collection。在控制台中,您将看到它只触发一次:
这恰好适用于您的 Promise,因为第一次也是唯一一次触发包含您希望的数据。
This comment in the firebase-js-sdk
repo 按预期描述此行为,并说明原因。
与 .onSnapshot
配对的 Promise 是搅乱数据流的主要因素,因为 .onSnapshot
预计会触发不止一次。您可以改用 .get
,从技术上讲它会起作用。
然而,正如 Robin 指出的那样,专注于利用 @angular/fire
functionality) 的更具反应性的 RxJS 方法会大有帮助。
答案 1 :(得分:1)
我找不到您的代码的问题,因为您达到了 Firebase 配额。对我来说,您的排序似乎有点过于复杂,并且会引入赛车问题。我认为你可以通过使用 RxJS 让它变得不那么复杂。像这样的事情会起作用:https://stackblitz.com/edit/angular-ivy-cwthvf?file=src/app/app.component.ts
我无法真正测试它,因为您达到了配额。