繁体   English   中英

是否可以获取其子集合包含特定文档 ID 的所有文档?

[英]Is it possible to fetch all documents whose sub-collection contains a specific document ID?

我正在尝试获取其子集合包含特定文档 ID 的所有文档。 有没有办法做到这一点?

例如,如果 'enquiries' 子集合下的盒装文档存在,那么我需要来自 'books' 集合的盒装文档 ID。 我不知道如何向后 go 获取父文档 ID。

我的数据库结构

尝试以下操作:

Firestore.instance.collection("books").where("author", isEqualTo: "Arumugam").getDocuments().then((value) {
  value.documents.forEach((result) {
   var id = result.documentID;
   Firestore.instance.collection("books").document(id).collection("enquiries").getDocuments().then((querySnapshot) {
    querySnapshot.documents.forEach((result) {
      print(result.data);
    });

首先,您需要检索books集合下的 id,为了能够做到这一点,您必须进行查询,例如where("author", isEqualTo: "Arumugam") 检索id后,您可以进行查询以检索集合enquiries中的文档

我假设所有子集合都具有相同的名称,即enquiries 然后,您可以执行以下操作:

  1. 在包含文档 ID 的enquiries文档中添加字段docId
  2. 执行集合组查询以获取具有所需docId值的所有文档( Firestore.instance.collectionGroup("enquiries").where("docId", isEqualTo: "ykXB...").getDocuments() )。
  3. 然后,循环查询结果,并为每个DocumentReference调用两次parent()方法( 第一次获得CollectionReference第二次获得父文档的DocumentReference )。
  4. 你只需要使用id属性就可以了。

例如,如果 'enquiries' 子集合下的盒装文档存在,那么我需要来自 'books' 集合的盒装文档 ID。

您无法在单个 go 中做到这一点。

我不知道如何向后 go 获取父文档 ID。

正如您可能想的那样,Firestore 没有回头路了。 在 Firebase 实时数据库中,我们有一个名为getParent()的方法,它完全符合您的要求,但在 Firestore 中我们没有。

Firestore 中的查询很浅,这意味着它仅从运行查询的集合中获取项目。 Firestore 不支持在一个 go 中跨不同 collections 的查询。 单个查询只能使用单个集合中文档的属性。 因此,解决您的问题的解决方案是执行两个get()调用。 第一个是在enquiries子集合中检查该文档是否存在,如果存在,只需创建另一个get()调用以从books集合中获取文档。

Renaud Tarnec 的回答非常适合获取相关书籍的 ID。

如果您需要获取的不仅仅是 ID,则可以在某些情况下使用一个技巧。 我想您的目标是显示与特定查询 ID 关联的所有书籍的某种索引。 如果您希望在该索引中显示的数据不是太长(可以在少于 1500 字节的时间内序列化)并且不经常更改,您可以尝试使用文档 ID 作为该数据的占位符。

例如,假设您想显示与某个enquiryId对应的书名和作者列表。 您可以使用以下内容在集合中创建图书 ID:

// Assuming admin SDK

const bookId = nanoid();
const author = 'Brandon Sanderson';
const title = 'Mistborn: The Final Empire';

// If title + author are not unique, you could add the bookId to the array
const uniquePayloadKey = Buffer.from(JSON.stringify([author, title])).toString('base64url');

booksColRef.doc(uniquePayloadKey).set({ bookId })
booksColRef.doc(uniquePayloadKey).collection('enquiries').doc(enquiryId).set({ enquiryId })

然后,在按照 Renaud Tarnec 的回答运行集合组查询后,您可以使用路径上的正则表达式提取序列化信息,然后反序列化。 例如:

// Assuming Web 9 SDK

const books = query(collectionGroup(db, 'enquiries'), where('enquiryId', '==', enquiryId));

return getDocs(books).then(snapshot => {
  const data = []
  snapshot.forEach(doc => {
    const payload = doc.ref.path.match(/books\/(.*)\/enquiries/)[1];
    const [author, title] = JSON.parse(atob(details));
    data.push({ author, title })
  });
  return data;
});

“在 ID 中存储有效负载”技巧只能用于为您的儿童驱动的搜索结果提供一些基本的人性化信息。 如果您的图书文档有很多您想在用户单击查询返回的图书之一时显示的信息,您可能希望将这些信息存储在 ID 为真正bookId的单独文档中。 在唯一有效负载键下添加的bookId字段允许在必要时进行此类查找。

您可以重复使用相同的数据结构来从不同的起点返回书籍结果,而不仅仅是查询,而无需复制此结构。 例如,如果每本书存储了许多作者,则可以添加作者子集合以进行搜索。 只要您希望在结果索引页面中显示的信息是相同的,并且可以在 1500 字节的限制内进行序列化,您应该是好的。

这种方法的(相当大的)缺点是无法在 Firestore 中重命名文档 ID 如果负载中的某些细节发生变化(例如管理员修复了书名),您将需要创建其下的所有子集合并删除旧数据。 这可能非常昂贵 - 每个子集合中的每个文档至少 1 次读取、1 次写入和 1 次删除。 所以请记住,对于快速变化的数据,它可能并不实用。

键名的 1500 字节限制记录在Usage and Limits中。

如果您担心这可能会根据Cloud Firestore 的最佳实践生成潜在的热点,我想将bookId作为前缀添加到uniquePayloadKey (带有允许您将其丢弃的分隔符)可以解决问题 - 但我不是肯定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM