1

In my app upcoming project, I plan to use the google ecosystem - that means firebase auth, firestore (noSQL), cloud functions. I want to stick to noSQL as it fits better for this project. While scoping the project, the client asked for a leaderboard of all users (application has some kind of xp system), which is not a problem, but then I remembered I'm working in noSQL.

What is the best approach for this problem? This is a duplicate of this question, but I don't feel satisfied with the approaches that were presented. Is there any better way to do this? I expect rapidly scaling user base in 10's of thousands of users.

I did a quick math considering this answer. The max size of document in Google Firestore is 1MiB, which amounts to maximum of 62,500 users that can fit into the document, considering they have userID that spans to 8 digits and XP that spans to 6 digits. This solution felt like the most peaceful amongst any others.

The problem is, that sorting would have be executed on the client, in Flutter app.

Would this solution be somehow good?

Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
  • I removed the MongoDB tag as that seemed irrelevant as the question just mentioned Firestore related queries. Either ways, getting count of users having score more than current user should help. If they have same score, you can sort by any other parameter. – Dharmaraj Oct 25 '22 at 17:36
  • 1
    @Dharmaraj thank you, I just wanted to do that, but when I edited my question, you already removed it. – FlutterIsLoveFlutterIsLife Oct 25 '22 at 17:43
  • @Dharmaraj As the original post stated "I have project that I need to display a leaderboard of the top 20, and if the user not in the leaderboard they will appear in the 21st place with their current ranking." I need a leaderboard of top users, rank of single user does not really matter, i need to show a leaderboard. – FlutterIsLoveFlutterIsLife Oct 25 '22 at 17:44

2 Answers2

1

The first link that you've shared mentions that you'll need a query that looks like:

SELECT count(id) FROM players WHERE score > {playerScore}

Firestore now supports aggregation queries and has a new count() function that returns number of documents matching your query. You can try running the following query:

db.collection("users").where("score", isGreaterThanOrEqualTo: userScore).count().then(
      (res) => print(res), // <-- user rank 
      onError: (e) => print("Error completing: $e"),
    );

This was added recently so make sure you are using latest version of the Firebase SDK.

Additionally, for the query related to billing in the comments, orderBy() does not incur additional charge. You are only charged for the number of documents returned by the query i.e. limit(100) will return a maximum of 100 documents and hence 100 read at most.

Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
  • Hello, thank you for your answer, but I dont want to print user's position in the ladder (even tho this is a useful answer and I will probably use it, so thank you), I want to print (f.e.) top 100 users on the leaderboard. I've edited my question so it is a bit more understanding. – FlutterIsLoveFlutterIsLife Oct 25 '22 at 17:39
  • @FlutterIsLoveFlutterIsLife based on your comment above, you can just order the documents by XP, read first 100 documents and cache it for a certain duration? Also checkout [Firestore bundles](https://firebase.blog/posts/2021/04/firestore-supports-data-bundles), you can run the query once and serve it over the CDN for next few minutes. And then to get current user's rank, you can use the count function as in the asnwer above – Dharmaraj Oct 25 '22 at 17:45
  • I think that ordering documents by XP also counts as a read, so I would be still billed for 50K reads for that one query. Thank you for the bundles tip, I've already checked that out and it is a good option for caching the result. – FlutterIsLoveFlutterIsLife Oct 25 '22 at 17:46
  • @FlutterIsLoveFlutterIsLife no, you are charged 1 read per 1000 documents in count query. If user rank is 34465 then you will be charged 35 reads. – Dharmaraj Oct 25 '22 at 17:49
  • I am not talking about count query, that is not the important thing here, sorry for misunderstanding. I was talking about your latest comment about bundles, the sentence before. When I order documents by XP and read the first 100, I am still billed for the whole collection. Or am I wrong? – FlutterIsLoveFlutterIsLife Oct 25 '22 at 17:52
  • @FlutterIsLoveFlutterIsLife yes, that'll be 100 reads as you are reading only first 100 users but if only once if you cache it somewhere. You can use `query(collection("users"), orderBy("XP", "desc"), limit(100))` – Dharmaraj Oct 25 '22 at 17:53
  • Wait. For real? My whole life just turned upside down. I was used to believe that sorting all documents by attribute will still count as a read even though I am limiting the results. This is truly a game-changer. Thank you. I will test it as soon as I can. – FlutterIsLoveFlutterIsLife Oct 25 '22 at 17:57
  • @FlutterIsLoveFlutterIsLife no you are only charged for the number of documents returned by the query. That's the main use of limit() – Dharmaraj Oct 25 '22 at 18:02
  • @FlutterIsLoveFlutterIsLife if the answer resolved your queries then you can accept and upvote the answer so others will know it's resolved. [What should I do when someone answers?](https://stackoverflow.com/help/someone-answers) – Dharmaraj Oct 26 '22 at 03:59
  • I cannot accept my own answer till tomorrow. I figured I will write separate answer, so user does not have to read through 10 comments. – FlutterIsLoveFlutterIsLife Oct 26 '22 at 07:16
  • Your answer is not answer to the original question but to "how are reads charged for orderBy()". It's a different question after you edit it making my answer above seem irrelevant. For future reference (just in case one above was not of any use), if an answer resolves your query, you should ideally accept that and not post one yourself. – Dharmaraj Oct 26 '22 at 07:30
1

Answering my own question and locking this thread.

User Dharmaraj pointed out, that while using .limit(100), Firestore does not charge for the whole collection, but only for amount of the limited results. This was unknown for me and now I am presented with a great solution that doesn't screw up your data structure.

Thank you again, mr. Dharmaraj for your help.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 28 '22 at 10:22