【问题标题】:Querying for object key in Firestore在 Firestore 中查询对象键
【发布时间】:2020-07-09 22:43:20
【问题描述】:

我的 Firestore 查询技术目前存在一些问题。根据我最近发布的这篇 stackoverflow 帖子,Querying with two array with firestore security rules

答案建议将“ids”添加到对象中,键为id,值简单地为“true”。我已经完成了这个,现在我的结构是这样的:

这给我留下了这个查询:

db.collection('Depots')
  .where(`products.${productId}`, '==', true)
  .where(`users.${userId}`, '==', true)
  .where('created', '>', 1585998560500)
  .orderBy('created', 'asc')
  .get();

这个查询让我抛出一个错误,要求创建一个索引:

查询需要索引。你可以在这里创建它:...

但是,这会尝试索引特定的对象键,即QXooVYGBIFWKo6C 所以products.QXooVYGBIFWKo6C。这当然不是我想要的,因为这个查询会发生变化,并且可能有无数种可能性,这意味着我必须为每个键条目创建另一个索引才能查询它。

有什么办法可以解决这个问题吗?由于查询中使用了不同的运算符,我假设它需要为此查询建立索引,所以我想知道是否有任何解决方法来解决这个问题。

非常感谢您。

【问题讨论】:

  • 很遗憾,我看不出这个结构如何被扁平化为一个索引。 “用户”的目的是什么?如果您可以将其转换为“user0 = someId”和“user1 = someOtherId”,您可能需要进行两次查询,但您可以保留产品密钥的数组包含(您将对where('user0', '==', userId) 使用一个查询,另一个where('user1', '==', userId) 然后在客户端合并结果)。
  • @samthecodingman 我觉得这有点乱 - 我不知道每个文档会有多少用户,所以我不想将密钥硬编码为特定数量的每个项目的用户数。
  • 我知道它很乱——我的意思是要证明它不能被展平成一个单一的索引。我给出的示例将其展平为仅两个索引。
  • 您最终想要实现/查询什么?什么是理想的查询案例?我不知道怎么会有这么多答案,甚至没有人问过你这个问题。
  • @bsod 当我查询时,程序中需要文档中的其他数据 - 我删除了它,因为它对问题没有帮助,并且两个地图不适合窗口让我截图。

标签: javascript firebase google-cloud-firestore firebase-authentication


【解决方案1】:

这里有一个地图字段,通常应该为其创建索引automatically

这确实意味着您将拥有与产品一样多的索引,这意味着:

  1. 您可以拥有的产品数量有限,因为有一个maximum of 40,000 index entries per document
  2. 您为每个文档支付更多费用,因为您为每个索引的存储付费。

如果这些不是您想要的,您将不得不切换回原来的模型,并使用您在那里的查询限制。似乎没有一种解决方案可以同时满足您的两个要求。

【讨论】:

  • 感谢您的评论。我尝试了没有不同运算符的查询(删除了“>”和“orderBy”,它确实可以与该查询一起使用,而无需创建单独的索引。当前结构有什么解决方法吗?再次感谢您的帮助.
【解决方案2】:

在我们聊天讨论之后,这是我建议的起点。谁知道最终架构会是什么样子,但我认为这个或非常接近这个。您说一个用户可以同时存在于多个仓库中,并且多个仓库可以同时包含相同的产品。您还说过,一个软件仓库在给定时间永远不能拥有超过 40 个用户,因此 40 个用户的数组肯定不会侵犯 Firestore 的 1,048,576 字节的文档限制。

[collection]
    <documentId>
        - field: value

[depots]

    <UUID>
        - depotId: string       "depot456"
        - productCount: num     5,000

    <UUID>
        - depotId: string       "depot789"
        - productCount: num     4,500


[products]

    <UUID>
        - productId: string     "lotion123"
        - depotId: string       "depot456"
        - users: [string]       ["user10", "user27", "user33"]

    <UUID>
        - productId: string     "lotion123"
        - depotId: string       "depot789"
        - users: [string]       ["user10", "user17", "user50"]


[users]

    <userId>
        - depots: [string]      ["depot456", "depot999"]

    <userId>
        - depots: [string]      ["depot333", "depot999"]

在 NoSQL 中,存储成本低廉,计算不会像您需要的那样对数据进行非规范化,以使您的查询成为可能且高效(快速且廉价)。

要在单个查询中查找 user10lotion123 都为真的所有 depot,请查询 productId 等于 xusers 数组包含 y 的产品集合并收集 @987654330 @ 这些结果中的值。如果要为其他内容保留数组包含操作,则必须进一步对数据进行非规范化(替换单个用户的数组)。或者您可以将此查询拆分为两个单独的查询。

使用此模型,当用户离开仓库时,获取 users 数组包含该用户的所有产品,并从数组中删除该 userId。当用户加入仓库时,获取所有 depotId 等于 x 的产品并将 userId 附加到数组中。

观看此视频以及 Rick 的其他视频,以深入了解 NoSQL:https://www.youtube.com/watch?v=HaEPXoXVf2k

【讨论】:

  • 非常感谢您,这正是我所需要的!再次感谢您的持续帮助。
  • 观看该视频!它真的会帮助你。
  • 会的! :) 再次感谢。
【解决方案3】:

@danwillm 如果您不确定用户和产品的数量,那么您的数据库结构似乎不适合这种情况,因为 firestore 文档的大小和长度存在限制。 您应该为产品和用户创建一个单独的集合,即规范化您的数据并在产品集合中为用户提供参考。

User : 
       {
          userId: documentId,
          name: John,
          ...otherInfo
       }

Product : 

       {
          productId: documentId,
          createdBy: userId,
          createdOn:date,
          productName:"exa",
          ...otherInfo
       }

这样您的文档大小将受到限制,即如果您不确定文档的大小,请尽量避免在 Firestore 中使用地图/数组。 此外,在这种情况下,查询的数量会增加,但在这种情况下您不需要很多索引。

【讨论】:

    猜你喜欢
    • 2019-06-23
    • 2019-01-09
    • 2018-05-24
    • 2018-03-26
    • 2021-07-30
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    • 2021-07-29
    相关资源
    最近更新 更多