将含列表属性的自定义模型以文档+子集合形式存储于Firestore
Hey there! Let's walk through how to store your Contact model in Firestore, with the phones list saved as a subcollection under the contact document. Here's a step-by-step breakdown with code examples:
First, get your Firestore instance and prepare to save the main contact properties (from the map in your Contact class) as a document in a contacts collection.
import com.google.firebase.firestore.FirebaseFirestore import android.util.Log // Initialize Firestore instance val db = FirebaseFirestore.getInstance() fun saveContact(contact: Contact) { // Generate a unique document ID for the contact (or use a custom ID if you have one) val contactDocRef = db.collection("contacts").document() // Save the contact's core properties (from the map) to the document contactDocRef.set(contact.map) .addOnSuccessListener { Log.d("Firestore", "Contact document saved successfully") // Once the main document is saved, handle the phones subcollection savePhonesSubcollection(contactDocRef, contact.phones) } .addOnFailureListener { e -> Log.e("Firestore", "Failed to save contact document", e) } }
Next, we'll take the phones ArrayList and store each Phone as a document in a phones subcollection under the contact's document. Using a batch write ensures atomicity (all phones save successfully, or none do).
First, make sure your Phone class can convert its properties to a Map (Firestore requires this for storage):
class Phone( val number: String, val type: String // e.g., "mobile", "home", "work" ) { // Convert Phone object to a Firestore-compatible Map fun toMap(): Map<String, Any> { return mapOf( "number" to number, "type" to type ) } }
Then, implement the subcollection save function:
private fun savePhonesSubcollection(contactDocRef: DocumentReference, phones: ArrayList<Phone>) { val phonesCollection = contactDocRef.collection("phones") val batch = db.batch() // Add each phone to the batch write phones.forEach { phone -> val phoneDocRef = phonesCollection.document() // Generate unique ID for each phone batch.set(phoneDocRef, phone.toMap()) } // Commit the batch batch.commit() .addOnSuccessListener { Log.d("Firestore", "Phones subcollection saved successfully") } .addOnFailureListener { e -> Log.e("Firestore", "Failed to save phones subcollection", e) } }
When you need to retrieve a contact later, you'll first fetch the main document, then fetch its phones subcollection:
fun loadContact(contactId: String, onComplete: (Contact?, List<Phone>?) -> Unit) { val contactDocRef = db.collection("contacts").document(contactId) // Fetch the main contact document contactDocRef.get() .addOnSuccessListener { contactDoc -> if (contactDoc.exists()) { val contact = Contact(contactDoc.data as MutableMap<String, Any?>) // Fetch the phones subcollection contactDocRef.collection("phones").get() .addOnSuccessListener { phonesSnapshot -> val phones = phonesSnapshot.documents.mapNotNull { phoneDoc -> Phone( phoneDoc.getString("number") ?: "", phoneDoc.getString("type") ?: "" ) } contact.phones.addAll(phones) onComplete(contact, phones) } .addOnFailureListener { e -> Log.e("Firestore", "Failed to load phones", e) onComplete(contact, null) } } else { onComplete(null, null) } } .addOnFailureListener { e -> Log.e("Firestore", "Failed to load contact", e) onComplete(null, null) } }
- Why a subcollection? Storing
phonesas a subcollection avoids hitting Firestore's 1MB document size limit (critical if contacts can have many phone numbers). It also makes querying individual phone entries easier later. - Document IDs: We're using auto-generated IDs for both contacts and phones, but you can use custom IDs (like a unique
contactIdorphoneIdfrom your model) if you need direct access to specific documents. - Atomicity: Using batch writes for phones ensures that if one phone fails to save, none are saved—this keeps your data consistent.
内容的提问来源于stack exchange,提问作者Niels

