Fetching data from my FireStore DB is unreliable and it worsens after adding a new feature that uses the first date fetch to do some more data fetchs
I've my user songs (which are document ids to match the data)
To
//To get user data
suspend fun getAllUserPlaylists():List<Playlists>
= try{ FS_USER.get().await().toObjects(Playlists::class.java) }
catch (e:Exception){ emptyList() /*getAllUserPlaylists()*/ }
//Function to call above funcion
val allUserPlaylist = GlobalScope.launch { vm.allUserPlaylists. postValue(MusicDB(). getAllUserPlaylists()) }
runBlocking {
//wait for data fetch to finish
allUserPlaylist.join()
Log.i("WORKS","${vm.allUserPlaylists.value}")
//Match ids with song data
vm.allUserPlaylists.value?.forEach {
val q = arrayListOf<Song>()
it.songsIDs.forEach { it2 ->
val t = FS_ALL_SONGS.document(it2).get().await().toObject(Song::class.java)
if (t != null) {
q.add(t)
}
}
//Insert
val vmT = vm.allUserPlaylists.value!!
vmT[vmT.indexOf(it)].songs = q
vm.allUserPlaylists.postValue(vmT)
}
}
data class Song(val mediaID: String = "", val title: String = "",
val artist: String = "", val songURL: String = "",
val imgURL: String = "", val album: String = "")
data class Playlists(val name:String= "", val mainArtists: String = "",
var songs:ArrayList<Song> = arrayListOf(),
val songsIDs:ArrayList<String> = arrayListOf(),
val imgURL:String = "",var pinned:Boolean = false)
What i dont get why is it so unreliable.In theory it should work but only does (10% of the time) after clearing all app data and cache.I simpliy add documents to a list with using their doc ids and display it using a recycler view that updates on value change.
So any sort of help is appreicated
Edited : My code doesn't provide any visible errors (to me) however even though getAllUsersPlaylists function returns valid results(doc IDs) which I've confirmed by logging.
Code snippet to match doc IDs with song data doesn't give expected results(also checked by logging).It doesn't match the Doc Ids with data so doesn't return song data that I've referred using its doc IDs.
And this data is later shown to the user using a recycler view which auto updates ui
Song IDs refer to the doc IDs for song data
Related
private fun getData(){
firestore.collection("Posts").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_SHORT).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val pc = document.get("Panel Sayisi") as String
val ps = document.get("Panel Boyutu") as String
val ls = document.get("Arazi Eğimi") as String
val lsi = document.get("Arazi Boyutu") as String
val c = document.get("Şehir") as String
val post = Post(pc,ps,ls,lsi,c)
postArrayList.add(post)
}
}
}
}
val db = FirebaseFirestore.getInstance()
db.collection("Posts").orderBy("Panel Sayisi",Query.Direction.DESCENDING).limit(1)
.get()
.addOnCompleteListener {
val result : StringBuffer = StringBuffer()
if (it.isSuccessful){
for (document in it.result){
result.append(document.data.getValue("Panel Sayisi"))
}
verimText.setText(result)
}
}
}
}
It shows all the data I have added to Cloud Firestore that has the keyword "Panel Sayisi" in verimText. I only want the last data entered to be displayed.
Here is the problem. The last file I added is added in the middle. That's why the value I call last or the first call doesn't change. So it always calls the same value. This middle value should be written at the end or at the beginning. So I can get this data by calling ASCENDING or DESCENDING method.
When you are using the following call to:
.orderBy("Panel Sayisi",Query.Direction.DESCENDING)
It means that are only ordering the documents according to the "Panel Sayisi" field, which doesn't actually solve the problem. The problem is that you don't know that the "c4Q5...q4vP" document is the last document that was added. To solve this issue, you have to add an additional field of type Firestore Timestamp, as explained in my answer from the following post:
How to add Firebase Timestamp when adding an object of a data class to the Firestore in Kotlin?
And then use the following query:
val db = FirebaseFirestore.getInstance()
val query = db.collection("Posts").orderBy("timestamp", Query.Direction.DESCENDING).limit(1)
calculateButton.setOnClickListener {
val panelC = binding.panelCount.text.toString()
val panelS = binding.panelSize.text.toString()
val landS = binding.landSlope.text.toString()
val landSi = binding.landSize.text.toString()
val cit = binding.city.text.toString()
val sun = (1000).toInt()
val air = (1.25).toFloat()
val cel = (25).toInt()
val verim = ((sun * air)/ cel).toString().toDouble()
if (panelC.equals("") || panelS.equals("")|| landS.equals("")|| landSi.equals("")||cit.equals("")){
Toast.makeText(requireContext(),"Alanları Doldurunuz.", Toast.LENGTH_SHORT).show()
}
else{
val action = SignUpCalculateFragmentDirections.actionSignUpCalculateFragmentToDataFragment()
Navigation.findNavController(view).navigate(action)
}
val postMap = hashMapOf<String, Any>()
postMap.put("Panel Sayisi",binding.panelCount.text.toString())
postMap.put("Panel Boyutu",binding.panelSize.text.toString())
postMap.put("Arazi Eğimi",binding.landSlope.text.toString())
postMap.put("Arazi Boyutu",binding.landSize.text.toString())
postMap.put("Şehir",binding.city.text.toString())
postMap.put("date", Timestamp.now())
postMap.put("verim",verim.toString().toDouble())
firestore.collection("Posts").add(postMap).addOnFailureListener {
}.addOnFailureListener {
Toast.makeText(requireContext(),it.localizedMessage,Toast.LENGTH_SHORT).show()
}
There is a calculatePage Fragment Codes. On this page, I am trying to make a yield calculation based on the data I receive from the user. However, I need to add the values that are kept constant in the efficiency calculation, such as "sun", "cel", "air" that I defined in the code. I wrote a random operation there as an example. To see if I can write inside the text I'm trying to print without getting any errors. But the app crashed.
private fun getData(){
firestore.collection("Posts").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_SHORT).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val pc = document.get("Panel Sayisi") as String
val ps = document.get("Panel Boyutu") as String
val ls = document.get("Arazi Eğimi") as String
val lsi = document.get("Arazi Boyutu") as String
val c = document.get("Şehir") as String
val v = document.get("verim") as Double
val post = Post(pc,ps,ls,lsi,c,v)
postArrayList.add(post)
}
}
}
}
val db = FirebaseFirestore.getInstance()
db.collection("Posts").orderBy("date",Query.Direction.DESCENDING).limit(1)
.get()
.addOnCompleteListener {
val result : StringBuffer = StringBuffer()
if (it.isSuccessful){
for (document in it.result){
result.append(document.data.getValue("verim"))
}
verimText.setText(result)
}
}
On this page, I added the values I defined in my class named 'post' to the postList and added them to Firestore.
data class Post(val pc: String, val ps: String, val ls: String, val lsi: String, val c: String, val v : Double)
#ServerTimestamp
var date: Date? = null
This is my post class
The error is like this: java.lang.NullPointerException: null cannot be cast to non-null
type kotlin.Double
As I explained at the beginning of my question, what I am trying to do is using both the data such as "panelCount", "panelSize" that I get from the user and the "sun", "cel", "air" values that are defined as constants, using the "verimText.setText(result)" in the DataFragment page. I need to show this calculation to the user.
The user enters values such as 'Panel Sayisi', 'Panel Boyutu' that should be used while calculating on the calculation page. I need to show this to the user in verimText using both this data and the 'cel', 'sun', 'air' constants that I wrote in the first code.
PS: verim: 20000 value is the result of hypothetical values that I wrote in the first code. In this part, I need to make a calculation using the other data entered by the user and these constant values and show it in the verimText.
I have a PagingSource that pages through a firestore collection to return documents.
class ClipPageDataSource(mParams:Bundle, private val mAds:Boolean):PagingSource<QuerySnapshot, Clip>(), ClipDataSource {
var query : Query?= null
private val mFirestore = FirebaseFirestore.getInstance()
private var mBaseQuery = mFirestore.collection(SharedConstants.COLLECTION_CLIPS)
.orderBy("createdAt",Query.Direction.DESCENDING)
private var mLikedQuery = mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_USER_LIKES)
.orderBy("createdAt",Query.Direction.DESCENDING)
private var mSavedQuery = mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_SAVES)
.orderBy("createdAt",Query.Direction.DESCENDING)
val mine = mParams.getBoolean(ClipDataSource.PARAM_MINE)
val liked = mParams.getBoolean(ClipDataSource.PARAM_LIKED)
val saved = mParams.getBoolean(ClipDataSource.PARAM_SAVED)
val user = mParams.getString(ClipDataSource.PARAM_USER)
val first = mParams.getString(ClipDataSource.PARAM_FIRST)
val private = mParams.getBoolean(ClipDataSource.PARAM_PRIVATE)
override fun getRefreshKey(state: PagingState<QuerySnapshot, Clip>): QuerySnapshot? {
return null
}
override suspend fun load(params: LoadParams<QuerySnapshot>): LoadResult<QuerySnapshot, Clip> {
try {
query = when {
liked -> {
mLikedQuery
}
saved -> {
mSavedQuery
}
else -> {
mBaseQuery
}
}
if(mine){
query = query!!.whereEqualTo("createdBy.uid",Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
}else if(user!=null){
query = query!!.whereEqualTo("createdBy.uid",user)
}
query = if (private){
query!!.whereEqualTo("private",true)
}else{
query!!.whereEqualTo("private",false)
}
first?.let {
val item = mFirestore.collection(SharedConstants.COLLECTION_CLIPS).document(it).get().await()
if (item!=null){
query = query!!.startAt(item)
}
Log.d(TAG,"the first item fetched is ${item.data!!["id"]}")
}
query = query!!.limit(15)
val currentPage = params.key ?: query!!.get().await()
if (currentPage.size() < 1)
return LoadResult.Page(emptyList(),null,null)
val lastDocumentSnapshot = currentPage.documents[currentPage.size() - 1]
val nextPage = query!!.startAfter(lastDocumentSnapshot).get().await()
val clips = currentPage.map {
it.toObject(Clip::class.java)
}
return LoadResult.Page(clips,null,nextPage)
}catch (e:Exception){
return LoadResult.Error(e)
}
}
companion object{
private const val TAG = "DataSource"
}
}
So, in the above code, I have three different collections to fetch data from, and the required one is selected based on the parameters passed.
Now, when I fetch data using the mBaseQuery, and passing an id in first parameter, it returns the data correctly.
But, when I fetch data using the mLikedQuery or the mSavedQuery, instead of returning data from the id passed in first parameter, it uses the next item as the first one. Basically, startAt works as startAfter.
I have checked the snapshot fetched using the id passed in first is correct. So, the block in first?.let, works correctly. But, when the final query is executed, it skips the first item passed in startAt and instead starts from the next item in list.
This only happens with mLikedQuery and mSavedQuery and not with mBaseQuery.
Anybody got any idea what's happening here?
The DocumentReference you are providing to startAt is always for a document from the collection SharedConstants.COLLECTION_CLIPS:
val item = mFirestore.collection(SharedConstants.COLLECTION_CLIPS).document(it).get().await()
This works fine for your mBaseQuery because that query is querying the documents in the SharedConstants.COLLECTION_CLIPS collection, however your mLikedQuery and mSavedQuery are querying documents from different collections so providing a DocumentReference from the SharedConstants.COLLECTION_CLIPS collection as the startAt value here doesn't make sense, the query can't start at a document that doesn't exist in the collection you're querying.
Perhaps you need to set the item you provide to startAt based on which query is being used, e.g.:
...
first?.let {
val item = when {
liked -> {
mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_USER_LIKES).document(it).get().await()
}
saved -> {
mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_SAVES).document(it).get().await()
}
else -> {
mFirestore.collection(SharedConstants.COLLECTION_CLIPS).document(it).get().await()
}
}
...
I am using Firebase FireStore to retrive my all collections data
but i want to print Log of each 5 interwal how can i get the current count of the loop?
If any suggestion to change loop please also share your solution with code..
Here is my code
db
.collection("news")
.orderBy("timestamp", Query.Direction.DESCENDING)
.whereLessThan("timestamp",tm)
.limit(10)
.get()
.addOnSuccessListener {
for (doc in it) {
val imgUrl = doc.getString("imageUrl")
val heading = doc.getString("headline")
val timestamp = doc.getTimestamp("timestamp")
val tagline = doc.getString("tagline")
val type = doc.getString("type")
// position%5 but how to get the current position?
}
}
for ((i, doc) in it.withIndex()) {
val imgUrl = doc.getString("imageUrl")
val heading = doc.getString("headline")
val timestamp = doc.getTimestamp("timestamp")
val tagline = doc.getString("tagline")
val type = doc.getString("type")
if(i%5==0) {
//add ur item
}
The QuerySnapshot and its Iterator don't expose any index information as you iterate. You would have to keep track of it yourself:
int i = 0
for (doc in it) {
if (++i % 5 == 0) {
// multiple of 5
}
}
Or you can iterate a List of snapshots the you get from getDocuments(), using its ListIterator, which will expose indexes.
So in my android app I'm fetching all the documents from a collection by the following code :
log("Player Listener Started")
FirebaseFirestore.getInstance().collection("players")
.limit(4)
.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
log("Player Listener firestore exception message ${firebaseFirestoreException?.localizedMessage}")
if (firebaseFirestoreException == null) {
querySnapshot?.documents!!.forEach {
val serverPlayer = it.toObject(Player::class.java)!!
snapshotPlayers.add(serverPlayer)
}
log("Player numbers : ${snapshotPlayers.size}")
I've 4 documents in "players" collection in the database :
But it always return just 1 document i.e. "Sheetal". Please check the log from the above code :
This exact code was working fine an hour ago and I was getting all 4 users.
I cleared database from the console and re-created these document using my app this code is not returning 4 documents.
Player class :
class Player(
var name: String = "",
var gameId: String = "",
var currentCard: Card = Card(),
var cardList: ArrayList<Card> = ArrayList(),
var made: String = "0",
var expected: String = "0",
var message: String = "",
var order: Int = 0,
var turn: Int? = null
)
Player document contents in the database
I tried these things but still getting same result :
Clear my app cache manually
Cleared Firestore persistence data using FireDB.db.clearPersistence()
Its my first project in firestore so I don't know what am I doing wrong. Please help me.
UPDATED LOGCAT: