我可以通过哪些方法正确地在 Firebase Firestore 中的数据结构来建模多对多关系以检索 vue js 中的特定 uid

Posted

技术标签:

【中文标题】我可以通过哪些方法正确地在 Firebase Firestore 中的数据结构来建模多对多关系以检索 vue js 中的特定 uid【英文标题】:What ways can I properly my data structure in Firebase Firestore to model many-to-many relationships to retrieve specific uids in vue js 【发布时间】:2022-01-17 05:01:40 【问题描述】:

我知道这种类型的问题已经被问过很多次了,但我还没有找到一个足够有意义的例子来执行,所以我将我的具体案例提出来澄清一下。

问题总结: 这个问题来自使用 Vue 路由器。在项目中,我们有一个用户可以创建自己的团队并将团队成员添加到该团队。最初,我得到了我想要的结果,因为我正在寻找的数据流按预期工作,使用路由器链接中的道具为详细视图提供类似这样的数据

this.members = this.$route.params.data

团队列表会很好地显示,单击按钮查看特定团队的成员也可以正常工作。我的问题的开始是来自路由器链接的数据没有保留,并且在刷新或更改页面以添加新团队成员时,当我使用 this.$router.back() 方法时,团队成员列表没有更新,也没有显示router.push( path: 'home' ) 方法符合预期。

在研究了更多关于路由器链接如何与 Vue 一起工作并阅读了一些具有类似情况的用户的 SO 帖子之后,我意识到我需要一种更好的方法来检索我想要的数据,只需使用 :to=" name: 'PageThatShowsTeamMembers', params: id: id 方法,这就是我的地方似乎陷入了死胡同。

我当前的数据结构是什么样的: 在 Firestore 中,我使用***集合来存储团队。文档 id 是当前用户的 uid,该文档内部包含包含团队信息的字段和存储团队成员的数组。

我如何尝试将数据添加到 Firestore: 我探索了几种方法来存储团队成员信息以供检索

let ref = db.collection("Teams").doc(firebase.auth().currentUser.uid).collection("Team Members");

ref.set(
  // team member info
)

let ref = db.collection("Teams").where("userId", "==", firebase.auth().currentUser.uid);
ref.get().then(querySnapshot => 
  querySnapshot.forEach(doc => 
    db.collection("Teams").doc(doc.id)
      .update(
        teamMembers: firebase.firestore.FieldValue.arrayUnion(
          // team member info
        )
      )
  );
)

let ref = db.collection("Teams").doc(this.$route.params.id);
ref.update(
  teamMembers: firebase.firestore.FieldValue.arrayUnion(
    // team member info
  )
)      

正如预期的那样,这两种方式都没有达到我想要的程度。

我是如何尝试检索信息的: 在使用 Firestore 控制数据的整体结构后,我尝试了以下方法来获得与 :to=" name: 'PageThatShowsTeamMembers', params: id: id, data: teamMemberData 之前看到的相同的结果,但无济于事。

let ref = db.collection("Teams").where("userId", "==", this.$route.params.id)
ref
  .get()
  .then(querySnapshot => 
    querySnapshot.forEach(doc => 
      this.members.push(...doc.data().teamMembers)
    );
  )
  .catch(err => 
    console.log(err);
  )

上面的代码块获取与路由参数中的 id 关联的每个团队,即当前用户的 uid。这不是预期的结果,因为所有成员都会显示给每个团队,而不是特定成员显示给任何被点击的团队。

let ref = db.collection("Teams").doc(this.$route.params.id).collection("Team Members")

ref.get().then((querySnapshot) => 
  querySnapshot.forEach((doc) => 
    this.members.push(doc.data());
  );
);

这个代码块给我的结果更接近:to=" name: 'PageThatShowsTeamMembers', params: id: id, data: teamMemberData 方式,但由于该数据需要在添加新团队成员时使用set() 方法而不是add() 方法,我觉得我'除了在需要其他页面时无法轻松查询集合中所有团队成员的列表之外,我的文档大小很快就会达到 1mb 的限制。

现在的情况: 这让我想到了现在的情况。我已经广泛研究了如何开始在 Firestore 中重构数据。我已经阅读了以下帖子: Many to Many relationship in Firebase Firebase Cloud Firestore Many to Many Relationships Firebase query if child of child contains a value 以及除此之外的许多其他人,我仍然对如何为特定团队的团队成员存储 uid 以仅获取我想要的特定数据感到困惑。我在其中一些带有建议答案的帖子中看到,会将 uids 存储为要检索的字段键,但我不知道在查询它们后如何列出数据。我也越来越怀疑将团队成员信息本身存储在数组或地图字段中,因为这些数据本身包含超过 7 个字段。

在观看了讨论数据建模和非规范化的 Firebase 视频并对其进行了更多思考之后,我有了一个想法,将团队成员完全设为自己的***集合并可能存储团队名称可能会更好一个成员以某种方式属于,但我不确定这应该是团队成员的文档 ID 还是我查询的文档中的字段。除此之外,我对如何在 Vue 中使用路由感到困惑。如果我将团队名称设为文档 ID,我将如何在路由器链接中正确设置它以获得我想要的结果。另一种选择是让团队成员成为一个子集合,但这样我会失去一些查询能力并增加我必须进行的读取次数。

我知道没有一种特定的“正确”方法可以解决它,无论如何都会有权衡,我只想更清楚地了解代码的实际外观。我还想知道使用当前用户 uid 作为用户集合之外的任何内容的文档 id 是否明智?我最初的想法是,一个团队属于特定的用户,但道路上的这些坎坷让我重新思考我的方法。

我要运行的查询示例:

let ref = db.collection("Team Members").where("teamId", "==", this.$route.params.id) // the id here could possibly be the team name or the document id or something along those lines
ref
  .get()
  .then(querySnapshot => 
    querySnapshot.forEach(doc => 
      this.members.push(doc.data())
    );
  )
  .catch(err => 
    console.log(err);
  )

根据 Ernesto 的评论,我写了一个我想写的示例查询,它源于拥有一个***团队成员集合的想法,该集合跟踪该成员所在的团队。我的想法是,对于这样的查询,路由参数中的 id 可能是团队名称或团队的文档 id,该成员位于当前存在的 Team 集合中。我对这种方法的唯一犹豫是如何在 vue 的 data 属性中正确设置该 id。

【问题讨论】:

据我了解,制作一个包含每个用户或团队成员信息的***集合似乎是一个很好的起点。通过这种方式,您可以简化结构,因为您在每个团队中都没有子集合,因为如果团队成员是多个团队的一部分,它可能会导致重复的团队成员文档。您可以仅添加您要运行的查询的示例吗?只是为了更好地理解问题。 @ErnestoContrerasPinon 感谢您的回复,我编辑了我的帖子以显示一个示例查询。 【参考方案1】:

基于this article,我做了一个简单的Firestore数据库,结构如下:

TeamMembers (Collection)
    tm1 (Doc)
        “FullName”: “John Smith”,
        “MemberOf”: [“team1”, “team2”]
    ...

Teams (Collection)
    team1 (Doc)
        “Owner”: “tm1”,
        “Members”: [“tm1”, “tm2”, tm3”]
    ...

对于这个用例,其中一个团队可以有多个成员,并且每个成员都可以成为多个团队 (M:N) 的一部分,这可以工作。正如您在问题中所说,这里可以采用多种方法。用于查询此结构的有用运算符包含在 array membership 文档中。例如,以下查询将为我提供属于“team1”的所有团队成员:

  let teamRef = firestoreDB.collection("TeamMembers").where("MemberOf", "array-contains", "team1");
  teamRef.get()
    .then((qSnap) => 
      qSnap.forEach((doc) => 
        console.log(doc.data().FullName);
      );
    )
    .catch((err) => 
      console.log("Caught error: ", err);
    );

我们可以编辑此查询以相反的方式工作,找到 TeamMember 所属的团队:

  let teamRef = firestoreDB.collection("Teams").where("Members", "array-contains", "tm1");
  teamRef.get()
    .then((qSnap) => 
      qSnap.forEach((doc) => 
        console.log(doc.id); // Since the team name is the doc ID, it can be obtained from the DocumentSnapshot object
      );
    )
    .catch((err) => 
      console.log("Caught error: ", err);
    );

您必须使用Batch Writes 来执行添加或删除操作,因此您可以在不能半完成的原子操作中更新 Team 和 TeamMember 文档的相关数组。至于如何获取 Document ID 以与 Vuejs 数据道具一起使用,我找到了这个 related question 和相关的 reference 用于 Vuejs。

【讨论】:

很高兴你引用了那篇具体的文章,我最近也读了它并且喜欢它的结构。总体思路对于使用新创建文档的 docRefId 是有意义的,唯一的问题是我们项目中的导航将其作为选项移除。用户从团队视图开始并向下钻取到成员列表,向下钻取页面以添加成员并导航回更新的成员列表视图。我最初的想法是:to=" name: ' ', params: id: id separate 路由器链接中的 id 会与向下钻取导航冲突,但我会测试一下。

以上是关于我可以通过哪些方法正确地在 Firebase Firestore 中的数据结构来建模多对多关系以检索 vue js 中的特定 uid的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS XCUITest 期间配置 Firebase

如何在 C++ 中正确地在循环中使用互斥锁?

如何为 Firebase Dynamic Link Analytics Rest API (NodeJS) 获取正确的访问令牌

有没有办法使用 SwiftUI 和 Firebase 动态地在属性初始化程序中的实例中赋予初始值?

如何将图像从Firebase下载到Swift,然后更快地在UIImageView中显示?

这是从 Firebase 检索数据的正确方法吗?