Removing first value from Firebase reference? - java

I'm looking for something like this:
final DatabaseReference ref = FirebaseDatabase.getInstance().getReferenceFromUrl("https://myproj.firebaseio.com/groceries");
ref.limitToFirst(1).removeValue();
Unfortunately, ref.limitToFirst(1).removeValue(); doesn't exist, only ref.removeValue() does. I want to remove only the first value! How can I do that? I'm sure it's easy & straightforward.

You can only all removeValue() on a DatabaseReference. Calling limitToFirst(1) returns a Query, which does not implement removeValue().
Calling getRef() on that query also won't work, since Query.getRef() returns the location where you executed the query. In other words limitToFirst(1).getRef().removeValue() will remove the entire location, not just the first child.
To delete a specific child node, you will first need to get a DatabaseReference to that child node. And this requires that you execute the query, because only then will you know what child nodes match the query:
DatabaseReference ref = FirebaseDatabase.getInstance().getReferenceFromUrl("https://myproj.firebaseio.com/groceries");
Query query = ref.limitToFirst(1);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
childSnapshot.getRef().removeValue();
}
}
The loop over getChildren() is needed since a query can potentially match multiple child nodes. While your query will match at most one child node, in general there can be any number of results, so the code handles that.

Related

Retrieve firebase data within a range of 1 week

I want to retrieve Firebase data within a range of say 1 week. I can query and get data for a day like today, but how about for a range of say 1 week? This is the code that am currenly using for retrieving data for a given day
String mDate = DateFormat.getDateInstance().format(new Date());
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("expenses").child(onlineUserId);
Query query = reference.orderByChild("date").equalTo(mDate);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
myDataList.clear();
for (DataSnapshot snapshot :dataSnapshot.getChildren()){
Data data = snapshot.getValue(Data.class);
myDataList.add(data);
}
todayItemsAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
I cannot find a way of retrieving data for a given range. Someone please help.
The Realtime Database is not designed for complex SQL-like queries.
Not sure if your question refers to grouping results by week. If all you need is a set of results that start and on a certain date and end on another date, you can use startAt(...) and endAt(...) like described in this answer or the spec.
If you need anything more complex than that, you need to
either take all results and filter them in your front end/app code
or use a Cloud Function to do the filtering on the server and
passing the results to the front end.
(Not recommended) You can record the week number (i.e. 45_2020) as a separate field in the document, and filter by that. It's messy and you would have to trust that the front end enters correct info in the field.

Firebase Query startAt()

I am using a query and having some trouble. I want retrieve value from 5th to 10th. I try to use startAt () the query just doesn't get anything.
public void read(){
Query query = FirebaseDatabase.getInstance().getReference("values").orderByChild("timestamp").startAt(5).endAt(10);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot data: dataSnapshot.getChildren()){
Value upload = data.getValue(Value.class);
mUploads.add(upload);
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, "Loading Quote failed", Toast.LENGTH_SHORT).show();
}
});
}
Tl;DR: Firebase database queries are cursor based, and not offset based.
When you call startAt() you have to pass in the value of the timestamp you want to start returning results at. You can't pass in offsets, as you are trying to do now.
You current code:
FirebaseDatabase.getInstance()
.getReference("values")
.orderByChild("timestamp")
.startAt(5)
.endAt(10);
Tells the database to:
Find the node called values under the root.
Order each child node by its timestamp value.
Then find a child node with value 5 and start returning results from there.
Stop returning results once it finds a child node with value 10.
As you can probably see, this won't give you any results, since none of your child nodes have a timestamp value between 5 and 10.
As said: Firebase queries are cursor based, meaning they work on knowing something about the node to start at. While many databases paginate based on offsets, Firebase doesn't and you'll need to know the timestamp value of the node to start at.
For example, if you want to start at the node you have opened in your screenshot, and then return the next 5 results, you'd do:
FirebaseDatabase.getInstance()
.getReference("values")
.orderByChild("timestamp")
.startAt(-1590413110563)
.limitToFirst(5);

Find max value from last 50 nodes from Firebase database

I want to find max value from only last 50 nodes. I used to orderByChild and limitToLast methods but it finds max from all nodes, not only from last 50.
Here my db:
And my code:
database = FirebaseDatabase.getInstance();
reference = database.getReference("sensor");
Query query = reference.orderByChild("hum").limitToLast(50);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot myDataSnapshot : dataSnapshot.getChildren())
{
humTemp = myDataSnapshot.child("hum").getValue().toString();
humMax.setText(humTemp);
}
}
public void onCancelled(DatabaseError firebaseError) {
}
});
So, as you see i try to find max value of hum variable from last 50 nodes.
P.S. New nodes are constantly appearing in the database, so the maximum value should change for every 50 values in realtime
If you want the maximum of the 50 most recent nodes, you're trying to order by two properties:
humidity
the time of the node (or its key)
Firebase Database queries can only order/filter on a single property.
In many cases it is possible to combine the values you want to filter on into a single (synthetic) property. For an example of this and other approaches, see my answer here: http://stackoverflow.com/questions/26700924/query-based-on-multiple-where-clauses-in-firebase. But I don't think that's possible for you.
The best I can think of is to just listen for the last 50 nodes, and then re-ordering them on humidity in the client.

How can I create a unique key and use it to send data in Firebase?

I'm using Firebase in my project and I was trying to create a unique key using Firebase. Using this key I wanna send posts when user start the activity. Like this:
"Posts"
|
-> "Parent Unique key"
|
-> "child unique key 1"
-> "child unique key 2"
-> "child unique key 3"
...
I wanna create Parent Unique key and by using it I wanna send posts. I know how to create unique key in firebase using push() but the problem is when user restart the activity, a new unique key will generate. I don't wanna create the same parent key again after creating once and I can't use user_id here as multiple users have different ids. Also I don't wanna store the parent key in some storage medium. Is it possible to create such key in firebase?
If you do this inside onCreate() method:
DatabaseReference ref=FirebaseDatabase.getInstance().getReference().child("Posts").push();
then yes, everytime you enter that activity a new push id will be created. This is because push() generates a random id.
To be able to solve this, you need to add push() later on.. or under a condition but not at the beginning.
So you can add it onClick of a button:
ref.push().child("name").setValue(name);
This way everytime you click a button it will generate a push id, not when you restart the activity.
To solve this, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
String uniqueKey = rootRef.child("Posts").push().getKey();
DatabaseReference uniqueKeyRef = rootRef.child("Posts").child(uniqueKey);
So for adding data, use only uniqueKeyRef reference. So, using this code you'll create a unique id only once. You'll be able to add those childs under a single id.
If you want another key, see UUID which can help you generate unique keys for your Firebase database without having to use the push() method.
You can create a unique key in Firebase database using push() and then add child nodes under that key. Now next time when you come to that activity, first check if parent node exists or not. If the node exists, save parent node key and use it to save new child nodes.
String strParentKey;
DatabaseReference mDatabaseReference = FirebaseDatabase.getInstance().getReference("Posts");
mDatabaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot != null && dataSnapshot.exists()) {
// parent node exists, get parent node from datasnapshot
strParentKey = dataSnapshot.value().toString();
} else {
// parent node doesn't exists, generate parent node using push
strParentKey = FirebaseDatabase.getInstance().getReference().child("Posts").push().getKey();
}
}
#Override
public void onCancelled(final DatabaseError pDatabaseError) {
pOnChildCheckListener.onChildDoesntExist();
}
});
Now using the parentKey, you can add child node using that reference.
String childKey = FirebaseDatabase.getInstance().getReference("Posts").child(strParentKey).push().getKey();
Hope this will help you lead to the solution.

Firebase onDataChanged called a lot of times (without updates) and sometimes with null values

I have the following json tree in my firebase app
--ashWoYViS3SbHtBhLpvStRleBl13
----items
-------- -KxDDW1FYMUOxea5w5ii
------------- description: "gggh"
------------- name: "gggh"
ashWoYViS3SbHtBhLpvStRleBl13 is the userId, obtaineid with FirebaseAuth.getInstance().getUser().getUid();
And -KxDDW1FYMUOxea5w5ii is the key for an item, obtained with the push function before inserting the item in the json tree.
There are more items under the items node, this is only a sample.
Then I want to read all the items to show them in a list in my app. I do the following:
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Item> items= utils.convertChildrenToList(dataSnapshot, Item.class);
getView().setData(customContexts);
getView().showContent();
}
#Override
public void onCancelled(DatabaseError databaseError) {
getView().showError(databaseError.toException(), true);
}
};
Query query = database.getReference("/ashWoYViS3SbHtBhLpvStRleBl13/items")
.orderByKey();
query.addValueEventListener(listener);
At this moment there are 5 items in the list, and after setting the listener with addValueEventListener, I expect to receive the result in onDataChanged only once. What really happens is that I receive infinite calls to onDataChanged every few seconds. The first time the snapshot has the 5 items. The second time the snapshot is empty (null). The third time the snapshot has again the 5 items, and so on, in an infinite loop.
The database is not being updated, because of that I don't understand why I'm becoming more than one callback in onDataChanged. The data is always there, and I don't understand also why the snapshot's value is sometimes null
If I use addListenerForSingleValueEvent instead, then I receive only one result in the callback "onDataChanged". Sometimes the snapshot value is null, sometimes the snapshot has the 5 elements. In any case, it does not solve my problem.
I tried with many versions of the Firebase sdk, from v11.0.2 to firebase 11.4.2, and it happens in all the versions.
The only way to solve the problem I found, is the following.
Instead of registering the listener for the path
"/ashWoYViS3SbHtBhLpvStRleBl13/items", I register a listener for the path
"/some_prefix/ashWoYViS3SbHtBhLpvStRleBl13/items" (and obviously, I save the data using the same prefix too).
Then all works as expected, that is, I receive only on result in the callback "onDataChanged". The snapshot is not null, and I receive the next callbacks only when the data under the items node is really changed.
Or if I use addListenerForSingleValueEvent, then I receive only one callback with all the elements in the node "items".
May you say me what I'm doing wrong here? (because I don't want to use the prefix before the user id).
Thanks in advance.
Every time you want to make a query make the fetched false to make sure that onDatachange read the method but when the method inside the onDataChange want to call onDataChange with accident can't have access because your fetched is true then.
For exemple when you make insertion in onDataChange the method will call them self other time without you calling them.
Boolean fetched = false;
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (fetched == false) {
fetched = true;
List<Item> items = utils.convertChildrenToList(dataSnapshot, Item.class);
getView().setData(customContexts);
getView().showContent();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
getView().showError(databaseError.toException(), true);
}
};
Query query = database.getReference("/ashWoYViS3SbHtBhLpvStRleBl13/items")
.orderByKey();
fetched=false;
query.addValueEventListener(listener);

Categories

Resources