4

I'm writing a Firebase Cloud Function that is called from an Android app. The function should get a random user from the users node in the Database (Any random user but not the one who sent the request) and return it to the client. The problem is that the data is not structured as an array (And it should not be and can't be).

Data structure in the database.

So I can't pick a random number and read that node, I also don't want to read all the users node and iterate it because it contains a lot of users and that would be time and resources-consuming. The Question:

How can I get a random user different from the one who sent the request, and changes with every time the function is called (completely random), while not querying a lot of data from the database?

4
  • is the user who sent the request in the same place at other users ? Commented Jul 26, 2018 at 12:47
  • Yes it's in the same node. Commented Jul 26, 2018 at 12:48
  • 1
    Dan's answer here shows the best options: stackoverflow.com/questions/46798981/…. Even though it's written for Cloud Firestore, the same logic applies to the Firebase Realtime Database. Commented Jul 26, 2018 at 13:21
  • But I don't think that Realtime Database has the whereField function. Commented Jul 26, 2018 at 13:32

1 Answer 1

3

The main problem:

Firebase doesn't keep track of the child count, which we need for the randomization. We can however use DataSnapshot.numChildren().

To avoid this, you can add a count element to the /users node, which you would have to maintain manually, using cloud functions (e.g. when adding a user, increment count, and decrement on deletion).

On the other hand, since the cloud functions are on the server, and firebase will manage the cache for you, it is not a big deal to actually use DataSnapshot.numChildren(), since the data will be cached once the function has been executed for the first time, and will be updated only when there are changes.

Therefore I would recommend to utilize the numChildren() function:

db.ref('/users').once('value').then(snapshot => 
    Math.floor((Math.random() * snapshot.numChildren()))
);

Alternatively using the count-method:

db.ref('/users/count').once('value').then(snapshot => 
    Math.floor((Math.random() * snapshot.val()))
);

Getting the random Node:

db.ref('/users').orderByKey().startAt(random).limitToFirst(1);

Method 1:

// https://stackoverflow.com/a/38423694/4161937
// attach permanent listener to force firebase caching (not sure if it works for this case)
db.ref('/users').on('value', () => {});
// will read #random amount of items
db.ref('/users').orderByKey().limitToFirst(random).once('value').then(users => Object.keys(users)[random]);

Method 2:

// https://stackoverflow.com/a/38423694/4161937
// attach permanent listener to force firebase caching
db.ref('/users').on('value', () => {});
db.ref('/users').once('value').then(users => Object.keys()[random])

You can read more about query order- and filtering in the official docs:

Retrieving Data - How Data is Ordered

Sign up to request clarification or add additional context in comments.

7 Comments

I actually can't read all the users node, it's really very large, and I need to execute the function as fast as I can, this is not a n effective solution.
@AmeerTaweel That's why I included the count method, you have to add a count node and add two functions that will update it when you add or remove users. Than you can use that to get the random user. Using the count-method will reduce the reading to the node /users/count, which is just a single read operation of a number, and therefore really quick. The only thing left to do is to check that the user at the random position is not the executing user.
But the data is not structured as an array, therefore I can't use a random index.
Ah, sorry, I forgot to address that Issue, I'll update my answer
It actually doesn't work, because the startAt() method needs a string as a parameter not an integer,so I tried to convert the random to a string like this: startAt(${random}) but the result of the query is always the first user.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.