繁体   English   中英

Firestore 活动文档快照侦听器中断排序

[英]Firestore active document snapshot listener breaks sorting

我遇到了一个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 条记录),第二次用于排序的集合(不在缓存中,因为每次执行sortswitchMap都会取消订阅)。

我们可以在 Zack 发布的功能演示中看到这一点——它闪烁着与文档侦听器相对应的一条记录,然后片刻之后,列表中填充了排序后的集合。

你的听众越活跃,结果的闪现就越奇怪。 这是设计使然吗? 对我来说似乎有缺陷......

直观地说,我希望this._firestore.firestore.collection('users').orderBy('firstName', this.sort as any).limit(20)查询中的snapshotChanges()valueChanges()返回 20 个结果(排序后的前 20 个),独立于任何其他文档侦听器。 这个查询的第一个快照是来自文档查询的初始活动文档,这与直觉相反——这应该与排序查询无关。

我在一个简化的环境中使用了您的示例数据,我想我知道发生了什么。 这是我的游乐场,只需更新AppModule以指向您的 Firebase 项目。

发生了什么?

当您调用onSort()时,您正在重新订阅queryRef.onSnapshot 此回调触发两次:

  1. 订阅后,它会立即触发 Firestore 本地缓存中可用的一组初始数据(在您的情况下,它有一个记录,我稍后会解释)。
  2. 获取实际数据后,它会使用您希望的完整数据集再次触发。

在 Playground 中,确保已订阅Document ,然后订阅Sorted Collection 在控制台中,您会看到它两次触发:

在此处输入图像描述

不幸的是,由于您将此侦听器包装在 Promise中,因此您只会获得第一次火灾,因此只能获得缓存中的单个记录。

为什么取消订阅文档快照会修复它?

似乎本地缓存是根据当前打开的onSnapshot订阅填充的。 因此,在您的情况下,您有一个侦听器来监听单个记录的快照,因此这是您的缓存中唯一的东西。

在 Playground 中,尝试使用各种订阅状态访问Log Cache 例如,虽然只有Document Subscribed为 true,但缓存中只有一条记录。 当两个订阅都处于活动状态时,您会在缓存中看到大约 20 条记录。

如果您取消订阅所有内容并点击Log Cache ,则缓存为空。

事实证明,如果在上面的步骤 1 中没有数据可以从缓存中返回,它会完全跳过该步骤

在操场上,确保Document已取消订阅,然后订阅Sorted Collection 在控制台中,您会看到它只触发一次:

在此处输入图像描述

这恰好适用于您的 Promise,因为第一次也是唯一一次触发包含您希望的数据。

如何解决这个问题?

firebase-js-sdk库中的此评论按预期描述了此行为,以及原因。

Promise 与.onSnapshot配对是扰乱数据流的主要因素,因为.onSnapshot预计会触发不止一次。 你可以改用.get ,它在技术上可以工作。

然而,正如 Robin 指出的那样,专注于利用@angular/fire功能的更具反应性的 RxJS 方法)将 go 走很长的路。

功能齐全的示例。

我找不到您的代码的问题,因为您达到了 firebase 配额。 对我来说,您的排序似乎有点过于复杂并引入了赛车问题。 我认为您可以通过使用 RxJS 来降低复杂性。 像这样的东西会起作用: https://stackblitz.com/edit/angular-ivy-cwthvf?file=src/app/app.component.ts

我无法真正测试它,因为您达到了配额。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM