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) {
}
});
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 am using firestore database that has the following structure:
I have collection named NBDB, inside there are documents with the uid of each user. For example I have 2 users so I have 2 documents called: ZxK2BR..., xy9BHY....
In each user document there is another collection called MyBooks and there are documents for all the books the user search.
Here are pictures from my database:
I want to make like home page screen that will disaply 20 random images from all of the images that users have in the app database.
From my understanding I need to get inside each user document and search for all the my books it has and then to move for the next user.
My finally goal is to read the BookID value of all of the users and to choose 20 random
I used the following to obtain all BookID from specific user:
FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference MyDB = db.collection( "NBDB" ).document(auth.getUid()).collection( "MyBooks" );
MyDB.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<String> list = new ArrayList<>();
for (QueryDocumentSnapshot document : task.getResult()) {
list.add(document.getString( "BookID" ));
}
Log.d(TAG, list.toString());
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
Thank you
I think what you're looking to do is pull in 20 different images, at random, from any user's sub-collection. That said, if you don't choose to use another structure and wish to have all of the data nested, you can opt for a collectionGroup query.
The bit on "random" is a bit tricky, and you may want to use some hacky ways to make it work. But starting with getting access to the various user's nested sub collections:
All of this data, regardless of user, lives in collections called MyBooks so you can run a collectionGroup query on MyBooks.
The way I would do it, with the random-image hacky bit would be, in partial pseudocode code to give you an idea (I confirmed collectionGroups in the Java SDKs):
usedImages = []
for numbers 0 to 19 {
let num = false
while (!num) {
let tempNum = Math.floor(Math.random() * 10);
if (!( usedImages.includes(tempNum)){
num = tempNum
}
}
var allBooks = db.collectionGroup('MyBooks').where('imageKey', '==', num);
allBooks.get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
console.log(doc.id, ' => ', doc.data());
});
});
}
The idea with the code is to generate 20 random and unique numbers num (probably better ways to do it than what I hacked together above), and then take that number and look for all entries in MyBooks where that imageKey exists. This is the hacky way around the random image code, where you would need to add a globally unique ID to each of them.
Collection Group doesn't specify a path of parent collections, so it allows you to look into all collections at once.
In the above pseudocode, you're running 20 collection groups queries.
You can find more on Collection Group queries here: https://firebase.google.com/docs/firestore/query-data/queries#collection-group-query
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.
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