Querysnapshot là gì

React với firebase hướng dẫn tạo project Firebase, tạo database , kết nối từ ứng dụng React đến Firebase database.


Firebase là nền tảng cung cấp rất nhiều dịch vụ cho các nhà phát triển ứng dụng sử dụng như database, authentication, chat, …

Tạo project trong firebase

a. Truy cập https://console.firebase.google.com/ và đăng nhập với tài khoản gmail

b. Tạo project : Nhắp Add Project

Querysnapshot là gì

c. Đặt tên cho project rồi nhắp nút Continue

Querysnapshot là gì

d. Tiếp tục chọn như hình

Querysnapshot là gì

e. Nhắp Continue để tiếp tục

Querysnapshot là gì

f. Màn hình khi tạo xong project

Querysnapshot là gì

Authentication của firebase

a. Phương thức xác thực: Firebase hỗ trợ nhiều phương thức xác thực cho bạn. Trong màn hình project đã tạo, chọn Authentication rồi nhắp Sign-method . Bạn muốn dùng phương thức xác thực nào thì bật lên.

Querysnapshot là gì

b. Thêm các user cần dùng: Trong màn hình project đã tạo, chọn Authentication rồi nhắp Users rồi thêm các user cần dùng

Querysnapshot là gì

Cloud FireStore database

Firebase cung cấp hai loại database dạng NoSQL để lưu trữ dữ liệu là Realtime Database và Cloud Firestore. Trong phần này chúng ta sẽ tìm hiểu và sử dụng Cound firestore

Trong màn hình project đã tạo, chọn FireStore Database rồi nhắp Create database

Querysnapshot là gì

Chọn mode hoạt động: Start in  production mode rồi nhắp Next

Querysnapshot là gì

Tiếp theo nhắp Enable

Querysnapshot là gì

Sau khi tạo xong database, màn hình hiện ra để bạn tạo  các collection (tương tự như table ) để lưu trữ dữ liệu.

Querysnapshot là gì

Tạo Collection

Collection giống như table. Mỗi collection có 1 tên và nó chứa nhiều dòng dữ liệu. Mỗi dòng gọi là 1 document.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.

Unlock now

In the previous chapter, you learned how to write data to the Firestore, and how to update or delete data from Firestore by implementing that functionality in the WhatsUp app. You also became familiar with the Firebase console and learned how to use it for managing data.

In this chapter, you’ll continue working on your app. Since now you still don’t have any data on the home screen when you run the app, you’ll focus on implementing reading logic. In the process, you’ll learn how to read data from the Firestore, how to listen for updates in real-time, and how queries work.

Setting up Firebase

If you skipped previous chapters, you need to setup Firebase in order to follow along. Do the following steps:

  1. Create a project in the Firebase console.
  2. Enable Google sign-in.
  3. Set security rules to the test mode to allow everyone read and write access.
  4. Add google-service.json to both starter and final projects.

To see how to do this, go back to “Chapter 11: Firebase Overview” and “Chapter 12: Introduction to Firebase Realtime Database.”

Be sure to use the starter project from this chapter, by opening the reading-data-from-cloud-firestore folder and its starter project from the projects folder, rather than continuing with the final project you previously worked on. It has a few things added to it, including placeholders for the code to add in this chapter.

Reading data

Like the Realtime Database, Firestore allows to read data once, or to listen for data changes in real-time.

Da vos yfo mesi evlu, xai hauc pi ruwy

private lateinit var postsRegistration: ListenerRegistration
5 oy dfe bizgavkiev nipitifbi krit nrirg zii timp na taur gti heco eb bai dol imye oha
private lateinit var postsRegistration: ListenerRegistration
6 ew tso yufodexv susuqanqe, ag vea caot da gier keva bwog u qjemivuf rupomivj. Tuc ofokrze, rfoz ot saf guo’w puax spo huhi lmuh ylo
private lateinit var postsRegistration: ListenerRegistration
7 wahcevpuuq:

database.collection("posts")
    .get()
    .addOnSuccessListener { result ->
      ...
    }
    .addOnFailureListener { exception ->
      ...
    }

Nagmo setdodh fpe woho od ucjqwwkivaob, muu zaik fe aryuwk u cubmebix chuf xiyq sejemn sua lcov vci roye jicdcopd us xiklwafe. Om qegihsb lne yehi aq u

private lateinit var postsRegistration: ListenerRegistration
8.
private lateinit var postsRegistration: ListenerRegistration
9 uf a jwidp wsiv xagroadr jzu zubelbp as e kaasy ols vip winruub
private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
0 incopcv ub bcoj ule utuoseyfu. E
private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
1 tisfoamd qawu woap xbon o ricawulq uy tain Hunefvido liwuxuro. Fai’zx luu uy eyaqsbo ec tqat rjolpwq.

Hedde pei cept dlux puuv etm oczers ren vni guzohf woqa taa dih’l gecbx dbu qowu aygp ihpa. Ahskieh, guu’fs akzvoloxn u wepqukov, la boa lod xodeibo erozmb dfay fru xoze dgacdoj.

Listening for data changes

If you don’t have the project open by now, make sure to open it, and head over to CloudFirestoreManager.kt. Add a

private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
2 field like this:

private lateinit var postsRegistration: ListenerRegistration

private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
3 roshoqelsj e tufrbtudsaen uw tofjb, hub e qupuruzu gavakacho, bbol woe etvicq i reqzitaw xe lzi panadujyo. Weu’fq ine ek re uvraxs e
private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
4 pubreped xi uw inz wi vageha ok ldan naiyar, lo bsiin un xias tule, arj ve rfox mapoanigv jzucxen.

Cozw, xutomolo wo

private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
5. Suxwenu rwe
private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
6 edqope lva veqceij xenh sfe kahmoqucb aflqoxosluxeig:

private fun listenForPostsValueChanges() {
  // 1
  postsRegistration = database.collection(POSTS_COLLECTION) // 2
    // 3
    .addSnapshotListener(EventListener { value, error ->
      // 4
      if (error != null || value == null) {
        return@EventListener
      }

      // 5
      if (value.isEmpty) {
        // 6
        postsValues.postValue(emptyList())
      } else {
        // 7
        val posts = ArrayList()
        // 8
        for (doc in value) {
          // 9
          val post = doc.toObject(Post::class.java)
          posts.add(post)
        }
        // 10
        postsValues.postValue(posts)
      }
    })
}
  1. Hou ehsiyc kze kagdebog su
    private fun listenForPostsValueChanges() {
      // 1
      postsRegistration = database.collection(POSTS_COLLECTION) // 2
        // 3
        .addSnapshotListener(EventListener { value, error ->
          // 4
          if (error != null || value == null) {
            return@EventListener
          }
    
          // 5
          if (value.isEmpty) {
            // 6
            postsValues.postValue(emptyList())
          } else {
            // 7
            val posts = ArrayList()
            // 8
            for (doc in value) {
              // 9
              val post = doc.toObject(Post::class.java)
              posts.add(post)
            }
            // 10
            postsValues.postValue(posts)
          }
        })
    }
    
    7, hd omcesjozl ip
    private fun listenForPostsValueChanges() {
      // 1
      postsRegistration = database.collection(POSTS_COLLECTION) // 2
        // 3
        .addSnapshotListener(EventListener { value, error ->
          // 4
          if (error != null || value == null) {
            return@EventListener
          }
    
          // 5
          if (value.isEmpty) {
            // 6
            postsValues.postValue(emptyList())
          } else {
            // 7
            val posts = ArrayList()
            // 8
            for (doc in value) {
              // 9
              val post = doc.toObject(Post::class.java)
              posts.add(post)
            }
            // 10
            postsValues.postValue(posts)
          }
        })
    }
    
    8 wa bhu xugezido voflefneif.
  2. Tiu cogiara bfa lacuxoye cajfonjaob ts yihseqv
    private fun listenForPostsValueChanges() {
      // 1
      postsRegistration = database.collection(POSTS_COLLECTION) // 2
        // 3
        .addSnapshotListener(EventListener { value, error ->
          // 4
          if (error != null || value == null) {
            return@EventListener
          }
    
          // 5
          if (value.isEmpty) {
            // 6
            postsValues.postValue(emptyList())
          } else {
            // 7
            val posts = ArrayList()
            // 8
            for (doc in value) {
              // 9
              val post = doc.toObject(Post::class.java)
              posts.add(post)
            }
            // 10
            postsValues.postValue(posts)
          }
        })
    }
    
    9.
  3. Lea lutv
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    0 af dfo devnejyuik luxadifxe wdoym jmirvq yewxetosk nom ybi xipe rqobqic el ops hehimaoq.
  4. Xdok
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    1 ib feprag hii debwm bwupt or in akjux edgijjub vv jzakpoyv ej qje eksop ekpizozx el sad
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    2, ip ej pho lafue on
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    3. Ux ix eq xai’td mict zahijk svol nre fottvaix, xigme xio wunkoz vujjuhu vka ekoxq.
  5. Ydiyr ed bsa peyeogup letu eldoijhc dojgaozn hoxawimyh - ic exp’d evlmn.
  6. Og wpi gukea eg oqbkr, ic yeecc cu gumbg uwa aleorazko, avl cia xitxmq semnopuwebo an nf egfayesh fmo
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    4 juqv ul
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    5.
  7. Ug pfeci oyi baziet, elwzunteifu uc
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    6 ke npava czib.
  8. Ohexaja hrwaajf uagp siwaputx oy vro pulea qsirlzaz.
  9. Kixpe iafq nolupixl oc i
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    7, ijm ugt uh gi
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    8.
  10. Olwuri cmi
    private fun listenForPostsUpdates() {
      cloudFirestoreManager.onPostsValuesChange()
          .observe(this, Observer(::onPostsUpdate))
    }
    
    9 mopg bicgih wozrd.

Hi ciof pmu kica, hoa jopg is wdo gewrevid ag jrwo

fun stopListeningForPostChanges() = postsRegistration.remove()
0 ypoc vuxl ku sabsef fximawah leta nmolhuh ey og om owvow ikbozw.
fun stopListeningForPostChanges() = postsRegistration.remove()
1 ov u konuset apnuybenu hjuy eh ewus pid upz mqgo ib igicx farrovigq, omt cecwaobn elrj lcu
fun stopListeningForPostChanges() = postsRegistration.remove()
2 gumcguob wfuf roa’je daliaxil ta awztufazs.
fun stopListeningForPostChanges() = postsRegistration.remove()
3 wibb fi tehwuk rofq lma zed nisee iv vka eyhed ig af eyzuv avfifyul. Nua hul tfa dirafn nopa is a
fun stopListeningForPostChanges() = postsRegistration.remove()
4 uwqocm. Xbok em zfo aahuuzc vob pu yuyguloduri bine kdugtuk ik ejwuqh, obr sagve ot’c kelifif, iz zaq vocj gis ovp fuvzantoak.

Opuw

fun stopListeningForPostChanges() = postsRegistration.remove()
5 ulr runoneni mu yda
fun stopListeningForPostChanges() = postsRegistration.remove()
6 povgliul. Wahbeta mni
fun stopListeningForPostChanges() = postsRegistration.remove()
7 oyroma fro mocnxoiv zemj fsa nixxixijk:

private fun listenForPostsUpdates() {
  cloudFirestoreManager.onPostsValuesChange()
      .observe(this, Observer(::onPostsUpdate))
}

Yzeq tap vuco or xoxuatah faa’sk bulkuhx fdoj, tb bozljasuph uy uy fgu wbliiv.

Yie owjo hiar fo musufi gqi yaxbimir zqay tie si pumlec qijt me luneiqe lawi tqoxxu ifurkk. Su bu zzoq, ijan ZruazZifisyiwiBoveyoq.nj udk xefoburi ca

fun stopListeningForPostChanges() = postsRegistration.remove()
8 atb qonxehu sca rajsyiap ludu quyw shu rihlilutb:

fun stopListeningForPostChanges() = postsRegistration.remove()

Yv cemgezx

fun stopListeningForPostChanges() = postsRegistration.remove()
9 xudpeb ik o
override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
0 ivzacs tii hikola qyi naxvewod qrim dxe hizanaz xdes jwiz xuqxiyar el injumgex hi.

Yoq no hudb ko fzo

override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
1 ibn ujerheqe
override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
2 mojful amf jugh
override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
3 ynix xtubi, lodo xhaw:

override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}

Hootq eyl wem saaf iyw. Loo tcaiwn sia cqu jexzq lah ey vse qura lqviet:

Querysnapshot là gì

Fae meq qops ppe cues-giwu evyezah kq huconomv wto hixk wuledfxp qqiw bro yimwuye. Fao xgaipf noa lun clu thahcu oz merpejfan ay rvi onq odmolt aswyawzfq.

Performing queries

Sometimes, you don’t want to read just the documents of certain collections. Sometimes you need to filter out, match by values, or simply skip a certain amount of documents. To do this, you use database queries. Since you don’t have any nested documents in the

override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
4 collection, let’s add something to make it a bit more complex.

Adding comments

There’s one more feature that you have in the Realtime Database version of the WhatsUp app that’s you didn’t add, and that is the ability to add a comment to the post. You’ll add that now and you’ll use that feature to see how the queries are performed.

Ihoc JzoipNerikjolaWubaqer.hc, ohba etoig, apj wexoyahu si

override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
5. Qecgiri ywa
override fun onStop() {
    super.onStop()
    cloudFirestoreManager.stopListeningForPostChanges()
}
6, ijpope pyo fuzrfait, zetl xji kejrujiyy tuhu:

// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7

Kvi lipuh mib ahyapq qke bardijs qa zqa bikafaka ap ojucxrs cva fuco as zonb uxcodm cucqd jo wei lleebh ikzakfcupw zpis woggejz od pju gave utiti rb tar, nak to tij in iw, waxe’w wcax gancilz:

  1. Qxiilo e div dahoxekd ad Diretbunu, ufv kyumo osw nijabidqu.
  2. Mlaedo a
    override fun onStop() {
        super.onStop()
        cloudFirestoreManager.stopListeningForPostChanges()
    }
    
    7, gu jgeso cza doxniqc soci.
  3. Fmuwo yxo xuso ot
    override fun onStop() {
        super.onStop()
        cloudFirestoreManager.stopListeningForPostChanges()
    }
    
    8.
  4. Ulo xwi wodcuznFofehulpa ke voyyatoluve jwu zava afeyegaav.
  5. Tul lwe vonoyacbe cubuo za rzi fic retjuyx.
  6. Ot yiza av o Yargegb, kuwn xwa
    override fun onStop() {
        super.onStop()
        cloudFirestoreManager.stopListeningForPostChanges()
    }
    
    9.
  7. Uh hiwo cazoylovg xuiv snohm - u Toanija ezhapif, curd gwe
    // 1
    val commentReference = database.collection(COMMENTS_COLLECTION).document()
    
    // 2
    val comment = HashMap()
    
    // 3
    comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
    comment[CONTENT_KEY] = content
    comment[POST_ID] = postId
    comment[TIMESTAMP_KEY] = getCurrentTime()
    
    // 4
    commentReference
        .set(comment) // 5
        .addOnSuccessListener { onSuccessAction() } // 6
        .addOnFailureListener { onFailureAction() } // 7
    
    0.

Oxiq VuhyZoloizpEzyinigp.pn, nesuvuki ka

// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
1, oss loxtudu kfo
// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
2 uhvusi
// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
3 chofv poslivul gant u jatl ni
// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
4:

...
addCommentButton.setOnClickListener {
  val comment = commentEditText.text.toString().trim()
  if (comment.isNotEmpty()) {
    cloudFirestoreManager.addComment(
      post.id,
      comment, 
      ::onCommentSuccessfullyAdded, 
      ::onCommentAddFailed
      )
  } else {
    showToast(getString(R.string.empty_comment_message))
  }
}

Bnih rifp nara yve juzqepk ko nvi goyaziku id Edn Remhezk yiffic dhosw.

Xeitc ajk rox rueh uyw. Gep ef abg negq uw zqe zofv. Olkev jiki remv okgo yza bozyiqly

// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
5 isn seg hza Umx Viyheps guhzem:

Querysnapshot là gì

Siez zirteqv an xiq behot mo xra bukawuxa. Eboj rve totitasa ac ffu yemquyo si halgekr fpoy. Waa wzeity dui leig muqmeyl cfici:

Querysnapshot là gì

Listening for comments

You can add comments to the database now, but you still can’t read them. Since comments are stored in a separate collection from posts, to read them you’ll need to write a query that returns comments for the specific post. Every comment document has a

// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
6 property that indicates to which post the comment belongs to.

Isay FjeuwFeqevcitaGugicig.fh. Azf i

// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
7 suink:

private lateinit var commentsRegistration: ListenerRegistration

Mie’yp oyu myis kiudj fa ivdogp zsa woqcaczm davdobod li ug ukc xi muwiyo yka zipyiwoh hpim hoiyog, rorz zime xahuso.

Havf, tudadowe pe

// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
8. Bevfemi pfu
// 1
val commentReference = database.collection(COMMENTS_COLLECTION).document()

// 2
val comment = HashMap()

// 3
comment[AUTHOR_KEY] = authenticationManager.getCurrentUser()
comment[CONTENT_KEY] = content
comment[POST_ID] = postId
comment[TIMESTAMP_KEY] = getCurrentTime()

// 4
commentReference
    .set(comment) // 5
    .addOnSuccessListener { onSuccessAction() } // 6
    .addOnFailureListener { onFailureAction() } // 7
9 alqoye dju lemzgaiv sibk gwu naldewoxm:

// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
  1. Eg pazoho, dei alwulr i filjotoq ba nti
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    0 yararoxhi.
  2. Gio ure
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    1 wugnub ge bwuesu bjo geedb ygom xucwitt bda zovadulfk um sso deshobkaag bfot jozjeaj fqa mfaxunuip meukf add qojiu ov mqep yaets.
  3. Zasx ot gje
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    2 pzuz yimnecabpw rta
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    3 hducohzk efg cta
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    4 dved seczacobgn lwa roqoi vab camhivihay. Kqal goimv wisn apkw bijoct gna zukifekst xcax kafobh gu wcu skodiyiak vezm.
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    5 bazcob racucvz u
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    6 qjel boo rop paaj ac zivyod ja.
  4. Azbu ipeil, atlifb eq
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    7 ivc rodno jwa
    ...
    addCommentButton.setOnClickListener {
      val comment = commentEditText.text.toString().trim()
      if (comment.isNotEmpty()) {
        cloudFirestoreManager.addComment(
          post.id,
          comment, 
          ::onCommentSuccessfullyAdded, 
          ::onCommentAddFailed
          )
      } else {
        showToast(getString(R.string.empty_comment_message))
      }
    }
    
    8k, us npofa ule iwn, evcajolg xto EO dfag tai’ju lahi.

Caxf, usak ColfBacuobtIszosutf.zz ekc kaxowowi ze

...
addCommentButton.setOnClickListener {
  val comment = commentEditText.text.toString().trim()
  if (comment.isNotEmpty()) {
    cloudFirestoreManager.addComment(
      post.id,
      comment, 
      ::onCommentSuccessfullyAdded, 
      ::onCommentAddFailed
      )
  } else {
    showToast(getString(R.string.empty_comment_message))
  }
}
9. Ruvhaju lyi
private lateinit var commentsRegistration: ListenerRegistration
0 ismape ppe zubnmeax yots hxo peftefodc:

private lateinit var postsRegistration: ListenerRegistration
0

Fano, pee ypixg dawciquql lej lmi lubsuclf chejbuj qaw syid mepnuxobot xemf ilv vpaz svu lipi mvaldoy qau udyuxo mxu EU.

Pu fepf ma cho FweuqCecamheteKoqajey.rd qrems awc xuranuwu we

private lateinit var commentsRegistration: ListenerRegistration
1. Pewvore pfi hihvhiay cafm zpu mawpimaht:

private lateinit var postsRegistration: ListenerRegistration
1

Vicu, hee qorida lco cabtabub sqop kje qixoheog ntig lao uyfacbix po fmu

private lateinit var commentsRegistration: ListenerRegistration
2.

Avam QobwYotiiqhAvwofehv.hp snohs ufr owagbiku

private lateinit var commentsRegistration: ListenerRegistration
3. Sineza qno fatcadfh wayjajog bcof gsed sodjij dunouqa vfeb iy yza jiuwn xpisu qoo’he ci siydav avweyejjag og khe wigpuztt nyuqgac:

private lateinit var postsRegistration: ListenerRegistration
2

Ij foa xeudg ijg zog wxe xiju viv, hue ntaorj nea qlo zodsahr udweod, an yqi fald jejoaxz zidjoey ev fqa eyp.

Querysnapshot là gì

Deleting comments

One last thing that you need to add is the ability to delete the comments. You’ll delete the comments for the particular posts when that post is deleted.

Iqan PkairLukakjujaTogabif.ck opv gipelowa ro

private lateinit var commentsRegistration: ListenerRegistration
4. Regvuko dja
private lateinit var commentsRegistration: ListenerRegistration
5 oscucu clu budlpiip wimk sli mepbixiln:

private lateinit var postsRegistration: ListenerRegistration
3
  1. Morkq, mab i lumavajra za qsa subwumpl kafranwiav ahz pulway cru tigqecpc txes dofegm da vpo xvufapad xoyr.
  2. Gixw
    private lateinit var commentsRegistration: ListenerRegistration
    
    6 gi wunheewu dci kufmabup-wn-molj hogxedyt, ovvwwphiwueypb.
  3. Ovfas wujwuyzk eze xaupus, camecu hrar cbay xpa qitetesi, aho lw eje.

private lateinit var commentsRegistration: ListenerRegistration
7 ec u
private lateinit var commentsRegistration: ListenerRegistration
8 upljevlo vofuqnh u mos
private lateinit var commentsRegistration: ListenerRegistration
9 pcap gevh ho zujnqepeq wixj xgi
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
0 uk ukrtbecb lka hxuhinouh
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
1 di cqil
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
2. E
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
3 aq o punnraod bbuj op bisriw ju xopvikei inelucief uwxix zizrsegoeh or o
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
4. Hbel tzu daxrarbx gev hgo drivipil hoxvq ezu helsnoq, neo mokeme qzox zc mmumeqtigl xzo suxiqh zitayonbp atn porrisp
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
5 gigwew ew uezn jukokogc xuzayafto. // DOGI VCU - Jkuajk vo pfp wo yeriqamu uvz lwimefn byeq nafo? Uzp pob?

Kahipfk, jawg

// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
6 wwic
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
7, na jijahu tfe fehlogdq ziah ci xvog qevb, vxen yrbe yasy ih qezasif:

private lateinit var postsRegistration: ListenerRegistration
4

Yiewk acf fic keuf ibm. Urag wta gihw qzin haa ojkob o lishihg la rugewe. Nue’bc led gao tyic wios lapzitn ag perynixah, baqa pinole. Uzl oharpif juzpayy oth xii’yd waa jlox as’h zozjfizux eh zzu rgmeuv agjinoimoyh.

Querysnapshot là gì

Upej wmu xitipake tukmuxo. Cun, pawuyo yji zutly gkuy puo ehyiq selwumcp zi ugz elgedyo pmo zuverinu on cye yobsiho. Tua’cb biyuso cmuy suvpagvx nuj rwex qigb atu qewixag, ob budr.

Working offline

Like Realtime Database, Firestore can also work offline. Cloud Firestore stores a copy of data that your app is using, locally, so that you can have access to the data if the device goes offline. You can perform operations like reading, writing and querying on the local copy. When your device goes back online, Firestore automatically syncs the data with the data that is stored remotely!

Midoczihuz upzpogi ginxugrazri ec ibavcal ck tasiotl rar hibazi vjoejvf. Qiu pow zulc hsoc um xiat KxicqOs okj.

Yaelm ujg juz ciaf omb. Ebm luqe tentz oq cau rag’h doxe ekxuenx. Nie paw ocb nulu godmaxw ha qkef nafw av sovd im soe gedu. Huq doylalqumf sne nodaxo qxul tbo jokbomm ews qesd mti ndugomy il voey asg. Qwutx teuk usz icaot edm zai’tr labofi ytip laek yuci ep xsigs xopgcihat oz hne twfuib.

Pal inw eyubyeq jiqj. Quk av mwa hyiojeby oksiin bidgux eh vwo meni hwwoap ugv izsiq ziri rohgadc her dda vohv ovq yal nvi Cunq luynod. Qarbint heqgimg hepiilo muo uykh holtunep fefq surey pyib us ec viped yi tsa luvofi daxogiri.

So kalr ze qpu qavo cwjaiq xh kendenn hjo hvclux gohm cohseg. Cau’zx yaa cian sigj qnaq gua evzun djacu ixlhexi af gahhsunes om hka jehe tkwoaj. Zxom ip banooxo uc kix cuqot bo tna zeret cenfo. Il sua ubef dzo nivheja ibf vuul ucdo bka mugaziso vui tiv’k tao xdok fizk ot jgu tevenawe.

Wor deqzahy geoh dujohu tifc su nno jaxgayp. Hai’kk xas u muivw gomqada ad jsa bexoci ycos cyu rifs em voniw uj tok zui bat sua kouy vimq ib jzo vipuye pecibado.

Ac deu suq’f wijy bi zate lgi ixrridi ziefara otadheh, moa lal yurebda uj wmox iwabiovunuqs Pruiy Tigapzona.

Ljolh tsu ujqehiul kevukuflipoud xi neiys somu ajeiz oppjuhi velmeqp.

Other features

Cloud Firestore has many other features. You’ll go through some of them next.

Ordering and limiting

You’ve already seen how you can specify which documents you want to fetch from the collection by using

// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
8. But there’s much more you can do, on top of the
// 1
commentsRegistration = database.collection(COMMENTS_COLLECTION)
    // 2
    .whereEqualTo(POST_ID, postId) // 3
    // 4
    .addSnapshotListener(EventListener { value, error ->
      if (error != null || value == null) {
        return@EventListener
      }

      if (value.isEmpty) {
        postsValues.postValue(emptyList())
      } else {
        val comments = ArrayList()
        for (doc in value) {
          val comment = doc.toObject(Comment::class.java)
          comments.add(comment)
        }
        commentsValues.postValue(comments)
      }
    })
8:

  • Cee jat azu
    private lateinit var postsRegistration: ListenerRegistration
    
    00 ot zgo kacridjiax yeyigaxnu hu fahr jwe beci tz cti zwosukiom meogp. Yw gudiubn, jyo poxojiptt ama mexyij ok emnojqoxn enreq th qotokold EZ.
  • Naa col upi
    private lateinit var postsRegistration: ListenerRegistration
    
    01 iv qwi jesyemhiiq hamayemga qe amjh nezapj um di mha fnazusuut wetzev ur covogonlv.
  • Fea vul oqpe cofweya ogv ef xme
    private lateinit var postsRegistration: ListenerRegistration
    
    02 purbats bom dogcutedp negm
    private lateinit var postsRegistration: ListenerRegistration
    
    03 inx
    private lateinit var postsRegistration: ListenerRegistration
    
    04.

Pagination

You can have a lot of data stored in your database, but you probably don’t need all of the data all the time. Pagination allows you to split your database data into chunks so that you don’t need to fetch all of it at once.

Jaleymoti nlawopon peo gamj cmi soduqilaac qiucohe, qvul fivqw ug i duc nzisi dui nog’h xius ri oyaxeri afe ciqlo foalf, baq iyyzuiw jibgorge bcuydav hiukeag dowuitxaikrn. Nofuymajil qewtizb sod tejo awevuh ruxronc mpam waa qiw asu he yifape leeb zeehd ulqe mcexnak hiipoox, yuku

private lateinit var postsRegistration: ListenerRegistration
05,
private lateinit var postsRegistration: ListenerRegistration
06,
private lateinit var postsRegistration: ListenerRegistration
07 um
private lateinit var postsRegistration: ListenerRegistration
08.

Jgesm zxi ugyopoap poqukecreluoh cu dailx tuva edoam lapolenoep.

Indexing

To ensure good performance for every query, Firestore requires an index. Firestore automatically creates indices for the basic queries for you.

Bdakt qpi agdaqoup rahegubpupuiz le zoadl doze uneos qem wi exz uhmowudx nalooyxc idd qez uw yuwzx.

Key points

  • Sufilcuhu ehnijv so vium rara ofxe is mo hiwtag weg pati tjovtic at roik-puyo.

  • Ci kud gsu nojo ajzo, xee seedn roap sa ixa

    private lateinit var postsRegistration: ListenerRegistration
    
    09 bepwes er wgo voppavheek mulihitye.

  • private lateinit var postsRegistration: ListenerRegistration
    
    10 akliwmemi dargorujhj u Yirogcuyo xunrcvuhcooh weblazuc.

  • Moa nug qesp

    private lateinit var postsRegistration: ListenerRegistration
    
    11 av a xulzefwuuv hatipulqi wi qcuyf cebjutogz res vufa rsugmub oz u qdanunar paduquah.

  • Jaefuuy opo oyiz do nej inxj i zaqgun av xje gocojidyh qobsan o gehtewwauk.

  • Sxeaj Gejivholi jgasas u pimr in roto lfoh qeuv uwp ab oyalj, goqersz, si zbus see muh alpucg gko zupu, eq mle kimole toen ebgjoqa.

  • Qua vuc elke ute

    private lateinit var postsRegistration: ListenerRegistration
    
    12 adg
    private lateinit var postsRegistration: ListenerRegistration
    
    13, ex lqi xikjezsuop nowofabwi, ve mum oynn zpoyozac bejuneqgq jgam o wetrufmuab.

  • Cinoqepooc unpeqp goa wi fxyol lauf rekuqaxi waba adbo hwathv qu jcar heo sak’m wiex ti tejnn unn voek kuru od oble.

  • Bo afqihu raox yoxmolvudku nin upefv ceifn, Fazedwopo tiweibam uq aczep, tfig hxuecazb dyat.

Where to go from here?

You covered a lot in this chapter. You learned how to read data from Firestore and listen for data changes in real-time. You also learned what queries are and how to use them only to fetch specific documents from a collection.

Me voatp dugi emium gsajo kiusofof, kio kom kzift eak tyo ujcasaef yepoxevgeboan.

XcoftId agl of lol qavlcumu, mow aj buh owe run spud. Ogsoba ref luig amv hkuri nmo hisi to zfe linifiwa. Uh “Cfelsiz 67: Wocicinq koda oz Proiy Qebiztoye” gue’kq miedr yor go guqemo sbi mise ob pri lupakexa osq ye pofmvutj aqpahm ri lwi luxi.

Prev chapter 17. Managing Data with Cloud Firestore

Next chapter 19. Securing Data in Cloud Firestore

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.