0

I face the problem that I have a list of keys inside my firebase database and I want to retrieve one random key out of it, without downloading all keys and selecting one in my own app. Downloading all keys with for example .once() is a big no go, especialy because I may up end with 10.000+ keys in the future and I dont want my firebase bills to explode, Im still a student with no budget at all.

I already researched this topic and found following answer for my question.

The problem with this is, that I can not use limitToFirst() and also limitToLast() in the same query, else I get following error:

[Unhandled promise rejection: Error: Query.limitToLast: Limit was already set (by another call to limit, limitToFirst, or limitToLast).]

Also other people face the same problem like me and I generaly see that this question always gets much views but rather poor answers which are, like apperently in the case I mentioned earlier outdated, or just not for usage in Javascript and React-Native.

Additional informations: I have a counter for my keys so I know at every time how many keys are stored inside my database. Following picture shows my database entirely: enter image description here

This is the part of my code which shows the mentioned error:

const numberOfUsers = await firebase.database().ref("usercounter").once("value").then(snapshot => {
            return(snapshot.val()); //retrieve numbers of users from "usercounter"
        });
        const randomIndex = Math.floor(Math.random() * numberOfUsers) + 1; //"generating random number, excluding 0"
        var ref = firebase.database().ref('users'); //ref to my users keys
        await ref.limitToFirst(randomIndex).limitToLast(1).once('value').then(DataSnapshot =>
            {
                //This is the query that does not work at all. 
                console.log(DataSnapshot.val())
               
            });

Incase there is a way to overcome that problem, please let me know.

1 Answers1

2

I would solve that problem this way:

Create for each list element a value with the field name id or some other you want. It should be a simple integer. Idealy incrementaly growing with the list. You could use the counter to increase the value each time you add an element.

When trying to get a random element from the list first get a random integer between 0 and the total count of all elements. Then use this query to get a single random element:

firebase.database().ref("users").orderByChild("id").startAt(randomNumber).limitToFirst(1);

Even if you don't hit the id with the randomNumber you would the neareas next element. This should be able to scale to any size.

The most diffictult part would be to optain the id incrementaly without repeating them. I would use for that an cloud function that does it when an item is added. Even a combination with the counter would be interesting.

Tarik Huber
  • 7,061
  • 2
  • 12
  • 18
  • Tried it out, and it works perfectly. Not only the problem with downloading too much data is solved, Im getting the key which I asked for and also the child values nested below it which is totaly fine for my purpose. This solution should also apply to the linked example I shared above in my question. I dont think the Cloud Functions are necessary here because when I create a new user (thats what those IDs are for) I automaticly raise the total counter of all users. When its raised I can obtain it again and give the new list element a id with ```totalusers+1```. Mission accomplished. – Kubaghetto the fresh Testobun Jun 02 '21 at 22:42
  • the only thing thats somehow strange is a warning that pops off: ```FIREBASE WARNING: Using an unspecified index. Your data will be downloaded and filtered on the client. ``` Do you consider this a threat to my app? – Kubaghetto the fresh Testobun Jun 02 '21 at 22:48
  • I searched up this warning and now added ".indexOn": "counter" in my security rules, this diabled the little warning and now its everything perfect. – Kubaghetto the fresh Testobun Jun 02 '21 at 23:01
  • 1
    Sorry for the late respnse. Yes just adding an index for the 'id' field will solve that warning problem. Great to hear that it worked out – Tarik Huber Jun 03 '21 at 06:11