Android load multiple firestore documents - android

I have a spinner. Into that spinner I added one of my firestore collections. I thought but calling the collection I could load any data into my spinner, but when I added a second document to my collection it stopped working.
This is what I have right now:
db.collection("KitList").get().addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
val data = document.data
val skipRope = data["rope"] as String
spinnerArray.add(skipRope)
val kettle = data["kettle"] as ArrayList<String>
for (item in kettle) {
val kettleWeight = "kettle $item"
spinnerArray.add(kettleWeight)
}
}
}
I tried db.collection("KitList").document("documentname")get().addOnSuccessListener { snapshot -> but it didn't work because my snapshot.documents got an error.
Could anyone help me out here? Just want to know how I can can call multiple documents. Thanks :D

1st, create your custom data model (I will call it DataClass).
Then, in addOnSuccessListener put this code:
snapshot.documents.mapTo(spinnerArray) { it.toObject(DataClass::class.java)}
Of course, replace DataClass with your own :)

Related

This suspend function is taking very long time to execute ,How can I optimize this?

I am writing code to get the newsfeed from the database and to show this feed in UI. This is my Firestore database structure:
users->uniqueUserId |-->UsersProfileInfo--->Profile(document)
|-->FeedNewsFeed |--->unique documents for each newsfeed
|--->unique documents for each newsfeed
|--->unique documents for each newsfeed
to get each news feed from every user in my user collection I have to write nested for loops which takes some extra time and getting data from Firestore also takes some times, so is there any nice way to optimize this problem.
Function for getting newsfeed
suspend fun getAllNewsFeeds(): ArrayList<NewsFeedClass> {
list.clear()
val querySnapshot = collectionRef.get().await()
//Traversing through each document in collection
for (document in querySnapshot) {
val currDocRef = document.reference
//Getting user name
val userName =
currDocRef
.collection(Constants.UserProfileInfo)
.document(Constants.Profile)
.get()
.await()
.toObject(FeedUserName::class.java)?.userName
// adding username in newsFeedClass for displaying on newsfeed
val newsFeedClass = NewsFeedClass()
if (userName != null) {
newsFeedClass.username = userName
}
//getting QuerySnapshot from FeedNewsFeed collection
val newsFeedQuerySnapshot =
currDocRef
.collection(Constants.FeedNewsFeeds).get().await()
//Traversing through each document in
// collecting and respectively adding feed on newsFeedClass
//adding newsFeedClass to list
for (documentOfFeed in newsFeedQuerySnapshot) {
val thisDocRef = documentOfFeed.reference
val feed =
thisDocRef
.get().await().toObject<FeedNewsFeed>()
if (feed != null) {
newsFeedClass.content = feed.newsfeed
list.add(NewsFeedClass(newsFeedClass.username, newsFeedClass.content))
}
}
}
list.shuffle()
return list
}
What you're experiencing in your code is the expected behavior. Why? Because at every iteration of your loop, you are reading data from Firestore using get() and right after that you call await(). This means that all the operations run one after another. It basically means, that at each iteration of your loop, you wait until you get the data from the database. The more iterations you have, the longer it will take. So these operations run sequentially, and not in parallel as you probably might think.
If you need to get the data in parallel you can add the kotlinx-coroutines-play-services library to your project and use the asDeferred extension function that converts a Task into a Deferred object. In the end, you can call the awaitAll() extension function that will wait while all Firestore read operations are loaded in parallel. So in code, it should look like this:
val tasks: MutableList<Deferred<DocumentSnapshot>> = mutableListOf()
for (document in querySnapshot) {
val currDocRef = document.reference
val deferredTask = currDocRef
.collection(Constants.UserProfileInfo)
.document(Constants.Profile)
.get()
.asDeferred()
tasks.add(deferredTask)
}
tasks.awaitAll().forEach { document ->
//Do what you need to do with your documents.
}

How to load array data from Firestore?

I don't know how to load this array field (IllnessHistory), I have searched on google for it, but no one works. Anyone can help me with this? Here is my code and screenshot of my Firestore structure:
override suspend fun loadHistoryGiveDonor(userId: String): ArrayList<GiveDonor> {
val data = ArrayList<GiveDonor>()
giveDonorCollectionReference.whereEqualTo(Constants.USER_ID, userId).get()
.addOnSuccessListener { documents ->
for (document in documents) {
data.add(
GiveDonor(
document.id,
document.data[Constants.USER_ID].toString(),
document.data[Constants.NAME].toString(),
document.data[Constants.WHATSAPP_NUMBER].toString(),
document.data[Constants.DATE_BIRTH].toString(),
document.data[Constants.GENDER].toString(),
document.data[Constants.BLOOD_TYPE].toString(),
document.data[Constants.PROVINCE].toString(),
document.data[Constants.CITY].toString(),
document.data[Constants.DISTRICT].toString(),
document.data[Constants.BODY_WEIGHT].toString(),
document.data[Constants.BODY_HEIGHT].toString(),
document.data[Constants.CURRENT_CONDITION].toString(),
document.data[Constants.LAST_DONOR_DATE].toString(),
document.data[Constants.EVER_COVID].toString(),
document.data[Constants.COVID_STATUS].toString(),
document.data[Constants.RECOVERED_DATE].toString(),
ArrayList(),
document.data[Constants.NOTE].toString(),
)
)
}
Log.d("ResultsizeofGive", "${data.size.toString()} ${documents.toString()}")
}.await()
return data
}
To get the content of the "IllnessHistory" array as a list of GiveDonor objects, then you should create a new class that contains that specific list like this:
data class GiveDonorDoc (
#get:PropertyName("IllnessHistory")
#set:PropertyName("IllnessHistory")
#PropertyName("IllnessHistory")
var illnessHistory: MutableList<GiveDonor>? = null
)
Now to read the data, you can simply use:
val illnessHistory = document.toObject(GiveDonor.class).illnessHistory
I have also written an article on this topic called:
How to map an array of objects from Cloud Firestore to a List of objects?

Get data from Firestore where an array match a list of data in Kotlin

Following is the structure of my Firestore database. I want to get all the documents from the collection 'products' where the 'pin_code' (is an Array) matches with the list of pin codes I have. The list of pin codes is from the collection 'addresses' which I have managed to get with the following code. But I am not able to get documents from the collection 'products' that match the list of pin codes.
Following is the code I have to get the pin code list from the collection 'addresses'
fun getPins(context: DashboardFragment) {
mFireStore.collection("addresses")
.whereEqualTo("user_id", getCurrentUserID())
.get()
.addOnSuccessListener { document ->
val codeList: MutableList<String> = mutableListOf()
for (i in document.documents) {
val code = i.toObject(Address::class.java)
code!!.user_id = i.id
codeList.add(code.pinCode)
}
context.getProductListBasedOnPin(codeList)
}
.addOnFailureListener { e ->
}
}
I tried to get the data from the collection 'products' with the following code. But with this code I can get the list of products only when my 'pin_code' is not an array. But I had to make the pin_code an array for some reason and I am not able to get the product list.
fun getProductListBasedOnPin(pinList: List<String>?) {
val mFireStore = FirebaseFirestore.getInstance()
mFireStore.collection("products")
.whereIn("pin_code", pinList!!)
.get()
.addOnSuccessListener { document ->
for (i in document.documents) {
val product = i.toObject(Product::class.java)!!
product.product_id = i.id
srchProductsList.add(product)
}
srchTempProductsList.addAll(srchProductsList)
if (newView == "ListView") {
successDashboardItemsListListView(srchTempProductsList)
} else {
successDashboardItemsList(srchTempProductsList)
}
}
.addOnFailureListener {
}
}
Can someone help me with this?
Thank you.
I want to get all the documents from the collection 'products' where the 'pin_code' (is an Array) matches with the list of pin codes I have.
You can definitely do that using Query's whereArrayContainsAny(String field, List<? extends Object> values) method, which:
Creates and returns a new Query with the additional filter that documents must contain the specified field, the value must be an array, and that the array must contain at least one value from the provided list.
Assuming that you want to get all documents from the "products" collection in which the "pin_code" arrays contains a List with two values ("123456" and "159874"), please use the following lines of code:
val pinCodeList = listOf("123456", "159874")
productsRef.whereArrayContainsAny("pin_code", pinCodeList).get().addOnCompleteListener {
if (it.isSuccessful) {
for (document in it.result) {
Log.d(TAG, document.id + " => " + document.data)
}
} else {
Log.d(TAG, "Error getting documents: ", task.exception)
}
}
This is done using an Array Contains conditions, using one of the various methods, you can see if an array contains some, any, all, and not-in.
val citiesRef = db.collection("products")
citiesRef.whereIn("pin_code", listOf("123456", "150875"))
Once you fetch this snapshot, it will return an array of matching documents which you can then use with other sections of your app logic and secondary Firestore queries as needed.
Source: https://firebase.google.com/docs/firestore/query-data/queries#kotlin+ktx_6

Firebase Cloud Firestore does not return data or errors

I am learning to use Firebase Firestore and I have created a list of items that I want to display in my app. When trying to load the list, I don't receive the data but I also don't get any error. I cannot display the data in the Log. What could be happening?
fun getDriverData(): LiveData<MutableList<Driver>> {
val mutableData = MutableLiveData<MutableList<Driver>>()
FirebaseFirestore.getInstance().collection("drivers").get().addOnSuccessListener { result ->
val listData = mutableListOf<Driver>()
Log.i("repo","called")
for (document in result) {
val photoUrl = document.getString("photoUrl")!!
val name = document.getString("name")!!
val team = document.getString("team")!!
Log.i("repo", "${document.id}} => ${document.data}")
val driver = Driver(name,team,photoUrl)
listData.add(driver)
}
mutableData.value = listData
}.addOnFailureListener {
Log.i("repo", "getDriverData: ${it.message}")
}
return mutableData
}
Your collection is actually called "drivers" - WITH the quotation marks. Whatever is generating the documents here is using extra quota when building the name of the collection.
You could read them back by adding those quotes:
FirebaseFirestore.getInstance().collection("\"drivers\"").get()
But you probably want to fix the code that generates the document to not add those quotes.

RecyclerView not showing elements

I recently started a new project on android studio. I switched to Kotlin and this language is giving me such a hard time!
I've set up a RecyclerView in my app and it's working fine. My adapter takes an ArrayList as an argument and displays all the data.
I have two functions here that create the ArrayList for my adapter: cardMaker() and getEventsInfo(). These two functions return an ArrayList with Cards elements.
When I call my adapter with an ArrayList created by getEventsInfo then all the events are showing fine.
But when I use cardMaker(), there are no events showing up!
I really can't understand what is going on here and it drives me crazy! ^^
If this language is really sequential by default, how is this behavior possible? I fell like I am missing something important here.
private fun cardMaker(): ArrayList<Card?> {
var newCards: ArrayList<Card?> = arrayListOf()
newCards.add(Card("UserCard", R.mipmap.logo_zenith_round, userData.firstName, userData.lastName))
val infoCards = getEventsInfo()
newCards.addAll(infoCards)
return newCards
}
private fun getEventsInfo(): ArrayList<Card?> {
var infoCards: ArrayList<Card?> = arrayListOf()
db.collection("Events")
.get()
.addOnSuccessListener { result ->
for (document in result) {
val eventsInfo = Card("EventCard",
R.mipmap.logo_zenith_round,
"${document.get("Name")}",
"${document.get("Date")}")
infoCards.add(eventsInfo)
}
}
.addOnFailureListener { exception ->
Log.w(TAG, "Error getting documents.", exception)
}
return infoCards
}
cards = cardMaker()
// RecyclerView
linearLayoutManager =
androidx.recyclerview.widget.LinearLayoutManager(this)
recyclerView = findViewById(R.id.recycler_view)
recyclerView.layoutManager = linearLayoutManager
adapter = RecyclerAdapter(cards)
recyclerView.adapter = adapter
Well, it was not related to Kotlin specificity.
As EpicPandaForce said: "Firebase fetches things asynchronously"
So I made a class instance of the variable cards: ArrayList and added adapter.notifyDataSetChanged() in my getEventsInfo() method inside the OnSuccessListener.
It is now working perfectly.
Thank you!

Categories

Resources