如何将子集合添加到 Firebase Cloud Firestore 中的文档

Posted

技术标签:

【中文标题】如何将子集合添加到 Firebase Cloud Firestore 中的文档【英文标题】:How to add subcollection to a document in Firebase Cloud Firestore 【发布时间】:2018-05-10 21:32:25 【问题描述】:

该文档没有任何关于如何将子集合添加到文档的示例。我知道如何将文档添加到集合以及如何将数据添加到文档,但是如何将集合(子集合)添加到文档?

不应该有这样的方法吗:

dbRef.document("example").addCollection("subCollection")

【问题讨论】:

【参考方案1】:

2021 年 1 月 13 日编辑:

根据关于array membership 的更新文档,现在可以使用whereArrayContains() 方法根据数组值过滤数据。一个简单的例子是:

CollectionReference citiesRef = db.collection("cities");
citiesRef.whereArrayContains("regions", "west_coast");

此查询返回每个城市文档,其中区域字段是包含 west_coast 的数组。如果数组有多个您查询的值的实例,则该文档仅包含在结果中一次。


假设我们有一个聊天应用程序,其数据库结构类似于:

要在文档中写入subCollection,请使用以下代码:

DocumentReference messageRef = db
    .collection("rooms").document("roomA")
    .collection("messages").document("message1");

如果你想创建messages 集合并调用addDocument() 1000 次肯定会很昂贵,但这就是 Firestore 的工作方式。如果需要,您可以切换到 Firebase 实时数据库,写入次数无关紧要。但是对于Firestore中的Supported Data Types,其实可以使用数组,因为是支持的。在Firebase Realtime database 中,您也可以使用array,但这是一种反模式。 Firebase 建议不要使用数组的众多原因之一是它使安全规则无法编写。

Cloud Firestore 可以存储数组,它不支持查询数组成员或更新单个数组元素。但是,您仍然可以利用 Cloud Firestore 的其他功能对此类数据进行建模。 Here 是解释得很好的文档。

您也不能创建一个包含 1000 条消息的子集合并将它们全部添加到数据库中,同时考虑一条记录。它将被视为对每条消息的一次写入操作。总共1000次操作。上图没有显示如何检索数据,它显示了一个数据库结构,其中你有这样的东西:

collection -> document -> subCollection -> document

【讨论】:

例如,您想如何在 messages 集合中动态添加 1000 条消息(它们的 id 是自动生成的),然后在一个查询中将此集合添加到 roomA 中?我很确定创建messages 集合并调用addDocument() 1000 次会很昂贵(在所有意义上)。我正在寻找一种方法来创建这些文档并将它们分配给本地子集合,然后将子集合添加到文档中。我目前正在设置一个字典数组(或哈希图)并将该数组设置为文档的数据类型... 但鉴于 Firestore 的强大功能,字典数组听起来并不是最佳解决方案。实际上,您上面的图片显示了如何从子集合中检索(如在文档中),但是文档中根本没有解释如何将子集合添加到文档中:( 不能编写整个数组(集合)的事实有点疯狂。这是一个基本的需求,但我必须遍历所有内容才能将它们单独添加到集合中......让我想知道,首先使用 firestore 的优势是什么? 请查看我的更新答案并查看此post 以获得更好的理解。你觉得我的回答对你有帮助吗? 任何人请帮助使用网络查询在文档(房间A)中添加字段(“名称”)和集合(“消息”)。我不知道如何在文档中添加字段 + 集合【参考方案2】:

这是一种变体,其中子集合在集合级别存储 ID 值,而不是在文档中,其中子集合是具有附加数据的字段。

这对于连接 一对多 ID 映射很有用,而无需钻取其他文档:

function fireAddStudentToClas-s-room(studentUserId, clas-s-roomId) 

    var db = firebase.firestore();
    var studentsClas-s-roomRef =
        db.collection('student_class').doc(clas-s-roomId)
          .collection('students');

    studentsClas-s-roomRef
        .doc(studentUserId)
        .set()
        .then(function () 
            console.log('Document Added ');
        )
        .catch(function (error) 
            console.error('Error adding document: ', error);
        );

感谢@Alex's answer

这个答案与这里的原始问题有点不同,它明确要求将集合添加到文档中。然而,在为这种情况寻找解决方案并且在文档或 SO 中没有找到任何提及之后,这篇文章似乎是分享发现的合理场所

【讨论】:

【参考方案3】:

这是我的代码:

firebase.firestore().collection($scope.longLanguage + 'Words').doc($scope.word).set(wordData)
  .then(function() 
    console.log("Collection added to Firestore!");
    var promises = [];
    promises.push(firebase.firestore().collection($scope.longLanguage + 'Words').doc($scope.word).collection('Audiosources').doc($scope.accentDialect).set(accentDialectObject));
    promises.push(firebase.firestore().collection($scope.longLanguage + 'Words').doc($scope.word).collection('FunFacts').doc($scope.longLanguage).set(funFactObject));
    promises.push(firebase.firestore().collection($scope.longLanguage + 'Words').doc($scope.word).collection('Translations').doc($scope.translationLongLanguage).set(translationObject));
    Promise.all(promises).then(function() 
      console.log("All subcollections were added!");
    )
    .catch(function(error)
      console.log("Error adding subcollections to Firestore: " + error);
    );
  )
  .catch(function(error)
    console.log("Error adding document to Firestore: " + error);
  );

这将创建一个集合EnglishWords,其中包含一个文档of。文档of 包含三个子集合:AudioSources(以美国和英国口音记录的单词)、FunFactsTranslations。子集合Translations 有一个文档:SpanishSpanish 文档有三个键值对,告诉你'de'是'of'的西班牙语翻译。

代码的第一行创建集合EnglishWords。我们等待用.then 解决的承诺,然后我们创建三个子集合。 Promise.all 告诉我们何时设置了所有三个子集合。

恕我直言,当整个数组一起上传和下载时,我在 Firestore 中使用数组,也就是说,我不需要访问单个元素。例如,单词“of”的字母数组将是['o', 'f']。用户可以问:“我如何拼写‘of’?”用户不会问:“'of' 中的第二个字母是什么?”

当我需要访问单个元素(也就是文档)时,我会使用集合。对于较旧的 Firebase 实时数据库,我必须下载数组,然后使用 forEach 遍历数组以获得我想要的元素。这是很多代码,并且由于数据结构和/或大型数组很深,我正在下载大量不需要的数据,并减慢了我的应用程序在大型数组上运行 forEach 循环的速度。 Firestore 将迭代器放在数据库的末端,这样我就可以请求单个元素,它只向我发送那个元素,从而节省了我的带宽并使我的应用程序运行得更快。如果您的计算机有宽带连接,这对于网络应用程序可能无关紧要,但对于数据连接较差且设备速度较慢的移动应用程序来说,这一点很重要。

这是我的 Firestore 的两张图片:

【讨论】:

【参考方案4】:

来自文档:

您不需要“创建”或“删除”集合。在集合中创建第一个文档后,集合就存在了。如果您删除集合中的所有文档,则它不再存在。

【讨论】:

【参考方案5】:

答案为时已晚,但这对我有用,

        mFirebaseDatabaseReference?.collection("conversations")?.add(Conversation("User1"))
            ?.addOnSuccessListener  documentReference ->
                Log.d(TAG, "DocumentSnapshot written with ID: " + documentReference.id)
                mFirebaseDatabaseReference?.collection("conversations")?.document(documentReference.id)?.collection("messages")?.add(Message(edtMessage?.text.toString()))
            ?.addOnFailureListener  e ->
                Log.w(TAG, "Error adding document", e)
            

添加成功侦听器以添加文档并使用 firebase 生成的 ID 作为路径。 将此 ID 用于您要添加的新集合的完整路径。 IE。 - dbReference.collection('yourCollectionName').document(firebaseGeneratedID).collection('yourCollectionName').add(yourDocumentPOJO/Object)

【讨论】:

【参考方案6】:

在这里我遇到了同样的问题并用@Thomas David Kehoe 的回答解决了

db.collection("First collection Name").doc("Id of the document").collection("Nested collection Name").add(
                //your data
            ).then((data) => 
                console.log(data.id);
                console.log("Document has added")
            ).catch((err) => 
                console.log(err)
            )

【讨论】:

【参考方案7】:

好吧,鉴于 firebase/firestore 文档中的最新更新,我最近遇到了类似的问题。

这是一个适合我的解决方案

  const sendMessage = async () => 
    await setDoc(doc(db, COLLECTION_NAME, projectId, SUB_COLLECTION_NAME, nanoid()), 
      text:'this is a sample text',
      createdAt: serverTimestamp(),
      name: currentUser?.firstName + ' ' + currentUser?.lastName,
      photoUrl: currentUser?.photoUrl,
      userId: currentUser?.id,
    );
  

您可以在文档中找到类似的示例 https://firebase.google.com/docs/firestore/data-model#web-version-9_3

chat room

如果你想收听实时更新,你可以使用类似的方法如下

const messagesRef = collection(db, COLLECTION_NAME, projectId, SUB_COLLECTION_NAME)
      
const liveUpdate = async () => 
        const queryObj = query(messagesRef, orderBy("createdAt"), limit(25));
        onSnapshot(queryObj, (querySnapshot) => 
          const msgArr: any = [];
          querySnapshot.forEach((doc) => 
            msgArr.push( id: doc.id, ...doc.data() )
          );
          console.log(msgArr);
        );
      

【讨论】:

【参考方案8】:

没有单独的方法可以将子集合添加到文档中。 您可以只调用收集方法本身。 如果集合存在,它将引用该集合,否则创建一个新集合。

dbRef.document("example").collection("subCollection")

【讨论】:

以上是关于如何将子集合添加到 Firebase Cloud Firestore 中的文档的主要内容,如果未能解决你的问题,请参考以下文章

Firebase Firestore 子集合

如何在 Firestore 中获取生成的文档 ID,然后将子集合添加到其中?

如何将 CSV 或 JSON 导入到 Firebase Cloud Firestore

从Firebase的Cloud Firestore删除文档是否会删除该文档中的所有子集合?

如何在 Firebase + Firestore + Flutter 中使用 StreamBuilder 将所有子集合数据获取到 Widget

新的 Firebase Firestore DocumentDb 如何为大型子集合建模