I am wondering if it is possible to apply multiple queries for the Firebase database in Android/Kotlin.
I have a firebase database that has time value in it.
what I need to get is I want to have sales report hourly based. I get this for 1 hour but, when I try it for another hour I don't get the result. I believe I am doing something wrong here logically.
val rootRef = FirebaseDatabase.getInstance().reference
val query1 = rootRef.child("Sales").orderByChild("time").startAt("00:00:00").endAt("00:30:00")
val query2 = rootRef.child("Sales").orderByChild("time").startAt("23:30:00").endAt("00:00:00")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
for(ds in p0.children) {
var timeList = arrayListOf<String>()
val time = ds.child("time").getValue(String::class.java)!!
var totalList = arrayListOf<Double>()
timeList.add(time)
println("time List : "+timeList)
}
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
query2.addListenerForSingleValueEvent(valueEventListener)
query1.addListenerForSingleValueEvent(valueEventListener)
Can anyone help me to understand the logic here?
Thanks.
......
Edited part:
Here it is what i have done: I am now saving date in miliseconds format as you suggested. And, my code looks like this :
`val query1 = rootRef.child("Sales").orderByChild("time").startAt(1577192700000).endAt(1577192701167)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
for(ds in p0.children) {
var timeList = arrayListOf<Long>()
val time = ds.child("time").getValue(Long::class.java)!!
// var totalList = arrayListOf<Double>()
timeList.add(time)
println("time List : "+timeList)
}
}
}`
when I use miliseconds (long value) as startAt() paramater it gives me error.
I get this for 1 hour but, when I try it for another hour I don't get the result.
The problem in your code is that you are passing strings to the startAt() and endAt() methods. Strings are ordered lexicographically, meaning that the following query:
val query1 = rootRef.child("Sales").orderByChild("time").startAt("00:00:00").endAt("00:30:00")
Will work since 00:30:00 is after 00:00:00 but the second query:
val query2 = rootRef.child("Sales").orderByChild("time").startAt("23:30:00").endAt("00:00:00")
Will not return any results. To solve this, you should store the time as a timestamp and not as a String. To achieve this, please see my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
In the end, just pass timestamps (long values) to both startAt() and endAt() methods and your problem will be solved.
Related
The code
This is our code, we just want to display the current user's key, that's it so how should we do that.
Currently, the code gives the output of all the keys and not the current user key.
As output, we are getting the values: Asif, Test24, and Vaishnavi in the variable key
The image is of the Realtime Database The image of Realtime Database
To solve this issue, you need to use a query. In Kotlin, the code should look like this:
val uid = FirebaseAuth.getInstance().currentUser?.uid
val db = FirebaseDatabase.getInstance().reference
val usersRef = db.child("Users")
val queryByUid = usersRef.orderByChild("uid").equalTo(uid)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val key = ds.getKey()
Log.d("TAG", key)
}
}
override fun onCancelled(error: DatabaseError) {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
queryByUid.addListenerForSingleValueEvent(valueEventListener)
If for example,e the authenticated user is Asif, then the result in the logcat will be:
KYGf ... uSP2
Always remember to add all necessary keys when you create a reference. Never use .child(""), which is an empty string.
The app is connected to Firebase and is using the Realtime database on the free spark plan. The app is based in kotlin.
This code properly stores the number of button presses across all devices and saves the number of presses after the application restarts, however I'm having trouble retrieving the number of times the button has been pressed from the database, to be displayed on screen.
//private var database : DatabaseReference = FirebaseDatabase.getInstance().getReference("IncrementButton")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val rootRef = FirebaseDatabase.getInstance().reference
val pressedRef = rootRef.child("pressed")
button_increment.setOnClickListener {
pressedRef.setValue(ServerValue.increment(1));
}
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val pressed = dataSnapshot.getValue(Long::class.java)
Log.d("TAG", "Button has been pressed $pressed times!")
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
pressedRef.addValueEventListener(valueEventListener)
}
Here is what I have in my data class:
data class IncrementButton(val pressed : Int? = null)
Since you say that the button can be clicked by multiple users, there might be a situation in which two or even more users can click the button simultaneously. This means that you should implement a mechanism that can allow you to have consistent data. This can be done using transactions, as explained in my answer from the following post:
How to save users score in firebase and retrieve it in real-time in Android studio
However, since a button click will always increment the counter by one, please see below an even simpler atomic solution:
button.setOnClickListener {
rootRef.child("counter").setValue(ServerValue.increment(1))
}
Edit:
You are getting the following error:
UninitializedPropertyAccessException: lateinit property rootRef has not been initialized
Because by the time you are using the rootRef the is not yet object initialized. So please make sure you call first:
val rootRef = FirebaseDatabase.getInstance().reference
Edit2:
I've seen you have added a similar question here. Maybe I didn't understand that in the first place, but you cannot update a counter atomically using a model Total class. There is no need for that. It's true that you can read the data and update it using a transaction, but for a simple increment, you should only use the above code. That means that you can only take a reference of the field that needs to be incremented and that's it. No need for any model classes. Model classes are used when you want to map an object from the database into an object of the particular class.
Edit3:
To display the newly added counter, please use the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val counterRef = rootRef.child("counter")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val counter = dataSnapshot.getValue(Long::class.java)
Log.d("TAG", "counter: $counter")
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
counterRef.addValueEventListener(valueEventListener)
The output will always be the value of the incremented counter.
Edit4:
This is all you need:
//private lateinit var pressed : IncrementButton
//private var database : DatabaseReference = FirebaseDatabase.getInstance().getReference("IncrementButton")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val rootRef = FirebaseDatabase.getInstance().reference
val pressedRef = rootRef.child("pressed")
button_increment.setOnClickListener {
pressedRef.setValue(ServerValue.increment(1));
}
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val pressed = dataSnapshot.getValue(Long::class.java)
Log.d("TAG", "Button has been pressed $pressed times!")
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
pressedRef.addValueEventListener(valueEventListener)
}
Edit5:
When you remove:
private var database : DatabaseReference = FirebaseDatabase.getInstance().getReference("IncrementButton")
It makes sense to get this error:
"unresolved reference: database"
As the line of code:
database.child("pressed").setValue(ServerValue.increment(1))
Because this line is notcorrect. It should be:
pressedRef.setValue(ServerValue.increment(1))
// ^ ^ See the reference?
So use the exact code I have provided in the Edit4, nothing else. Besides that, your schema should look like this:
Firebase-root
|
--- pressed: 2 <- Not counter, not anything else. Just "pressed"
In this way, you won't get:
"Button has been pressed null times!"
You certainly get:
"Button has been pressed 2 times!"
You can simply use
val counter = 0 //class level variable(global)
button.setOnClickListener {
Log.d(TAG, "button clicked...increasing the counter")
counter++
updateTextOnScreen()
}
fun updateUI() {
textView.text = counter.toString()
}
Everytime the button is clicked by the user, this will print a log statement you can see in logcat. Also, it will update the user interface with the number of times user has clicked the button.
Im trying to pull data from firebase realtime database but i'm not sure how to pull more than one data piece at the same time if its possible.
so this is what the database looks like:
so far i have managed to be able to print out all of these values in the following way:
private fun getData() {
var currentUid = mAuth.currentUser?.uid
val myRef = database.getReference("User-following").child(currentUid!!)
myRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val children = dataSnapshot.value
Log.e("Database", "value = $children")
}
override fun onCancelled(error: DatabaseError) {
Log.e("Database", error.toString())
}
})
}
when it prints the value of children i get all 5 in the following format:
E/Database: value = {-MLwcu81dicGo1NezqJD=1, -MLwcwBjdjRo-vgSkEjR=1, -MLwep1w5z4DfGeabx0d=1, -MLw_sc6aHPxPpGBIpCL=1, -MLwdqVch3iDr3GXylln=1}
how to i return each individual id so that i can use it to retrieve the data that corresponds to each id?
To access the individual child nodes under the snapshot you retrieved, you'll want to loop over its children.
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (childSnapshot in dataSnapshot.children) {
Log.i("Database", "child key = $childSnapshot.key")
}
}
Also see the Firebase documentation on listening to value events.
I'm trying to do the following in my Android application using Kotlin :
I have the Firebase data structure as shown in this image:
The user is able to choose 2 stations from the stations included in the data, either as "start_station", "end_station" or "station_number".. in the next function am trying to take the user's selections and check if both are encluded in the same line.
private fun fetchingLinesData(
theEndStation: String,
theStartStation: String,
) {
val database = FirebaseDatabase.getInstance()
val myRef = database.getReference("line")
myRef.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
toast(getString(R.string.could_not_find_a_way))
}
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach{
//Here should be the line that checks if both data exist
}
}
})
}
Tried using This hasChild Method but it wasn't right.
Try to convert your response to HashMap and search using containsValue
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
//Here should be the line that checks if both data exist
if(it.hasChildren()) {
val stationLines = it.value as HashMap<String, Any>
val validStart = stationLines.containsValue(theStartStation)
val validEnd = stationLines.containsValue(theEndStation)
...
}
}
}
You can do a query:
myRef.orderByChild("end_station").equalTo(theEndStation).addValueEventListener
This will retrieve the data according to the query, then inside onDataChange() after the forEach you can do the following:
if(p0.exists()){
// retrieve data here
}
Using exists() you can check if the datasnapshot exists.
This query will only check for end_station, if you want to check both, then after the query inside the if statement you can retrieve the first_station:
if(p0.exists()){
val firstStation = it.child("start_station").value
And check if firstStation is equal to theStartStation
I have a simple app where user set their DOB and the app calculates the days remaining for next birthday.
I am using firebase to store data and when fetching data I get DOB from database and perform calculation to find days remaining for next birthday. Just for note days remaining is calculated after fetching it is not stored in database.
So what I want is After fetching DOB and calcultaing remaing days I want to show the data in Recycler View in the ascending order of the remaing days.
How can I do that?
Databasemodel.kt
data class Databasemodel(val uid:String, val name:String, val dob:String) {
constructor():this("",",","")
}
Mainactivity.kt
lateinit var bdayList: MutableList<Databasemodel> //I have initialized it later
val ref = FirebaseDatabase.getInstance().getReference("BirthdayPersons")
ref.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
bdayList.clear()
for (iterator in p0.children) {
val bday=iterator.getValue(Databasemodel::class.java)
bdayList.add(bday!!)
}
row_recycle_actual.layoutManager = LinearLayoutManager(applicationContext)
row_recycle_actual.adapter=RecyclerAdapter(bdayList)
}
}
})
After
for (iterator in p0.children) {
val bday=iterator.getValue(Databasemodel::class.java)
bdayList.add(bday!!)
}
you should sort the list by desired predicate, you may do it with sortBy in Kotlin:
//assuming you have "remainingDays" or similiar field in your model
bdayList.sortBy { dbmodel -> dbmodel.remainingDays}
then you may pass it to the RecyclerView
row_recycle_actual.adapter=RecyclerAdapter(bdayList)