Firebase Firestore:文档的版本历史记录

Firebase Firestore:文档的版本历史记录,firebase,google-cloud-firestore,data-modeling,Firebase,Google Cloud Firestore,Data Modeling,我正在寻找一种适当的方法来构造Firestore数据库,以处理单个集合中文档的多个版本历史记录 例如:我有一个名为offers的集合,其中包含多个文档,这些文档对应于多个offers。对于这些文档中的每一个,我都希望有更改的历史记录,比如谷歌文档上的更改 由于文档只支持直接添加字段或嵌套另一个集合,因此我想到了一种结构: collections: offers - documents: offer1, (offer2, offer3, ...) - fields populated

我正在寻找一种适当的方法来构造Firestore数据库,以处理单个集合中文档的多个版本历史记录

例如:我有一个名为offers的集合,其中包含多个文档,这些文档对应于多个offers。对于这些文档中的每一个,我都希望有更改的历史记录,比如谷歌文档上的更改

由于文档只支持直接添加字段或嵌套另一个集合,因此我想到了一种结构:

collections: offers
 - documents: offer1, (offer2, offer3, ...)
     - fields populated with latest version of the offer content
     - nested collection named history
         - nested documents for each version (v1, v2, v3), which in turn have fields specifing state of each field in that version. 
这似乎有点过于复杂,因为我有最新的状态,而不是历史的嵌套集合。在平面结构中,数组中的最新项是最新状态,或者类似的东西

此外,历史状态是在点击按钮时生成的,所以我不需要在历史中保存所有可能的更改,只需要在用户保存时进行快照

我想使用Firebase作为我的数据库,因为我还需要其他一些东西,所以我现在不考虑不同的解决方案

谢谢

编辑:根据亚历克斯的回答,这是我对这个问题的另一种看法

Firestore-root
   |
   --- offers (collection)
        |
        --- offerID (document)
        |   (with fields populated )
        |        |
        |        --- history (collection) //last edited timestamp
        |            |
        |            --- historyId
        |            --- historyId
        |
         --- offerID (document)
            (with fields populated with latest changes)
                 |
                 --- history (collection) //last edited timestamp
                     |
                     --- historyId
                     --- historyId
通过这种方式,我可以查询整个
报价
集合,并获得报价数组以及最新状态,因为它与集合本身处于同一级别。然后,如果我需要来自历史状态的特定内容,我可以查询特定报价的历史集合并获取其历史状态。这有意义吗

我不确定反规范化,因为这似乎解决了我的问题,避免了复杂性

同样,要求是: -能够获取最新状态的所有报价(works) -能够加载特定的历史状态(works)

每次我用新状态更新历史记录集合时,我都会用相同的最新状态直接覆盖offerID集合中的字段


我错过什么了吗

在我看来,上述模式可能有效,但您需要进行一些额外的数据库调用,因为Firestore查询很浅。这意味着Firestore查询只能从运行查询所针对的集合中获取项目。Firestore不支持跨不同集合的查询。因此,您无法在单个查询中获得一个文档以及托管在该文档集合下的相应历史版本

我能想到的一种可能的数据库结构是使用如下的单个集合:

Firestore-root
   |
   --- offerId (collection)
        |
        --- offerHistoryId (document)
        |        |
        |        --- //Offer details
        |
        --- offerHistoryId (document)
                 |
                 --- //Offer details
Firestore-root
   |
   --- offers (collection)
        |
        --- offerHistoryId (document)
        |        |
        |        --- date: //last edited timestamp
        |        |
        |        --- //Offer details
        |
        --- offerHistoryId (document)
                 |
                 --- date: //last edited timestamp
                 |
                 --- //Offer details
如果要显示报价的所有历史版本,则需要一个查询。因此,您只需在
offerId
集合上附加一个侦听器,并一次性获取所有offer对象(文档)

但是,如果您只想获取最新版本的offer,那么应该在每个offer对象下添加一个
timestamp
属性,并根据该属性查询数据库。最后只需打一个
limit(1)
电话就可以了

编辑:

根据你的评论:

我需要一份所有报价的最新数据列表

在这种情况下,您需要创建一个名为
offers
的新集合,该集合将保存所有最新版本的优惠。您的新收藏应如下所示:

Firestore-root
   |
   --- offerId (collection)
        |
        --- offerHistoryId (document)
        |        |
        |        --- //Offer details
        |
        --- offerHistoryId (document)
                 |
                 --- //Offer details
Firestore-root
   |
   --- offers (collection)
        |
        --- offerHistoryId (document)
        |        |
        |        --- date: //last edited timestamp
        |        |
        |        --- //Offer details
        |
        --- offerHistoryId (document)
                 |
                 --- date: //last edited timestamp
                 |
                 --- //Offer details
这种做法被称为
非规范化
,是Firebase的常见做法。如果您是NoQSL数据库的新手,为了更好地理解,我建议您观看此视频。它适用于Firebase实时数据库,但同样的规则适用于云Firestore

另外,在复制数据时,有一件事需要记住。与添加数据的方式相同,您需要对其进行维护。换句话说,如果你想更新/删除一个项目,你需要在它存在的每个地方进行


在您的特定情况下,当您想要创建报价时,您需要将其添加到两个位置,一个是在
报价
集合中,另一个是在
报价
集合中。创建新的报价历史记录版本后,您只需再执行一个操作。如前所述,在
offerId
集合中添加
offeryStoryid
文档,在
offers
集合中添加相同的对象,但在这种情况下,您需要从
offers
集合中删除旧版本的offer。

我可以这样想。每个报价文档都将offerHistoryID作为编号

  • 您可以拥有一个单独的根集合,用于提供的版本化文档(例如提供\u事务
  • 现在,在提供的文档上编写一个更新触发云函数,该函数将包含文档的after和before值
  • 在执行文档更新之前,您可以将Before值连同时间戳和最新历史ID一起写入offers\u事务中
  • 将该报价的offerHistoryID增加1,并使用新值更新文档
现在,您可以根据过滤器查询根集合提供的\u事务。通过这种方式,您可以保持根集合更干净


想法?

谢谢Alex!实际上我不知道你不能得到深度嵌套的对象。我试过了,你是对的。然而,这种结构也不能解决所有问题。例如,我需要获得一份包含所有报价及其最新数据的列表,而在这里我看不到这样做的方法,因为每个报价都在它自己的集合中。我很好奇你对此是否有办法。之后我可以分享我的想法:)关于非规范化的很好的见解。听起来很疯狂,但我知道它是从哪里来的。尽管如此,我认为我的用例并不适合它,我已经附加了另一种方法来构建它。你能再看一下吗?谢谢。它几乎和f一样