对 documentId 的 Firestore 集合组查询

IT技术 javascript firebase google-cloud-firestore
2021-01-16 11:29:07

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

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

所以在用户被删除的罕见情况下,我想删除该用户的所有喜欢问题。

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

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

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

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

3个回答

更新 2020-01-23:

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

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

为了“加速”文档引用,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,当它应该像任何一样实现时其他查询,并且需要创建和维护一个 NEW INDEX 来容纳该查询。

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

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

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

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

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

2020 年 1 月 10 日更新:我构建了一个演示应用程序,显示了确切的错误,并已根据要求将其发送给 Firebase 支持。出于某种原因,支持小动物将其视为“功能请求”,尽管它显然是一个错误。当以“/ShowInfo/showID”形式调用 URL 时,应用匿名登录 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-09-27:Firebase 支持人员承认这是一个错误。什么时候修好还没有说。实际上,documentId()应该能够直接用于文档 ID。

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

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

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

另一方面,这种特定情况不是您想要使用 collectionGroup 的地方。记住采集组-指AA组的方式单独收集,好像他们是一个。在这种情况下,保存“用户 A”喜欢的“集合”作为“用户 A”文档下的集合存在。在删除“用户 A”的文档之前只需删除单个集合。没必要把其他人的喜好都带进来。

Sorta:显然比较的字段路径必须是文档完整路径。如果您知道 documentId,但由于“原因”,您不知道完整路径包括子集合所属的文档(这就是为什么您首先使用 collectionGroup 方法的原因),现在这似乎有点死路。继续努力。

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

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

使用集合组“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 复制到每条记录的更好解决方案,请告诉我!荣誉。
2021-03-17 11:29:07
这是一个很好的总结。很遗憾他们没有解决这个问题,这对于常见的查询是必不可少的。在任何数据库中,如果您有 ID,就可以获取。我希望能完成这个项目,这辈子再也不会碰 Firebase。
2021-03-22 11:29:07
也偶然发现了这个问题。你有@stevokk 要求的问题链接吗?
2021-03-26 11:29:07
扎实的帖子!非常同意!
2021-03-29 11:29:07
不要以为我有错误报告,因此 - 但我确实有案例#:“案例 00013719:FieldPath.documentId() 返回了错误的值”
2021-04-13 11:29:07

您无法使用以下查询:

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

这是因为集合组查询仅适用于文档属性而不适用于文档 ID。根据有关集合组查询的官方文档

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

您查询属性包含 的值landmarks子集合typemuseum

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

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

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

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

哪里usersWhoLikedMe是数组类型的属性。

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

如果您将用户 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 应该起作用。为什么要浪费额外的存储空间?
2021-03-21 11:29:07