My Firebase Database is like this
When the coding below was run:
String loc=(snapshot.child("loc").getvalue()).tostring();
The output I get has different sequence with the database:
Why is that so?
Firebase data is stored as JSON and is inherently unordered.
If you want to access the data in a specific order, you should specify an orderBy clause for your query and access the data using the snapshot's getChildren() method.
Say you want to log the items by ascending key:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getRef();
Query locations = rootRef.orderByKey();
locations.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot locSnapshot: snapshot.getChildren()) {
System.out.println(locSnapshot.getKey() + ": " + locSnapshot.getValue(String.class));
}
}
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
This sample comes (modified) from the Firebase documentation on reading lists of data.
Frank beat me to my edit, check out his correct solution using orderBy....
You need to use forEach rather than the child method (or child.foreach)
Here is a snippet from the doc:
Because of the way JavaScript Objects work, the ordering of data in
the JavaScript Object returned by val() is not guaranteed to match the
ordering on the server nor the ordering of child_added events. That is
where forEach() comes in handy. It guarantees the children of a
DataSnapshot will be iterated in their query-order.
Source: https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach
Related
Basically what I am trying to do is I have a database with the name users having an attribute username. I have some usernames in one list and I want to show details of these users only whose username is present in the list. How can I write a query to fetch details of those users only whose username is found in this list? And note that there is no lexicographical ordering so i can't use startAt() and endAt() functions as well.
code snippet:
=> myList contains usernames. This code doesn't yield accurate results.
Any help would be really appreciated! Thank you!
FirebaseRecyclerOptions<MainModel> options =
new FirebaseRecyclerOptions.Builder<MainModel>()
.setQuery(FirebaseDatabase.getInstance().getReference().child("users").orderByChild("username")
.startAt(myList.get(0)).endAt(myList.get(myList.size()-1)),MainModel.class).build();
As already mentioned in the comment, the Firebase-UI library doesn't help in your case, because it doesn't allow you to pass multiple queries to the FirebaseRecyclerOptions object. So you need to perform a separate query and use the combined result.
When you are calling .get() on a Firebase Realtime Database query object, you are getting back a Task object. So the key to solving this problem is to use whenAllSuccess(Collection> tasks). In your case, it should look like this:
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
DatabaseReference usersRef = db.child("users");
Query query = usersRef.orderByChild("username");
List<Task<DataSnapshot>> tasks = new ArrayList<>();
for (String username : myList) {
tasks.add(query.equalTo(username).get());
}
Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//Do what you need to do with your list.
for (Object object : list) {
MainModel mm = ((DataSnapshot) object).getValue(MainModel.class);
if(mm != null) {
Log.d("TAG", mm.getUsername());
}
}
}
});
Assuming that you have in your MainModel class a getter called getUsername(), the result in your logcat will be all the usernames of all returned children.
how to retrieve data from all documents in the first collection with a clause in the second collection.
I use this code but the data doesn't show up at all.. please help me..
dbf.collection("group").document().collection("member")
.whereEqualto("iduser", "098332")
.orderBy("updatetime", Query.Direction.DESCENDING)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value, #Nullable FirebaseFirestoreException error) {
List<DocumentSnapshot> list = value.getDocuments();
datalist.clear();
for (DocumentSnapshot d : list) {
final Modelfirestore c = d.toObject(Modelfirestore.class);
datalist.add(c);
}
mAdapterss.notifyDataSetChanged();
}
});
I've tried with the script above but it doesn't work
Every time you call document() without an argument, it generates a references to a new, unique, non-existing document in your database. So you're querying a non-existing subcollection, which explains why you don't get any results.
If you want to query the member subcollection of a specific group document, specify the ID of that group document in the call to document(...).
If you want to query across all member subcollections, you can use a collection group query.
If you want to query all member collections under groups, you can use a collection group query with the trick in Sam's answer here: CollectionGroupQuery but limit search to subcollections under a particular document
I have an android app with firebase as the backend. I have an activity where the use populates a list for a recycler view which they want to save to the database.
Below is a screenshot of the Firebase Realtime Database structure of my database:
The values of children in the Values node is what I'm using to create the id for new data that is added.
I have an arraylist of sales objects populated by the user which is to be saved in a new Sales node and would like to use the value in Values/Sales to populate the ids for each item in the arraylist.
Below is my code for saving that data. However in the database, only the last item in the arraylist is saved.
ArrayList<Inventory> salesArrayList = new ArrayList<>();
------------------- Code for populating data into the arraylist ----------------------
for (int i = 0; i < salesArrayList.size(); i++){
int finalI = i;
databaseReference.child("Values").child("Sales").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String newCount = String.valueOf(Integer.parseInt(String.valueOf(snapshot.getValue()))+1); // Gets the original value saved and adds 1 to it to be used as the id in the 'Sales' node
DatabaseReference countReference = databaseReference.child("Sales").child(newCount);
countReference.child("date").setValue(currentDate);
countReference.child("name").setValue(salesArrayList.get(finalI).getName());
countReference.child("quantity").setValue(salesArrayList.get(finalI).getQuantity());
countReference.child("unit_price").setValue(salesArrayList.get(finalI).getValue()/salesArrayList.get(finalI).getQuantity());
countReference.child("value").setValue(salesArrayList.get(finalI).getValue());
databaseReference.child("Values").child("Sales").setValue(Integer.parseInt(newCount)); // Updates the number of sales
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
If there are 7 items in the arraylist, I'd like a way to have all them saved in the Sales similar to the Purchases node rather than only the last item on the arraylist.
This doesn't work (and I'm surprised it even compiles):
databaseReference.child("Values").child("Sales")(Integer.parseInt(newCount)); // Updates the number of sales
To write back the incremented number of sales:
snapshot.getReference().setValue(snapshot.getValue(Long.class)+1);
Note that in general using such sequential numeric keys in Firebase is an anti-pattern. I highly recommend using Firebase's native push() keys, which are also always incrementing but provide much stronger guarantees outside of that. To read more about that, check out: Best Practices: Arrays in Firebase.
I can't comment, but I can answer, but I don't know if my answer will be correct.
If you are only updating the last item on the array it would seem to me that you are at each iteration overwriting the same object and finally saving only that object to the database.
Is there an append method? Or does the documentation say anything about this?
This for me seems the most probable cause.
Maybe you each time need a new instance of the 'databaseReference'-object.
Database snapshot
This is my fire base database (the image attached by the link ). I want to sort all these by the date. As you can see in the image, I've made a date variable. So for a date, "20 May 2018", the date variable has the value "20180520" . Hence, sorting the data by simply the integer value of date will do the job. The code I used for showing these data is,
DatabaseReference mDatabase= FirebaseDatabase.getInstance().getReference();
mDatabase.addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
map = (Map<String, Object>) dataSnapshot.getValue();
itr = map.entrySet().iterator();
entry = itr.next();
Map singleUser = (Map) entry.getValue();
String name=(String) singleUser.get("name");
String d=(String) singleUser.get("sdate");
String m=(String) singleUser.get("smon");
String y=(String) singleUser.get("syear");
String Submi=(String) singleUser.get("subm");
Now, I want this code to be manipulated in such a manner that the data appears in the increasing order of date. How can I sort the firebase ? And if it's not possible, how can I sort the Map(String,Object) by Object.date ?
Please help
You are able to sort the retrieved data from the server. Please take a look at this part of the documentations - Ordering by a specified child key.
In your case it's gonna look something like this:
final DatabaseReference usersRef = database.getReference("users");
usersRef.orderByChild("date").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Your code goes here...
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}});
If you want to order your database items by date you should store the date as a ServerValue.TIMESTAMP as explained here and not as number as I see in your screenshot.
So the ServerValue.TIMESTAMP is just a token that the server understands and translates to a number, which is the current time using its own clock.
If you're trying to store the date as numbers or strings in Realtime Database, don't. That's not a very good solution.
As a newbie in firebase I tried to mimic a kind of "where clause" request to retrieve the user's wallet in this simple use case:
User
48bde8f8-3b66-40bc-b988-566ccc77335c
email: "toto#acme.com"
username: "userTest1"
UserWallet
F4PvtvNT2Z
coins: 26
someList
elemet1
elemet2
user: "48bde8f8-3b66-40bc-b988-566ccc77335c"
First I tried to code my request like this:
Firebase root = new Firebase("https://myApp.firebaseio.com/");
Firebase ref = root.child("UserWallet");
Query query = ref.equalTo("48bde8f8-3b66-40bc-b988-566ccc77335c", "user");
The result was null, So I wrote this query:
Query query = ref.orderByChild("user").equalTo("48bde8f8-3b66-40bc-b988-566ccc77335c", "user");
Result was null again. The only way to retrieve the wallet was with this query:
Query query = ref.orderByChild("user").equalTo("48bde8f8-3b66-40bc-b988-566ccc77335c");
So Should I have to always use en "orderByChild()" query before to use "equalTo()"?
And so, what is the purpose of the query "equalTo(String value, String key)" compare to "equalTo(String value) since only the second one works correctly?
There are some edge cases that don't need an orderBy...(), but in general you'll need an orderBy...() before a filtering operation (equalTo(), startAt(), endAt()).
I highly recommend that you first read the Firebase programming guide for Android (95% of it applies to regular Java too). A few hours spent in that guide, will save dozens of questions here. For example: this is the section on queries.
After reading that, you might also want to read this guide on NoSQL Data Modeling. It covers many common patterns in NoSQL data modeling and will help you realize early on that trying to map SQL queries to a NoSQL database is a logical idea, but seldom a good one.
My initial (without any idea on your use-cases, except for "I need to be able to find the wallets for a user") model:
UserWallet
"48bde8f8-3b66-40bc-b988-566ccc77335c"
"F4PvtvNT2Z"
coins: 26
someList
element1
element2
In the above model, I've inverted the Wallet and User under UserWallet, so that looking up the wallet(s) for a user becomes easier.
ref.child('UserWallet').child(auth.uid).addValueEventListener(...
Note that there is no query involved here, so loading will be equally fast no matter how many users you have in your database.
Or alternatively:
User
"48bde8f8-3b66-40bc-b988-566ccc77335c"
email: "toto#acme.com"
username: "userTest1"
Wallet
"F4PvtvNT2Z"
coins: 26
someList
element1
element2
UserWallet
"48bde8f8-3b66-40bc-b988-566ccc77335c"
"F4PvtvNT2Z"
Now we've complete flattened the structure. To determine the wallets of a user, you go to UserWaller/$uid and then load each wallet from Wallets/$walletid. It may be a bit more code, but it'll be extremely efficient (since there are no queries involved).
You can use nested Query for this.! if you have multiple random id you can easily compare them.!
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference.child("user");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// dataSnapshot is the "issue" node with all children with id 0
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the individual "issues"
Query query = reference.child("user").child(dataSnapshot.getKey().equals(YourData)));
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});