简体   繁体   English

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

[英]How to add subcollection to a document in Firebase Cloud Firestore

The documentation does not have any examples on how to add a subcollection to a document.该文档没有关于如何将子集合添加到文档的任何示例。 I know how to add document to a collection and how to add data to a document, but how do I add a collection (subcollection) to a document?我知道如何将文档添加到集合以及如何将数据添加到文档,但如何将集合(子集合)添加到文档?

Shouldn't there be some method like this:不应该有这样的方法:

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

Edit 13 Jan 2021: 2021 年 1 月 13 日编辑:

According to the updated documentation regarding array membership , now it is possible to filter data based on array values using whereArrayContains() method.根据有关数组成员资格的更新文档,现在可以使用whereArrayContains()方法基于数​​组值过滤数据。 A simple example would be:一个简单的例子是:

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

This query returns every city document where the regions field is an array that contains west_coast.此查询返回每个城市文档,其中区域字段是包含 west_coast 的数组。 If the array has multiple instances of the value you query on, the document is included in the results only once.如果数组具有您查询的值的多个实例,则该文档仅包含在结果中一次。


Assuming we have a chat application that has a database structure that looks similar to this:假设我们有一个聊天应用程序,它的数据库结构类似于:

在此处输入图片说明

To write a subCollection in a document, please use the following code:要在文档中编写subCollection ,请使用以下代码:

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

If you want to create messages collection and call addDocument() 1000 times will be expensive for sure, but this is how Firestore works.如果您想创建messages集合并调用addDocument() 1000 次肯定会很昂贵,但这就是 Firestore 的工作方式。 You can switch to Firebase Realtime Database if you want, where the number of writes doesn't matter.如果需要,您可以切换到 Firebase 实时数据库,其中写入次数无关紧要。 But regarding Supported Data Types in Firestore, in fact, you can use an array because is supported.但是关于 Firestore 中支持的数据类型,实际上您可以使用数组,因为它是支持的。 In Firebase Realtime database you could also use an array , but this is which is an anti-pattern.Firebase 实时数据库中,您也可以使用array ,但这是一种反模式。 One of the many reasons Firebase recommends against using arrays is that it makes the security rules impossible to write. Firebase 建议不要使用数组的众多原因之一是它使安全规则无法编写。

Cloud Firestore can store arrays, it does not support querying array members or updating single array elements. Cloud Firestore 可以存储数组,它不支持查询数组成员或更新单个数组元素。 However, you can still model this kind of data by leveraging the other capabilities of the Cloud Firestore.但是,您仍然可以利用 Cloud Firestore 的其他功能对此类数据进行建模。 Here is the documentation where is very well explained. 是文档,其中解释得很好。

You also cannot create a subcollection with 1000 messages and add all of them to the database and at the same time to consider a single record.您也不能创建一个包含 1000 条消息的子集合并将它们全部添加到数据库中,同时考虑单个记录。 It will be considered one write operation for every message.对于每条消息,它将被视为一次写入操作。 In total 1000 operations.总共 1000 次操作。 The picture above does not show how to retrieve data, it shows a database structure in which you have something like this:上图没有显示如何检索数据,它显示了一个数据库结构,其中包含以下内容:

collection -> document -> subCollection -> document

Here's a variation where the subcollection is storing ID values at the collection level, rather than within a document where the subcollection is a field there with additional data.这是一个变体,其中子集合在集合级别存储 ID 值,而不是在子集合是包含附加数据的字段的文档中。

This is useful for connecting a 1-to-Many ID mapping w/out having to drill through an additional document:这对于连接1 对多 ID 映射非常有用,而不必钻取额外的文档:

function fireAddStudentToClassroom(studentUserId, classroomId) {

    var db = firebase.firestore();
    var studentsClassroomRef =
        db.collection('student_class').doc(classroomId)
          .collection('students');

    studentsClassroomRef
        .doc(studentUserId)
        .set({})
        .then(function () {
            console.log('Document Added ');
        })
        .catch(function (error) {
            console.error('Error adding document: ', error);
        });
}

Thanks to @Alex's answer感谢@Alex 的回答

This answer a bit off from the original question here, where it explicitly asks for adding a collection to a document.这个答案与这里的原始问题有点不同,它明确要求将集合添加到文档中。 However, after searching for a solution for this scenario and not finding any mention in docs or on SO, this post seems like a reasonable place to share the findings然而,在为这种情况寻找解决方案后,在文档或 SO 中没有找到任何提及,这篇文章似乎是分享研究结果的合理场所

Here's my code:这是我的代码:

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);
  });

This makes a collection EnglishWords , which has a document of .这使得集合EnglishWords ,其中有一个文件of The document of has three subcollections: AudioSources (recordings of the word in American and British accents), FunFacts , and Translations .该文件of有三个子集合: AudioSources ,(在美国和英国口音字的录音) FunFactsTranslations The subcollection Translations has one document: Spanish .子集合Translations有一个文档: Spanish The Spanish document has three key-value pairs, telling you that 'de' is the Spanish translation of 'of'. Spanish文档有三个键值对,告诉您“de”是“of”的西班牙语翻译。

The first line of the code creates the collection EnglishWords .代码的第一行创建集合EnglishWords We wait for the promise to resolve with .then , and then we create the three subcollections.我们等待 promise 用.then解决,然后我们创建三个子集合。 Promise.all tells us when all three subcollections are set. Promise.all告诉我们所有三个子集合何时设置。

IMHO, I use arrays in Firestore when the entire array is uploaded and downloaded together, ie, I don't need to access individual elements.恕我直言,当整个数组一起上传和下载时,我在 Firestore 中使用数组,即,我不需要访问单个元素。 For example, an array of the letters of the word 'of' would be ['o', 'f'] .例如,单词 'of' 的字母数组将是['o', 'f'] The user can ask, "How do I spell 'of'?"用户可以问,“我如何拼写‘of’?” The user isn't going to ask, "What's the second letter in 'of'?"用户不会问,“'of' 中的第二个字母是什么?”

I use collections when I need to access individual elements, aka documents.当我需要访问单个元素(也就是文档)时,我会使用集合。 With the older Firebase Realtime Database, I had to download arrays and then iterate through the arrays with forEach to get the element I wanted.使用较旧的 Firebase 实时数据库,我必须下载数组,然后使用forEach遍历数组以获取我想要的元素。 This was a lot of code, and with a deep data structure and/or large arrays I was downloading tons of data that I didn't need, and slowing my app running forEach loops on large arrays.这是大量代码,并且具有深层数据结构和/或大型数组,我正在下载大量不需要的数据,并减慢我的应用程序在大型数组上运行forEach循环的速度。 Firestore puts the iterators in the database, on their end, so that I can request a single element and it sends me just that element, saving me bandwidth and making my app run faster. Firestore 将迭代器放在数据库的最后,这样我就可以请求一个元素,它只向我发送那个元素,从而节省了我的带宽并使我的应用程序运行得更快。 This might not matter for a web app, if your computer has a broadband connection, but for mobile apps with poor data connections and slow devices this is important.如果您的计算机有宽带连接,这对于网络应用程序可能无关紧要,但对于数据连接较差且设备速度较慢的移动应用程序,这很重要。

Here are two pictures of my Firestore:这是我的 Firestore 的两张照片: 在此处输入图片说明

在此处输入图片说明

From the docs:从文档:

You do not need to "create" or "delete" collections.您不需要“创建”或“删除”集合。 After you create the first document in a collection, the collection exists.在集合中创建第一个文档后,集合就存在了。 If you delete all of the documents in a collection, it no longer exists.如果您删除集合中的所有文档,它就不再存在。

Here i faced the same issue and solve with the answere of @Thomas David Kehoe在这里,我遇到了同样的问题,并通过@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)
            })

too late for an answer but here is what worked for me,回答太晚了,但这对我有用,

        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)
            }

add success listener for adding document and use firebase generated ID for a path.添加用于添加文档的成功侦听器并使用 firebase 生成的 ID 作为路径。 Use this ID for the complete path for a new collection you want to add.将此 ID 用于要添加的新集合的完整路径。 IE - dbReference.collection('yourCollectionName').document(firebaseGeneratedID).collection('yourCollectionName').add(yourDocumentPOJO/Object) IE - dbReference.collection('yourCollectionName').document(firebaseGeneratedID).collection('yourCollectionName').add(yourDocumentPOJO/Object)

Okay so I recently faced a similar problem given the recent update in the firebase/firestore documentation.好的,鉴于 firebase/firestore 文档中的最新更新,我最近遇到了类似的问题。

And here is a solution that worked for me这是一个对我有用的解决方案

  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,
    });
  }

You can find a similar example in the docs https://firebase.google.com/docs/firestore/data-model#web-version-9_3您可以在文档https://firebase.google.com/docs/firestore/data-model#web-version-9_3 中找到类似的示例

chat room聊天室

If you wish to listen for live update you can use a similar method as follows如果您想收听实时更新,您可以使用如下类似的方法

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);
        });
      }

There is no separate method to add sub-collection into the document.没有单独的方法可以将子集合添加到文档中。 You can just call the collection method itself.您可以只调用集合方法本身。 If the collection exists it will reference that otherwise create a new one.如果该集合存在,它将引用该集合,否则创建一个新集合。

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

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

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