Reputation: 267
I am developing an application for Android that involves uploading and downloading images to a server. I am using Firebase for this purpose. All the images are stored in Firebase storage using the image hash as a filename. I also maintain a Realtime Database that stores some information about the pictures. I need to select a random picture from the storage, so I decided to select a random entry from the database. I found this answer on SO (as you see, int is written in Javascript), which suggests the following code:
const numberOfUsers = 15;
const randomIndex = Math.floor(Math.random() * numberOfUsers);
var ref = firebase.database().ref('companies/01/users');
ref.limitToFirst(randomIndex).limitToLast(1).once('value').then(snapshot =>
{
var user = snapshot.val();
// do something with the user data
});
but when I try to build a similar query in Java
imgref.limitToFirst(random + 1).limitToLast(1).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
randomHash = dataSnapshot.getKey();
Log.e("get random name: ", randomHash);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
I recieve an error
Can't call limitToLast on query with previously set limit!
So obviously I cannot use limitToFirst and LimitToLast in one query. So here is my question:
Is it possible to use limitToFirst and limitToLast together somehow to use this method of getting a random entry?
Is there any other method to get a random entry from a Firebase Realtime Database without downloading the whole database or iterating through it (as far as I understand, iterating is inefficient, if I am wrong, please explain)
If it is impossible to do this from inside the app, is there some other way (for example, using Firebase Cloud Functions)
Upvotes: 0
Views: 2389
Reputation: 376
If you aren't against adding an extra child to your data, you have a few options.
startAt(random).limitToFirst(1)
.If you want to avoid the transaction you could write a random long to each imgref node. Then you could try querying with a newly generated random like this:
ref.orderByChild("randomLong").startAt(newRandomLong).limitToFirst(1).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getChildrenCount() > 0){
for(DataSnapshot snap : dataSnapshot.getChildren()){
//Do something with your random snapshot
}
}else
{
/*
Handle case where the new random Long is after the range
currently in the database
ref.orderByChild("randomLong").endAt(random).limitToLast(1)
*/
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Note that #2 will add random weights to your values based on the random ranges between values. This effect should be less noticeable on larger lists, but if you need an even distribution you probably want to go with option #1.
If you can't change your current data structure then I'm not sure you can get around iterating the set as in Alex's answer.
Upvotes: 1
Reputation: 1591
You need to know how is your model to get a random key. If it is 1,2,3,4,5
ids you can use it like you are doing, so you can compare it.
The best way to do it, is to store all key values in a array, pick a random number with the array size and pick the index of the random number. Them u use the code from the similar answer dataSnapshot.getKey();
Upvotes: 0