I created a quiz like app where 10 questions are fetched once. If user got 8 marks out of 10. then I fetch next 10 questions. But startAfter always give the same response.
val questionCollectionRef = db.collection("questionCollection")
///.whereArrayContains("tags", tagName)
.orderBy("questionID", Query.Direction.DESCENDING);
val id = SharedPrefs(this#McqActivity).read(OLD_DOCUMENT_ID, "")
if(id.isNotEmpty()){
//questionCollectionRef.whereLessThan("questionID",id) //also tried for whereGreaterThan
questionCollectionRef.startAfter(id);
Log.v("startAfter","start After : " + id + "" );
}
questionCollectionRef.limit(10).get()
//fixme also orderBy date So user can see latest question first
.addOnSuccessListener { querySnapshot ->
if (querySnapshot.isEmpty()) {
Log.d(TAG, "onSuccess: LIST EMPTY")
} else {
val questionList = querySnapshot.toObjects(QuestionBO::class.java)
questionList.forEach { questionItem ->
resultList.add(ResultBO(questionItem))
}
if (resultList.size > 0) {
refreshQuestionWithData()
}
}
}
.addOnFailureListener { exception ->
exception.printStackTrace()
}
This code is written in Activity.After getting score above than 8 .
I open the same activity again and questionCollectionRef.startAfter called but still same question shown in Activity
When you call startAfter() (or any other query building methods), it returns a new query object. So you need to keep a reference to that object:
var questionCollectionQuery = db.collection("questionCollection")
.orderBy("questionID", Query.Direction.DESCENDING);
val id = SharedPrefs(this#McqActivity).read(OLD_DOCUMENT_ID, "")
if(id.isNotEmpty()){
questionCollectionQuery = questionCollectionQuery.startAfter(id);
Log.v("startAfter","start After : " + id + "" );
}
questionCollectionQuery.limit(10).get()...
I also renamed questionCollectionRef to questionCollectionQuery, since the type after orderBy, startAfter or limit is a query.
Related
i'm developing a quiz app. it is working fine.
but when i change the device date like more than 30 days, it is unable to read data from firestore.
see my code
private fun getDataforDQuiz() {
val docRef = db.collection("MonthlyQuiz")
.document(getQuizNum.toString())
docRef.get()
.addOnSuccessListener { documentSnapshot ->
if (documentSnapshot != null) {
val x = documentSnapshot.toObject<QuizModelClass>()
if (x != null) {
if (x.img != "" && x.img.isNotEmpty()) {
activity?.let {
Glide.with(it)
.load(x.img)
.fitCenter()
.into(imageViewToShow)
}
} else {
imageViewToShow.visibility = View.GONE
}
correctAnswer = x.answer
radio_pirates.text = x.a
radio_ninjas.text = x.b
radio_donny.text = x.c
radio_monny.text = x.d
text_to_show.text = x.text
}
}
}.addOnFailureListener {
Toast.makeText(activity, "failed to load data", Toast.LENGTH_SHORT).show()
}
}
my QuizModelClass
class QuizModelClass {
var a: String = ""
var b: String = ""
var c: String = ""
var d: String = ""
var answer: String = ""
var img: String = ""
var text: String = ""
constructor(
A :String,
B:String,
C:String,
D:String,
Answer:String,
Img:String,
Text:String,
)
{
a=A
b=B
c=C
d=D
answer=Answer
img=Img
text=Text
}
constructor() {}
}
I have three functions for daily, weekly and monthly quizzes. First two are working fine.
my doubt is does firestore impose any limitations on reading data for changing the dates ?
i have also noticed i can't create a new account when i changed the date. and also can not login.
i'm getting the toast message "failed to load data"
my database
MonthlyQuiz (collection)
1(document)-> data
2(document)->data
my data base structure screenshot
getQuizNum.toString() is just a number to reference the document in quiz collection. I save the previous quiz number in shared pref and use it to get next month quiz data.
How do I retrieve all the fields of the current user logged?
I've watched many tutorials and questions, and some of them talk about the whole collection, others about similar topics, but found no info about this.
Thank you
UPDATE
Current Code:
fun getUserName_FireBase(){
if(userID==null){
println("The userID is null")
userID= getUserID()
println("The userId has been assigned and now is: " + userID.toString())
}
println("1")
val db = FirebaseFirestore.getInstance()
println("1a")
val usersRef = db.collection("users")
println("1b")
usersRef.document(userID.toString()).get().addOnCompleteListener { task ->
println("2")
if (task.isSuccessful) {
println("3")
val document = task.result
if(document!!.exists()){
println("4")
userName = document!!.getString("user").toString()
println("user is " + userName.toString())
}else {
println("5")
Log.d("Error", "This document does not exist")
}
}else {
println("6")
task.exception?.message?.let {
Log.d(TAG, it)
}
}
println("7")
}
println("8")
}
Console error
The error is given because later I need to acces to userName var that is supposed to be filled in that function
To be able to get user data, you have to create a reference that points to that document, perform a get() call and attach a listener, as seen in the following lines of code:
val db = FirebaseFirestore.getInstance()
val usersRef = db.collection("users")
usersRef.document("gA4z1AhkQpQ6J47sIMmCGIZRKDK2").get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
val email = document.getString("email")
val pass = document.getString("pass")
val user = document.getString("user")
Log.d(TAG,"$email/$pass/$user")
} else {
Log.d(TAG, "The document doesn't exist.")
}
} else {
task.exception?.message?.let {
Log.d(TAG, it)
}
}
}
The result in the logcat will be:
barrooroor#gmail.com/paport/do3fe4232ef2
If "gA4z1AhkQpQ6J47sIMmCGIZRKDK2" is the ID of the user that comes from the authentication process, then instead of the hard coded ID, you can simply use:
val auth = FirebaseAuth.getInstance()
val uid = auth.currentUser?.uid
usersRef.document(uid).get().addOnCompleteListener {/* ... /*}
// 👆
Besides that, something more important, never store sensitive data as passwords in plain text. Malicious users might take advantage of that. Always use Firebase Authentication for that and secure the database using Firestore Security Rules.
I have a function where I validate a product as you can see in this snippet:
private fun validateProduct() : Boolean{
val newProductName = binding.etNewProductName.text.toString().trim()
val newProductPrice = binding.etNewProductPrice.text.toString().trim()
val newProductCategory = binding.spNewProductCategory.selectedItem.toString()
return when{
TextUtils.isEmpty(newProductName) -> {
showErrorSnackBar(binding.root, "Product name cannot be empty.", true)
false
}
TextUtils.isEmpty(newProductPrice) -> {
showErrorSnackBar(binding.root, "Price cannot be empty.", true)
false
}
//make sure the first element is not a valid category
newProductCategory == binding.spNewProductCategory.getItemAtPosition(0) -> {
showErrorSnackBar(binding.root, "Please select a valid category.", true)
false
}
//check if the new product's name already exists in the Firestore collection.
//if so, return false.
else -> {
true
}
}
}
Edit:
My logic in mind was to iterate over the documents. Check each document if document["name"].toString() == newProductName if so, return false and display an error snackbar.
Is there a way to check a Firestore document if its certain field is equivalent to some value?
Sure, there is. As you already said, yes, you have to iterate over the collection, but not for getting all documents and checking the new product name on the client. You have to do that in a query. Assuming that you have a collection called "products", to check if a specific product name already exists, please use the following lines of code:
val db = FirebaseFirestore.getInstance()
val productsRef = db.collection("products")
Query queryByProductName = productsRef.whereEqualTo("productName", newProductName)
queryByProductName.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!task.result.isEmpty) {
Log.d(TAG, "$newProductName already exists.")
} else {
Log.d(TAG, "$newProductName doesn't exist.")
}
} else {
Log.d(TAG, "Error getting documents: ", task.exception)
}
}
I'm trying to see if this "employee" has a service where the serviceComplete field is false (in other words trying to see if this employee has an active service that is not complete) if a Toast message pops up letting the employee know he cannot accept more services has he has an active one. If not the employee should be able to accept the service.
My problem is no matter what I do this firebase query seems to think there are documents in my DB that do not exist. Every time I go to accept the service it displays the toast. Meaning there is a collection "services" where a document has the field "serviceCompleted" which is equal to "false" but in my DB there is no collection "services" under employees
My database showing no collection "services" exist underneath "employees"
and here is my Kotlin code
fun setButton(serviceID: String, eID: String){
val btnAcceptService = view.findViewById<Button>(R.id.btnAcceptService)
btnAcceptService.setOnClickListener {
val queryEmpServices = database.collection("employees").document(eID).collection("services").whereEqualTo("serviceComplete", false)
queryEmpServices.get().addOnSuccessListener { documents ->
if (documents != null){
Toast.makeText(applicationContext,"You already have a service active!", Toast.LENGTH_SHORT).show()
}else {
database.collection("services").document(serviceID).update("saccept", true).addOnSuccessListener {
database.collection("services").document(serviceID).get().addOnSuccessListener { document ->
if (document != null) {
val Location = document.get("ulocation").toString()
val serviceType = document.get("serviceType").toString()
val uComment = document.get("ucomment").toString()
val uID = document.get("uid").toString()
if (document.getBoolean("saccept") == true) {
database.collection("users").document(document.get("uid").toString()).collection("services").document(serviceID).update("saccept", true).addOnSuccessListener {
database.collection("employees").document(mAuth.currentUser!!.uid).get().addOnSuccessListener { document ->
if (document != null) {
val calendar = Calendar.getInstance()
val simpleDateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss")
val acceptDate = simpleDateFormat.format(calendar.time)
val eFullName = document.get("ename").toString() + " " + document.get("esurname").toString()
val eCompany = document.get("ecompany").toString()
database.collection("users").document(uID).get().addOnSuccessListener { document ->
val uName = document.get("name").toString()
val uPhonenumber = document.get("phonenumber").toString()
val serviceAccept = EmployeeServiceAccept(acceptDate, serviceID, Location, serviceType, uComment, uName, uPhonenumber, false)
database.collection("employees").document(mAuth.currentUser!!.uid).collection("acceptedservices").document(serviceID).set(serviceAccept)
database.collection("services").document(serviceID).update("acceptedby", eFullName + ", " + eCompany)
database.collection("users").document(uID).collection("services").document(serviceID).update("acceptedby", eFullName + ", " + eCompany)
Toast.makeText(applicationContext, "Service Accepted", Toast.LENGTH_SHORT).show()
}
}
}
}
}
} else {
Toast.makeText(applicationContext, "Failed to accept service", Toast.LENGTH_SHORT).show()
}
When you are using the Task.addOnSuccessListener(OnSuccessListener) method on a Query object, the result that you get can be a success or a failure, never both and never null. It will always be one, or the other.
That being said, you should never check the documents object against nullity because it can never be null. What you should do instead, is to check the "documents" object, which is of type QuerySnapshot to see if isEmpty() or not:
if (!documents.isEmpty){
Toast.makeText(applicationContext,"You already have a service active!", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(applicationContext,"You don't have a service active!", Toast.LENGTH_SHORT).show()
}
Where indeed the Toast message from the "else" part of the statement will be displayed, as there are no documents present in the QuerySnapshot object:
"You don't have a service active!"
I am a beginner in android programming with Kotlin. I'm close on this, but missing something. Google is not showing me just what I need. Hoping someone here can.
I'm using android studio 4.1.1, kotlin and Firebase
I have several records in my test Firebase database.
each of these records has data, but when I use println in my code one part is showing null and it is not hitting another println at all.
my code - in part
WeightExercise.kt:
...
// i write records to the db like this:
val ref = FirebaseDatabase.getInstance().getReference("weights")
val recordID = ref.push().key
val userId = Global.userID
if (recordID != null){
ref.child(userId).child(recordID).setValue(weight).addOnCompleteListener{
Toast.makeText(applicationContext,"Weight Record rated successfully",Toast.LENGTH_LONG).show()
}
}
...
DisplayWeightsActivity.kt:
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val ref = FirebaseDatabase.getInstance().getReference("weights")
val uidRef = ref.child("weights").child(uid)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
println("$$$$$$ dataSnapshot: ${dataSnapshot.toString()}")
for (ds in dataSnapshot.children) {
println("#$#$#$#$#$ in override dataSnapshot.children")
val muscle = ds.child("muscleGroup").getValue(String::class.java)
val exercise = ds.child("exerciseText").getValue(String::class.java)
val date = ds.child("date").getValue(String::class.java)
Log.d("TAG", date + " " + muscle + " " + exercise)
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("DB ERROR", databaseError.message) //Don't ignore errors!
}
uidRef.addListenerForSingleValueEvent(valueEventListener)
}
In my Logcat window I see this:
$$$$$$ dataSnapshot: DataSnapshot { key = oB3pzvhAr6foyhGZUuvFl4CECOw2, value = null }
But the println("#$#$#$#$#$ in override dataSnapshot.children") does not print.
The key above oB3pzv... matches my userid, so it sees that. I'm not sure what value = null means. Is it not seeing the children records? How do I get the children records under weights >> userId ?