Read data from Firestore Jetpack Compose - android

I am currently trying to read data from Cloud Firestore, but while doing this I get this error message:
For-loop range must have an 'iterator()' method
And I don't know what to do to get rid of it. Maybe there is an easy way to fix this I haven't thought of yet...
I have been using this google firebase tutorial but with no success.
The error message comes on the documents in the for (document in documents) part of the code
The code I am using is this:
`
fun Varer() {
var firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
var docRef = firestore.collection("varer").document("varer")
var source = Source.DEFAULT
docRef.get(source).addOnSuccessListener { documents ->
for (document in documents) {
Log.d(TAG, "${document.id} => ${document.data}")
var v1 = VareFB(
tittel = document["tittel"].toString(),
pris = document.getDouble("pris"),
beskrivelse = document["beskrivelse"].toString(),
bildeID = document["bildeID"].toString(),
)
varerListe.add(v1)
Log.d(TAG, document["tittel"].toString())
Log.d(TAG, v1.toString())
}
}
.addOnFailureListener { exception ->
Log.w(TAG, "Feil med henting av varer: ", exception)
}
}
`
data class VareFB (
val tittel: String,
val pris: Double?,
val beskrivelse: String,
val bildeID: String,
) {
#Exclude
fun toMap(): Map<String, Any?> {
return mapOf(
"tittel" to tittel,
"pris" to pris,
"beskrivelse" to beskrivelse,
"bildeID" to bildeID,
)
}
}
`
object VarerObject {
var varerListe = mutableListOf<VareFB>()
}
`
Edit:
fun Varer() {
var firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
var docRef = firestore.collection("varer").document("varer")
var source = Source.DEFAULT
docRef.get(source).addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
Log.d(TAG, "${document.id} => ${document.data}")
var v1 = VareFB(
tittel = document["tittel"].toString(),
pris = document.getDouble("pris"),
beskrivelse = document["beskrivelse"].toString(),
bildeID = document["bildeID"].toString(),
)
varerListe.add(v1)
Log.d(TAG, document["tittel"].toString())
Log.d(TAG, v1.toString())
}
}
.addOnFailureListener { exception ->
Log.w(TAG, "Feil med henting av varer: ", exception)
}
}

documents is a QuerySnapshot object so there is no way you can iterate over it. To be able to iterate through the documents, you have to get the documents out of the QuerySnapshot object like this:
firestore.collection("varer").get(source).addOnSuccessListener { documents ->
for (document in documents.documents) {
//
}
}
But in my opinion, it's a little confusing. So I would rather name the object that comes from the lambda expression snapshot:
// 👇
firestore.collection("varer").get(source).addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
// 👆
}
}

Related

How to read and delete a document in firestore collection as one operation in kotlin

I have a firestore collection and I would like to read and delete a document as one operation in kotlin. I want to make sure no other user is able to read the same document before I delete it.
Here is my code in kotlin. CollRef is reference to the firestore collection. MY Collection contains simple documents with fields only.
I found online to use lock mechanism or firebase security rule but I don't know how to implement them.
private val db: FirebaseFirestore=FirebaseFirestore.getInstance()
private val collRef: CollectionReference = db.collection("Queue")
runBlocking {
val querySnapshot = collRef.limit(1).get()
.addOnSuccessListener { querySnapshot ->
if (!querySnapshot.isEmpty) {
val documentSnapshot = querySnapshot.documents[0]
if (documentSnapshot.data?.isNotEmpty()!!) {
db.runTransaction { transaction ->
val snapshot = transaction.get(documentSnapshot.reference)
if (snapshot.data?.isNotEmpty() == true) {
transaction.delete(documentSnapshot.reference)
}
// Success
null
}.addOnSuccessListener {
Utils.data["from"] = documentSnapshot.data!!["from"]
Utils.data["to"] = documentSnapshot.data!!["to"]
Utils.data["sender"] = documentSnapshot.data!!["sender"]
Utils.data["time"] = documentSnapshot.data!!["time"]
}
.addOnFailureListener { e ->
}
}
}
}
.addOnFailureListener {
}
}

Why can't I access my data from Firestore?

I'm trying to display data from Firestore and add it to a PieChart.
I can't figure out why I can't access my data
This is how data are stored in Firestore:
This is how I try to access it:
private val mFirestore = FirebaseFirestore.getInstance()
var chartdata: ArrayList<Measurements> = ArrayList()
private var chart: ScatterChart? = null
fun getCurrentUserID(): String {
val currentUser = FirebaseAuth.getInstance().currentUser
var currentUserID = ""
if (currentUser != null) {
currentUserID = currentUser.uid
}
return currentUserID
}
mFirestore.collection(Constants.MEASUREMENTS)
.whereEqualTo(Constants.USER_ID, getCurrentUserID())
.get()
.addOnSuccessListener { queryDocumentSnapshots ->
val userdata : ArrayList<Measurements> = ArrayList()
val weekdata = ArrayList<Measurements>()
if (!queryDocumentSnapshots.isEmpty) {
for (journals in queryDocumentSnapshots) {
val displayData: Measurements = journals.toObject(Measurements::class.java)
userdata.add(displayData)
Log.e("Data for chart", journals.toString())
}
And I get this error:
enter image description here
The data is being fetched precisely that's why you can see all the document names in the logcat but as you are logging DocumentSnapshot object, that's why you are seeing the data in unusual format. Try logging displayData variables like:
Log.d("Data for chart", displayData.activity) // Use Log.d instead of Log.e
or userdata as an array and it will work as desired.

FireBase multiple queries by document and collection

I am struggling with firebase to run one query to take the truckDocumentId and after that to run another query to take the routesByDateDocumentIdand at the end I am using both document ids to run the function "sendGpsPosition", my problem is that the first query finds truckDocumentId but sometimes the second query does not execute and that is why the applications stops. The code below is for Kotlin.
If I am on Debug then most of the time works.. if I switch off the debug it almost shows the error below =>
And because the query does not execute I got this error: java.lang.IllegalArgumentException: Invalid document reference. Document references must have an even number of segments, but trucks has 1
suspend fun getTruckId() {
val trucksReference = firestore.collection("trucks").whereEqualTo("dispatcher", "Miro")
.whereEqualTo("name", "PEUGEOT").get().await()
val document = trucksReference.documents[0]
if (document != null) {
truckDocumentId = document.id
}
}
suspend fun getRouteReferenceId() {
val routesByDate = firestore.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date").get().await()
val documentRoute = routesByDate.documents[0]
if (documentRoute != null) {
routesByDateDocumentId = documentRoute.id
}
}
fun sendGpsPosition(lat: Double, long: Double, imageRef: String? = null) {
runBlocking { getTruckId() } // if I get this DocumentID
runBlocking { getRouteReferenceId() } // this here maybe will be not found or maybe will be found.. the async is not done correct not sure how to do it.
firestore
.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.document(routesByDateDocumentId)
.collection("live_route")
.add(LatLong(Timestamp.now(), lat, long))
}
**I solved it this way.**
private suspend fun getTruckId() {
val trucksReference = firestore.collection("trucks")
.whereEqualTo("dispatcher", "Miro")
.whereEqualTo("name", "VW")
.get()
.await()
val document = trucksReference.documents[0]
if (document != null) {
truckDocumentId = document.id
}
}
private suspend fun getRouteReferenceId() {
val currentTime = Timestamp.now()
val routesByDate = firestore.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.get()
.await() // here will be better to look for data by delivery_day
val documentRoute = routesByDate.documents[0]
if (documentRoute != null) {
routesByDateDocumentId = documentRoute.documents[0].id
}
}
private fun addGpsDataInDatabase(lat: Double, long: Double, imageRef: String? = null) {
firestore
.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.document(routesByDateDocumentId)
.collection("planned_route") //planned_route or live_route depends if we want to show current state of a truck of make a route plan
.add(LatLong(Timestamp.now(), lat, long))
}
fun sendGpsPosition(lat: Double, long: Double, imageRef: String? = null) {
GlobalScope.launch {
val truckDocId = async { getTruckId() }
truckDocId.await()
val routeDocId = async { getRouteReferenceId() }
routeDocId.await()
addGpsDataInDatabase(lat, long, imageRef)
}
}

How to first step to get firestore's value finished then second step to let gotten value sent to 2nd snippet code?

Because Firestore's .addSnapshotListener is async. How could I first step to get imgsGroupIds from firestore then second step to send imgsGroupIds into trackImageViewModel.getUserTrackedImgs(imgsGroupIds!!)?
In other words, how to let step 1 run finished then run step 2 after step 1 got imgsGroupIds?
runBlocking{
val imgsGroupIds: MutableList<String>? = mutableListOf()
val deferred = async {
Log.d(TAG, "CoroutineScope(Dispatchers.IO): Thread:${Thread.currentThread().name}")
Firebase.firestore
.collection("userData")
.document(uid!!)
.collection("trackGroupId")
.addSnapshotListener { querySnapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
Log.d(TAG, "addSnapshotListener: Thread:${Thread.currentThread().name}")
Log.d(TAG, "onViewCreated: FirebaseFirestoreException: $error")
querySnapshot?.forEach {
val imgsGroupId = it.id
Log.d(TAG, "onViewCreated: imgsGroupId = $imgsGroupId")
imgsGroupIds!!.add(imgsGroupId)
}
}
}
deferred.await()
Log.d(
TAG,
"trackImageViewModel.getUserTrackedImgs: Thread:${Thread.currentThread().name}"
)
Log.d(TAG, "onViewCreated: imgsGroupIds = $imgsGroupIds")
if (imgsGroupIds != null) {
trackImageViewModel.getUserTrackedImgs(imgsGroupIds)
.observe(viewLifecycleOwner, Observer {
tracked_imgs_recycler_view.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(requireContext())
val detailRecyclerAdapter =
DetailRecyclerAdapter(requireContext(), it).apply {
notifyDataSetChanged()
}
adapter = detailRecyclerAdapter
}
})
}
}
You can use coroutines Flow. It's in the core module.
This is the "contacts" documents in Firestore:
Firestore collection/documents
Module class Contact:
class Contact(var name: String) {
constructor(): this("John Doe")
}
by using callbackFlow {} pre-design your Firestore read:
fun getContacts() = callbackFlow<Contact> {
val ref = FirebaseFirestore.getInstance().collection("contacts")
val eventListener = ref.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "getContacts: ", error )
} else {
for (doc in value!!) {
val contact = doc.toObject(Contact::class.java)
this#callbackFlow.sendBlocking(contact)
}
}
}
awaitClose {
eventListener.remove()
}
}
Here is the data that actually read and get:
CoroutineScope(Dispatchers.IO).launch {
getContacts().collect {
Log.d(TAG, "onCreate: ${it.name}")
}
}

how to get data from object _field in firestore

i need to get data from object (named "0") in a document in firestore, is that possible ?
this is my code now:
val db = FirebaseFirestore.getInstance()
val docRef = db.collection("accessories")
.document("brand0")
docRef.get().addOnCompleteListener(OnCompleteListener<DocumentSnapshot> { task ->
if (task.isSuccessful) {
val document = task.result
val group = document.get("0") as ArrayList<String>
}
but casting Any to Arraylist is not possible, any other way to get these data ?
It looks like 0 is a an object type field. That means it'll be represented locally as a Map type object with strings as the keys for the properties it contains.
After some trials and errors this is what worked for me in the end, but I am not sure why. I am using kotlin in my current project.
fun getOwner(userId: String) {
val db = Firebase.firestore
val docRef = db.collection("users").document(userId)
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
Log.d(TAG, "DocumentSnapshot data: ${document.data}")
val data = document.data as Map<String, String>
showOwnerName.text = data["name"]
} else {
Log.d(TAG, "No such document")
}
}
.addOnFailureListener { exception ->
Log.d(TAG, "get failed with ", exception)
}
}
I am converting the data I am getting to a map and this is how I am able to access its values.

Categories

Resources