【问题标题】:Firestore: new collection vs subcollectionFirestore:新集合与子集合
【发布时间】:2020-07-17 21:44:21
【问题描述】:

我正在尝试为 Firestore 中的社交媒体应用组织数据。是为帖子创建一个新集合还是将其放入用户的子集合中更好?

深度应该相同,但是一种方式比另一种方式有什么优势吗?

创建一个新集合:

posts(collection) >> user_id (document) >> userposts (subcollection) >> post_id (documents) >> ...

用户(集合)>> [其他用户信息...]

用户中的子集合:

users(collection) >> user_id (document) >> userposts (subcollection) >> post_id (documents) >> ...

【问题讨论】:

  • 选择一个让您的查询更容易的。没有“正确的方法”。只有满足您的应用需求和您的个人喜好的方式。

标签: database google-cloud-firestore


【解决方案1】:

好吧,正如其他人所提到的,您通常应该针对读取进行优化,但“优化”的含义将取决于您的应用程序。如果您认为您必须同时返回多个用户的帖子的情况很少见,那么我想将帖子放在用户下会更容易一些,因此您可以快速获取所有来自单个用户的帖子。如果您正在做一些有很多不同用户发布的常见“主题”的事情,并且您想展示他在某个主题上发布的所有帖子,那么我猜将您的所有帖子发布会容易得多在单个集合中,而不必搜索多个用户集合以将所有用户在单个主题上的帖子汇总在一起。 (尽管有了在单个查询中搜索多个集合的能力,这真的不再那么难了)

顺便说一句,需要考虑的一件事是,如果考虑成本,将用户的所有帖子放在单个文档中是否有意义。如果您这样做,您会在一个文档中读取所有帖子,并且 Firestore 费用主要由文档读取驱动。但是,如果您必须将多个用户关于“主题”的帖子汇总在一起,或者如果您的“线程”包含来自多个不同用户的帖子,则这种方法可能不切实际。

针对您的评论:让我们确保我们使用相同的术语。一个线程是来自多个不同用户的多个关于单个主题的帖子(即帖子和各种回复)。因此,如果您有一组用户,并且您有兴趣查看他们发起的所有帖子,则可以保留一个 ThreadPosts 集合,其中包含线程的发起者,并且线程中的每个帖子都包含一个 ThreadPost,您可以获得过滤收藏的用户发起的线程集合,然后您可以使用以下方法获取线程的帖子。

如果您想列出收藏夹参与的所有线程,那么您将从收藏夹用户那里获取所有帖子,删除 threadId 的重复数据列表,然后拉入该 threadId 上的所有帖子在创建时间戳顺序。您可以使用 firestore 的方法来获取这些集合,这些集合既按字段值(例如“threadId”)过滤,又按字段值(例如“creationTimestamp”)排序。这是一些代码(打字稿中的AngularFirestore)或多或少地展示了我的意思。

 interface  ThreadPost {
   id: string;  //this is the id for this thread.
   title : string;
   originalAuthorId: string;
   creationTimestamp : number;
   lastUpdatedTimestamp: number;
 }

然后您的帖子 Collection 也位于根目录,可能如下所示: [打字稿] 界面帖子{ id:字符串; authorId : 字符串 线程ID:字符串 postTest:字符串; 创建时间戳:数字; lastUpdatedTimestamp:数字; }

所以在你的数据库的根目录下,你有 3 个集合:

Users Posts PostThreads

因此,如果您想按 createTimestamp 顺序获取用户的所有帖子,您可以执行以下操作:

 async fetchPosts(userId) : Post[] {
    return getFilteredOrderedCollection("posts", "userId", ${userId}, "creationTimeStamp") 
 }

 async getFilteredOrderedCollection(collectionPath : string, filterField : string, 
                                     filterValue: string, orderByField: string) : any[] {
    let result: any[] = [];
    await AngularFirestore.collection(`${collectionPath}`).ref
      .where(`${filterField}`, "==", `${filterValue}`)
      .orderBy(orderByField).get()
      .then(function (snapshot) {
        snapshot.forEach(function (doc) {
          let p = doc.data()
          p.timestamp = Date.now();
          result.push(p)
        })
      })
      return result;
  }

如果你想要一个线程上的所有帖子,你可以调用:

 async fetchThreadPosts(threadId) : Post[] {
     return getFilteredOrderedCollection("posts", "threadId", ${threadId}, 
     "creationTimeStamp") 
  }

【讨论】:

  • 是的,我必须实现一个“提要”,我需要将当前用户关注的所有用户的帖子汇总在一起。那么,这种结构似乎不切实际?有一个更好的方法吗?谢谢。
  • 这个例子,你似乎需要从一个用户集合中获取所有帖子,你可以去任何一种方式。如果您将user_posts 作为user 的集合,则可以使用user/${followedUser.id}/posts 之类的内容检索用户的集合。当您只需要来自多个用户的一些帖子(例如,对于帖子的“线程”)时,它会变得更加棘手,您将在许多不同的集合上运行过滤查询。在这种情况下,使用类似post.threadId == thread.id 的查询从单个帖子集合中获取会更容易
  • 谢谢。我确实需要按时间戳顺序发布一系列帖子。您能否详细说明 threadId 和 thread 是什么?我真的无法想象如何从所有关注的用户那里获取帖子,然后按时间戳顺序对它们进行排序。由于这将是交叉集合,我将不得不在客户端对它们进行排序?我认为这可能真的效率低下。
  • 我已经编辑了答案,以表明你想要我在说什么。
【解决方案2】:

以他们认为最适合未来搜索的最简单方式保存您的数据:

  • Cloud Firestore 中的所有查询都具有成本效益和速度效率
  • 影响查询性能的唯一因素是您请求的文档数量

需要考虑的主要差异

如果您将数据从现有实时数据库树移至 Cloud Firestore 文档和集合,请注意数据库之间的以下主要差异可能会影响您在 Cloud Firestore 中的数据结构:

  • 浅层查询在分层数据结构中提供了更大的灵活性
  • 复杂的查询提供了更高的粒度并减少了对重复数据的需求
  • 查询游标提供更强大的分页
  • 事务不再需要所有数据的共同根,而且效率更高
  • 实时数据库和 Cloud Firestore 的计费费用不同。在许多情况下,Cloud Firestore 可能比实时数据库更昂贵,尤其是在您依赖许多小型操作的情况下。考虑减少对数据库的操作数量并避免不必要的写入。详细了解实时数据库和 Cloud Firestore 之间的计费差异

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-23
    • 2020-08-25
    • 2020-08-10
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-01
    相关资源
    最近更新 更多