Firestore:如何查询文档集合中的地图对象?

Firestore: How to query a map object inside a collection of documents?

我的objective:

Web 应用程序(类似于 Google 云端硬盘)有 2 个屏幕。

第一个屏幕是 'My Story',其中显示了我是所有者的所有故事文档。

第二个屏幕是 'Shared with me',显示我是 reader、作家或评论者的所有故事文档。

我们有这个 /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

问题:如何编写下面的查询来实现上面的objective?

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

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


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

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

此外,当您复制数据时,需要记住一件事。以与添加数据相同的方式,您需要维护它。换句话说,如果你想 update/detele 一个项目,你需要在它存在的每个地方都这样做。

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

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

所以这样的查询:

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

会很好地工作。

这是不可能的,因此我们需要在 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)

虽然我们可以查询 'Shared with me'

的 Web 应用 UI
db
.collection('stories')
.where('shared', 'array_contains', uid)

答案的灵感来自@Doug Stevenson 的评论。

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

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

这会得出正确的结果:

现在您可以过滤...

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

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