【问题标题】:Firestore: How to query a map object inside a collection of documents?Firestore:如何查询文档集合中的地图对象?
【发布时间】:2019-02-21 16:01:22
【问题描述】:

我的目标:

网络应用(类似于 Google 云端硬盘)有 2 个屏幕。

第一个屏幕是“我的故事”,它显示了我拥有的所有故事文档。

第二个屏幕是“与我共享”,显示我是读者、作者或评论者的所有故事文档。

我们有这个/stories/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time ...",
  roles: {
    alice: "owner",
    bob: "reader",
    david: "writer",
    jane: "commenter"
    // ...
  }
}

完整数据结构参考:https://firebase.google.com/docs/firestore/solutions/role-based-access

问题:如何编写以下查询来实现上述目标?

db
.collection('stories')
.where(`roles.${uid}`, '==', 'owner')

上述查询不起作用,因为它需要 Firestore 索引 roles.alice、roles.bob、roles.david、roles.jane 和 roles.[all_uid]。


编辑#1:找到一个相关帖子(没有答案)询问Firestore querying with roles

【问题讨论】:

  • 您提供的链接是关于安全性的,而不是关于查询的。如果要按用户查询此文档,则必须为每个用户编制索引。或者,重构您的数据,以便您可以以不需要用户名作为文档中的字段的方式查询文档。
  • 所以基本上你想得到alice == owner的所有故事,对吧?
  • @AlexMamo 是的。想要获取属于所有者的 UID(例如 alice)的所有故事。
  • @DougStevenson 是的,提供的链接是关于安全性的。但是,该链接仅引用您的团队提供的数据结构作为示例。使用示例数据结构,并且想要进行查询,按用户索引是没有意义的。那么如果有 1,000,000 个用户,那么必须创建那么多索引吗?没门!那么如何查询一个文档中的角色,可以返回所有文档都属于一个user_id呢?例如爱丽丝。
  • 正如我所说,您需要使用不同的结构来支持您想要执行的查询。在 NoSQL 数据库中,为不同的需求复制数据是很常见的。你有一个安全需求,这很好。现在你有第二个需求,需要第二个解决方案。

标签: firebase google-cloud-firestore angularfire2 angularfire5


【解决方案1】:

现在您可以过滤...

db
  .collection('orders')
  .where('orderDetails.status', '==', 'OPEN')

它将检查属性 status 中的字段 orderDetails 是否等于 'OPEN'

【讨论】:

    【解决方案2】:

    您无法通过您的实际数据库结构以不需要为每个用户分别创建索引的方式实现此目的。为了解决这个问题,您应该复制您的数据。这种做法称为denormalization,是 Firebase 的常见做法。如果您是 NoQSL 数据库的新手,我建议您观看此视频,Denormalization is normal with the Firebase Database,以便更好地理解。它适用于 Firebase 实时数据库,但同样的规则适用于 Cloud Firestore。

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

    话虽如此,您应该创建另一个名为userStories 的集合,您应该在其中添加用户为owner 的所有故事作为文档。所以你的数据库结构应该类似于这样:

    Firestore-root
       |
       --- userStories (collection)
             |
             --- uid (document)
                  |
                  --- allStories (collection)
                         |
                         --- storyId
                               |
                               --- role: "owner"
    

    所以这样的查询:

    db.collection('userStories').doc(${uid})
        .collection('allStories').where(`role`, '==', 'owner');
    

    工作得很好。

    【讨论】:

    • 你能评论我的回答吗?我想我知道我的问题的答案。
    • 请看我上面的评论。所以我会坚持这个答案。
    • 是什么促使您回答关于 SO 的问题?只是好奇。 Google/Firebase 是否会为每个成功的答案付费?
    • @choopage-JekBao 没有。我也曾经被帮助过,我想以同样的方式帮助别人;)
    【解决方案3】:

    如果我弄错了,请告诉我,但您的查询似乎应该有效。作为实验,我添加了您在问题中提供的结构,并在 firebase 控制台中成功执行了查询。

    这不是你想要的吗?我还确保“in”运算符也适用于这种情况。通过这种方式,您可以询问用户是哪些故事的所有者和评论者。

    这会得到正确的结果:

    【讨论】:

    • 你有索引吗?
    • 没有。没有额外的配置来完成这项工作。在本例中,这是开箱即用的。
    • 这就是我要找的。​​span>
    • 这实际上似乎有效,我刚刚用相同的数据结构做了一个测试,一切似乎都很好。
    【解决方案4】:

    这是不可能的,因此我们需要在 NoSQL 中复制数据。

    一种可能的数据结构是

    /stories/{storyid}

    {
      title: "A Great Story",
      content: "Once upon a time ...",
      roles: {
        alice: "owner",
        bob: "reader",
        david: "writer",
        jane: "commenter",
        mary: "writer",
        // ...
      },
      owner: "alice", // newly added
      shared: ["bob", "david", "jane", "mary"] // newly added
      // alice, bob, david, jane, mary are firebase auth uid
    }
    

    所以我们可以查询网络应用 UI 'My Story'

    db
    .collection('stories')
    .where('owner', '==', uid)
    

    虽然我们可以查询“与我共享”的 Web 应用 UI

    db
    .collection('stories')
    .where('shared', 'array_contains', uid)
    

    Answer 的灵感来自 @Doug Stevenson cmets。

    【讨论】:

    • @DougStevenson 你觉得这个答案怎么样?
    • 不,这不是一个选项,因为您在同一个文档中复制数据。在这种情况下,如何查找所有故事jane == commenter?当我们复制数据时,我们复制了整个文档,因此我们可以复制已经存在于一个地方、另一个地方的完全相同的数据,以适应可能无法进行的查询。这正是你的情况。
    猜你喜欢
    • 2020-05-08
    • 1970-01-01
    • 2020-07-26
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 2021-04-08
    相关资源
    最近更新 更多