【问题标题】:firestore loadBundle continues to store deleted documentsfirestore loadBundle 继续存储已删除的文档
【发布时间】:2023-02-06 19:09:54
【问题描述】:

我创建了一个包3个文件。然后我删除了2个他们并再次创建了捆绑包。所以包现在包含一文档!并且应用程序缓存包含3个

然后我再次下载包:

const resp = await fetch(downloadUrl);
await loadBundle(db, resp.body); //{totalDocuments: 1} - as expected
const query = await namedQuery(db, `my-bundle-query`);
if (query) {
   const snap = await getDocsFromCache(query); //Nope! there should be one document, but there are three
}

看起来像一个错误。我认为 loadBundle 应该以某种方式跟踪已删除的文档。我应该怎么办?

更新:

namedQuery - 查询整个缓存。但预期的行为是只获取与包关联的文档。所以这根本不起作用 - 在我的 namedQuery 中我有 4 个文档的限制但是从缓存中得到了更多!!

所以 official example 是错误的,因为它面临着我上面描述的相同问题

【问题讨论】:

    标签: javascript firebase google-cloud-firestore firestore-bundle-builder


    【解决方案1】:

    长话短说:博士;

    不幸的是,这是当前捆绑包编码方式的限制。它也超出了 bundle 的范围,因为它们被设计用于针对具有大量重用数据的集合以用于页面的初始加载。

    如果您认为 loadBundle 应该从本地缓存中清除未在捆绑包中返回的项目,您应该 file a feature request 将此功能添加到 loadBundle 调用(例如 loadBundle(rawBundle, /* forceRefresh = */ true))或 file a bug report。


    细节

    例如,假设您的查询是“获取集合/Posts 中的前 10 个帖子”。

    请求此查询的包后,第一个包返回以下结果:

    {
      "/Posts/D9p7MbcYCbTNzcXLrzfQ": { /* post data */ },
      "/Posts/xz3eY1Gwsjl4tTxTjXyR": { /* post data */ },
      "/Posts/fIvk5LF2zj2xgpgWIv9h": { /* post data */ }
    }
    

    然后你使用loadBundle加载它。

    接下来,使用另一个客户端从服务器中删除其中两个文档(使用同一个客户端会将它们从本地缓存中删除)。

    现在您重新请求包,它返回:

    {
      "/Posts/fIvk5LF2zj2xgpgWIv9h": { /* post data */ }
    }
    

    在调用loadBundle 时,库遍历包中的文档集合,更新每个文档的本地缓存:

    // this is psuedo-code, not the true implementation
    function loadBundle(rawBundle) {
      decodedBundle = parseBundle(rawBundle);
    
      decodedBundle.docs.forEach((doc) => {
        cachedDocuments.set(doc.id, doc);
      })
    
      return { // return bundle load progress
        totalDocuments: decodedBundle.docs.length,
        /* ... other stats ... */
      };
    }
    

    在上面的伪代码中,您可以看到只有包含在捆绑包中的文档才会在本地缓存中更新。未包含在包中的文档不会更新,并且返回的统计信息与包中包含的刚刚解码的文档相关 - 而不是相关查询的结果。

    当您运行命名查询时,查询将针对本地缓存进行解码、比较和执行。由于以前的文档当前根据缓存匹配查询,因此它们包含在结果中。

    只有在以下情况下,才会省略本地缓存中的文档:

    • 解码查询返回10个以上的结果,不符合查询条件。
    • 查询是针对实时数据库执行的。
    • 如果下载的包包含元数据,表明文档已被删除。

    因此,要清除本地缓存,捆绑包必须包含:

    {
      "/Posts/D9p7MbcYCbTNzcXLrzfQ": _DELETED,
      "/Posts/xz3eY1Gwsjl4tTxTjXyR": _DELETED,
      // ... for every deleted document that ever existed ...
      "/Posts/fIvk5LF2zj2xgpgWIv9h": { /* post data */ }
    }
    

    返回这样的捆绑包在技术上很复杂,而且效率极低。

    请求捆绑包时,您可以包括文档 ID 列表,如果给定的文档 ID 不存在,则文档删除将包含在包的数据中。但是,在这样做时,您也可以使用getDocs 或onSnapshot 向服务器发出一个普通的数据库请求以获得相同的结果,这样会更快、更便宜。

    Bundle 旨在用于具有大量重用数据的集合,并且通常仅在页面的初始加载时使用。如果前 50 个结果中的帖子被删除,您将使缓存的结果无效并重建包。所有新用户都会立即看到更改后的结果,只有那些拥有本地副本的用户才能看到它们。

    如果您认为 loadBundle 应该从本地缓存中清除未在捆绑包中返回的项目,您应该 file a feature request 将此功能添加到 loadBundle 调用(例如 loadBundle(rawBundle, /* forceRefresh = */ true))。

    【讨论】:

    • 所以官方的例子是骗人的firebase.google.com/docs/firestore/bundles 因为在他们的例子中,如果你删除文档,它会保留在缓存中。它还打破了命名查询的逻辑,因为它们与包中的文档无关,而只是在缓存上执行。因此,所有关于您可以从中创建一个包并阅读一次而不是阅读 50 个文档的承诺都是谎言,因为您必须阅读相同的 50 个文档以跟踪文档删除——这是全球范围内的失败。感谢你的回答!
    • @ValeraKvip 如果您在捆绑包中有 50 个文档,但最近删除了两个文档,则在使用 getDocsFromCache 将它们加载到缓存中然后使用 getDocs/onSnapshot 获取最新版本后,您只会被命中 2 次读取。这仍然为每位用户节省了 48 次读取。
    猜你喜欢
    • 2020-01-04
    • 2018-08-02
    • 2021-04-02
    • 1970-01-01
    • 2019-10-20
    • 2021-07-23
    • 2021-12-31
    • 2019-02-03
    • 1970-01-01
    相关资源
    最近更新 更多