【问题标题】:Firestore collection group query on documentId对 documentId 的 Firestore 集合组查询
【发布时间】:2019-10-02 14:42:53
【问题描述】:

在我的场景中,用户可以“喜欢”另一个用户的个人资料。结果,我为每个用户创建了一个名为“likedBy”的子集合,我在其中为特定用户创建了一个文档,例如:

users(col) -> 用户 A(doc) -> likeBy(col) -> 用户 B (doc), User C (doc)

因此,在用户被删除的罕见情况下,我想删除该用户的所有点赞问题。

我只是不确定这是否可能(解决方法可能是在所述文档中再次保存 userId 并查询)。

我基本上在寻找的是这样的:

db.collectionGroup('likedBy').where(firebase.firestore.FieldPath.documentId(), '==', "User A").get();

问题是,我无法在 Firestore 控制台中为 documentId 创建索引。

【问题讨论】:

    标签: javascript firebase google-cloud-firestore


    【解决方案1】:

    2020 年 1 月 23 日更新:

    有关这方面的更新,请参阅小组板上与 Sam Stern 的对话:https://groups.google.com/d/msgid/google-cloud-firestore-discuss/e1b47358-b106-43a0-91fb-83c97d6244de%40googlegroups.com

    大部分讨论来自一个明显的事实,即数据库中的所有记录都有一个基于文档的完全合格路径的单一“主索引”(因此只需要在“集合内”是唯一的)。

    为了“加速”文档引用,JS SDK 实际上将集合路径“预置”到 .doc 中传递的任何信息上,以“方便地”使用该主索引

    即所有这些都是完全等价的

    db.doc('collection/docId/collection/docId/collection/docId")
    db.collection('collection").doc("docId/collection/docId/collection/docId")
    db.collection('collection").doc("docId").collection("collection").doc("docId/collection/docId")
    db.collection('collection").doc("docId").collection("collection").doc("docId").collection("collection").doc("docId")
    db.doc("collection/docId").collection("collection").doc("docId").collection("collection").doc("docId")
    db.collection("collection/docId/collection").doc("docId").collection("collection").doc("docId")
    db.doc("collection/docId/collection/docId").collection("collection").doc("docId")
    db.collection("collection/docId/collection/docId/collection").doc("docId")
    

    --- 他们都创建了相同的索引引用“collection/docId/collection/docId/collection/docId”以在“主索引”中查找文档。

    (在我一点也不谦虚的观点中)FieldPath.documentId() 被实现(错误地)以“方便地”匹配此行为,因此需要完全限定的路径,而不是 docId,当它 应该像任何其他查询一样实现,并且需要创建和维护一个新索引以适应查询。

    此行为的代码是在实现 collectionGroups 之前编写的 - 并且从未记录使用的 hack 与使用的 METHOD NAME 不匹配。

    解决方法是要求 Coder 将 docId 作为字段复制到每个文档中,然后在上面写下您的查询。我已经在 Firestore.js 和我的应用程序之间编写了自己的层来抽象行为,并且可能会简单地将其实现为库的基本功能。

    但这显然是一个错误,到目前为止,每个人都一直试图告诉我这是有道理的,他们会更改文档以匹配现有行为(但不是方法名称)。

    正如我之前所写的,我不断收到一堆破烂的雏菊,并被告知“看到了吗?这些是玫瑰!文档称它们为玫瑰!任何其他名字的玫瑰闻起来都一样甜,等等!! "

    除非他们感到尴尬,否则不会有任何更新

    2020-01-10 更新:我已经构建了一个演示应用程序,显示了确切的错误,并已按要求将其发送给 Firebase 支持。出于某种原因,支持者认为这是一个“功能请求”,尽管它显然是一个错误。当以“/ShowInfo/showID”形式调用 URL 时,App 匿名登录 Firebase Auth;然后使用 FieldPath.documentId() "==" showID

    调用对 collectionGroup 的查询(3 级深)

    它以 3 种方式进行查询:

    1) 一次只有 showID- 失败并出现熟悉的“无效查询。当通过 FieldPath.documentId() 查询集合组时,提供的值必须导致有效的文档路径,但 'pqIPV5I7UWne9QjQMm72'(实际showID) 不是因为它有奇数个段 (1)。”

    2) 一次使用“相对路径”(/Shows/showID),没有错误,但不返回任何文档。

    3) 最后是“完整路径”(/Artists/ArtistID/Tour/tourID/Shows/showID)。这没有错误,确实返回一个文档 - 但如果我有完整路径,为什么我需要对 collectionGroup 的查询?而且我没有完整路径 - showID(上图)作为 URL 的一部分出现(显然是节目数据的链接) - 我为测试手工伪造了它。

    等待响应。

    2019 年 12 月 2 日更新:Firebase 支持人员向我询问我是否仍希望解决此问题。呵呵。

    2019 年 9 月 27 日更新:Firebase 支持人员承认这是一个错误。没有关于何时修复的消息。 documentId() 应该,确实,能够直接用于文档 ID。

    documentID 可以 用作查询的一部分,但正如上面@greg-ennis 所指出的,它需要偶数个段。事实证明,如果您确实需要一个 collectionGroup(我需要),那么要比较的“Id”需要冗余将 collectionGroup ID/名称添加为一个段:

    db.collectionGroup('likedBy')
    .where(firebase.firestore.FieldPath.documentId(), '==', "likedBy" + "/" + "User A")
    .get();
    

    我已经使用它并且它 (*sorta) 有效。 (诚​​然,我花了 2 个小时才弄明白,上面的问题有助于集中搜索)

    另一方面,this 特定情况是不是您要使用collectionGroup 的地方。记住集合组 - 一种引用一组独立集合 的方式,就好像它们是一个集合一样。在这种情况下,保存“用户 A”喜欢的“集合”作为“用户 A”文档下的集合存在。只需在删除“用户 A”的文档之前删除 那个 单个集合。不需要把其他人的喜欢都带进去。

    排序:被比较的字段路径显然必须是文档的完整路径。如果您知道 documentId,但由于“原因”,您不知道 完整路径包括子集合属于哪个文档(有点你使用的原因首先是 collectionGroup 方法),现在这似乎是一条死胡同。继续努力。

    已提交的已验证和错误报告:FieldPath.documentID() 与 documentId 进行比较;它与完全分段的文档路径进行比较,就像您必须提供给 .doc(path) 一样:

    即:在“TopCollection/document_this/NextCollection/document_that/Travesty/document_Id_I_want”中查找文档

    使用 collectionGroup "Travesty"

    db.collectionGroup("Travesty")
    .where(firebase.firestore.FieldPath.documentId(), '==', "document_id_I_want")
    

    ...失败,但是...

    db.collectionGroup("Travesty")
    .where(firebase.firestore.FieldPath.documentId(), '==', "TopCollection/document_this/NextCollection/document_that/Travesty/document_Id_I_want")
    .get()
    

    ...成功。这使得这毫无用处,因为如果我们拥有所有 信息,我们将只使用:

    db.doc("TopCollection/document_this/NextCollection/document_that/Travesty/document_Id_I_want")
    .get()
    

    【讨论】:

    • 喜欢您在与 Google“支持”的调查和讨论中付出的细节和努力。如果您有错误报告的参考或将文档 ID 复制到每条记录中的更好解决方案,请告诉我!荣誉。
    • 也偶然发现了这个问题。你有@stevokk 要求的问题链接吗?
    • 不要认为我有错误报告,但我确实有这种情况 #: "Case 00013719: FieldPath.documentId() is return the wrong value"
    • 这是一个很好的总结。很遗憾他们没有在这方面工作,这对于常见的查询是必不可少的。在任何数据库中,如果您有 ID,则可以获取。我希望完成这个项目,并且在我的生活中再也不碰 Firebase。
    • 实帖!非常同意!
    【解决方案2】:

    如果您将用户 A 和 B 的 ID 添加到文档本身:

    users(col) -> User A(doc) -> likedBy(col) -> User B ({user_id: B, profile_liked_id: A})
    

    然后您可以使用以下方式查询:

    db.collectionGroup('likedBy').where('user_id', '==', 'B');
    

    【讨论】:

    • 是的,可以使这样的 hack 起作用 - 它不会改变 FieldPath.documentID 应该起作用。为什么要浪费额外的存储空间?
    【解决方案3】:

    您无法使用以下查询:

    db.collectionGroup('likedBy').where(firebase.firestore.FieldPath.documentId(), '==', "User A").get();
    

    这是因为集合组查询仅适用于文档属性,而适用于文档 ID。根据collection group queries的官方文档:

    db.collectionGroup('landmarks').where('type', '==', 'museum');
    

    您查询landmarks 子集合,其中type 属性保存museum 的值。

    一种解决方法是将用户的 id 存储在一个数组中并使用array-contains,但请记住,对于您使用的每个集合组查询,您都需要一个索引,不幸的是,您无法以编程方式创建这样的索引。即使您可以在 Firebase 控制台中创建索引,它也无济于事,因为您会动态获取这些 ID。因此,不能单独为每个用户创建索引,因为您会很快到达maximim number of indexes

    数据库的最大复合索引数:200

    要解决此类问题,您应该考虑在每个用户对象下添加一个数组并使用如下查询:

    usersRef.where("usersWhoLikedMe", "array-contains", "someUserId")
    

    其中usersWhoLikedMe 是数组类型的属性。

    【讨论】:

    • 感谢您的回答。因此,数组方法的唯一限制是不超过 1 MB 限制(如果我只保存 ID,这将很难),并且限制在 40.000 左右(因为这是文档的最大索引数量),我对么?另外,当我想检索用户名时,我还需要下载完整的点赞列表。
    • 是的,你是对的。这不是关于你必须下载整个喜欢列表,而是关于你将执行的读取操作。 Cloud Firestore 中的一切都与读写次数有关。
    • @AlexMamo 您链接的文档使用按字段查询的示例,但这并不意味着无法按文档 ID 进行集合组查询。当我尝试时,我收到一条错误消息:“按文档 ID 查询集合组时,提供的值必须产生有效的文档路径,但 'testid' 不是因为它有奇数个段。”此错误消息强烈表明这是受支持的,但我不确定要为文档 ID 传递什么,因为它需要偶数个段。有什么想法吗?
    • @AlexMamo, 只是成像我在 Firestore 中保存了一个名为 Recipe 的集合 例如 [Collection(Recipes)-->document(Food_Recipe)-->subCollection(Chiken Recipe),subCollection(Rice Recipe) ,...--->文档()。如果我创建了这样的子集合,我可以使用 CollectionGroup 查询来获取 Food_Recipe 集合中所有不同子集合的特定字段值吗?
    • @IsuruBandara 是的,你可以,只要所有这些子集合中的名称相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-04
    • 2019-07-25
    • 1970-01-01
    • 1970-01-01
    • 2019-06-20
    相关资源
    最近更新 更多