Progresively select sorted mongodb documents - database

Context:
My code is in JavaScript (mongoose)
I have a database with a lot of users, I want to create a leaderboard based on their scores.
I plan to show 10 users at a time with arrows to navigate and show the next 10 users.
Ideas:
Is it possible to make a query and progressively get the users, like an iterator that fetches as I go?
Or have the limit:
await Users.find({})
.sort({ score: -1 })
.limit(10);
And rerun the query to get the next 10 users
Finally, I could just get the documents one at a time, but I don't really know how would I do this.
Any help would be greatly appreciated, thanks.

you can use Skip and Limit for page navigation. i.e., pagination
const page = req.query.page * 1 || 1;
const limit = req.query.limit * 1 || 10;
const skip = (page - 1) * limit;
Then in the Query,
await User.find({})
.sort({ score: -1 })
.skip(skip).limit(limit);
Then in your Query
{{URL}}/User?page=1&limit=10

Related

Firebase Firestore Stock Count updating?

I am using firebase purely to integrate a simple ticket buying system.
I would think this is a very common scenario people have and wondering what the solutions are.
I have an issue with the write limit time, it means I can't keep the stock count updated.
Due to Firebase's 1 second write limit and the way transactions work, they keep timing out when there is a large buy of tickets at one point in time.
For example:
Let's say we have a simple ticket document like this
{
name: "Taylor Bieber Concert"
stock: 100
price: 1000
}
I use a firebase transaction server side that does (pseudo)
transaction{
ticket = t.get(ticketRef).data() //get the data of ticketRef doc
guard (ticket .stock > 0) else return //check the stock is more than 0
t.update(ticketRef, {stock : increment(-1) }) //update the document and remove 1 stock value
}
The transaction and functionality all works however if I get 20-100 people trying to buy a ticket as it releases, it goes into contention it seems and times out a bunch of the requests...
Is there a way to avoid these timeouts? Some sort of queue or something?
I have tried using Transactions server-side in firebase functions to update the stock value, when many people try to purchase the product simultaneously it leads to majority of the transactions being locked out / Aborted Code 10

Delete Firestore documents older than 2 days using React JS

Description: I have created a meeting app using ReactJS. In the database, 1 Document contains the data of a single meeting. I want to delete all documents which has been created 2 days. This process can either run all the time in database, or I would prefer, that the process will run every day at 12:00am, which is at night...
const getlink=()=>{
var currentDate = new Date(new Date().getTime() + 48 * 60 * 60 * 1000);
var day = currentDate.getDate()
var month = currentDate.getMonth() + 1
var year = currentDate.getFullYear()
const exp = day + "/" + month + "/" + year
const uid=uuid()
db.collection("rooms").doc(uid).set({
roomId:uid,
expire:exp
})
alert("🚨Your (RoomId) is: \n"+uid+"\n\n⚠️ Your Room will expire on - "+exp)
history.push("/")
}
Above is the code I have used for creating a room and saving to a database...
Note: I am using Functional Components... but not Class Based components.. So please try to provide a Functional based answer
Could anyone please help me figure out how to delete the rooms which has been created before 2 days..
You should query all the documents older than 2 days and then iterate the query results and remove them individually.
I would suggest adding a field in your document that stores the timestamp of the creation of that document so you can then query those documents older than 2 days.
To run this process in a specific time you might try using Cloud Functions, you can use Firebase scheduled function or alternatively you can set up a cron job that executes the program every x minutes.
In this other post there are some answers that might be helpful, please check this answer as an example to do the above.

Google App Engine Datastore Queries and Getting Relative Rank

Say I have the following model in Google App Engine's datastore:
class Post(ndb.Model):
name = ndb.StringProperty()
votes = ndb.IntegerPropery()
I want to first query Post to get all posts ordered by number of votes (highest to lowest) and then find the ranking/place of the Post with name "John".
So if the Post by John has the 2nd most votes, he would be in 2nd place.
Is there an easy way to do this?
Right now I'm doing something like:
posts = Post.query().order(-Post.votes)
for post, index in enumerate(posts):
if post.name == "John":
print(index)
I imagine this is not a very good/efficient way to do this.
If the total number of posts is very high and those named John are few it might be more efficient to do something like this:
total_posts = len(Post.query().fetch(keys_only=True))
john_posts = Post.query(Post.name == 'John')
for post in john_posts:
# find out how many posts have higher votes than this post
rank = len(Post.query(Post.votes > post.votes).fetch(keys_only=True))
print('%d out of %d' % (rank, total_posts))
It will also be cheaper, since you'd be reading from the DB only John-named posts (paid DB ops) - keys_only queries are free.
It's not an actual index in the total list though - you'd get same rank for posts with the same number of votes.

Keeping count of user metrics based around time

I want to keep count of some kind of achievements for users in a community based website.
The idea is to give achievements for logging in 5 days in a row, or once every weekend for an entire month.
I'm also gonna give achievements for getting 100 posts, but this is easy to determine. The time based examples I just gave are a little harder I think.
How can I make some kind of generic system to keep count of these metrics per user? Or will I end up with a big table with fields such as "every_weekend_for_month" and "5_days_in_a_row" and once those integers reach 4 and 5, they have been 'achieved'. But then I also, for both fields, have to keep score of the last weekend/day.
You will need to track all data that is (even partially) required to get the achievement.
For the achievements around logging in, you need to track each login once per day, having a table like:
user_id | login
1 | 2013-07-20
1 | 2013-07-19
1 | 2013-07-16
2 | 2013-07-20
...
Whenever the tracking event is triggered, you also check for the achievements.
event onLogin {
// get the last 4 logins before the current login
statement = (
SELECT login FROM tracking_user_login
WHERE user_id = 1
ORDER BY login DESC
LIMIT 1,4
);
statement.execute();
// did the user even login at least 4 times already?
if (statement.rowCount == 4) {
date lastLogin = todaysLogin;
int consecutiveLogins = 1;
// iterate descending through the last days
foreach (row in statement) {
if (row.login == (lastLogin - 1day)) {
consecutiveLogins++; // increment consecution
lastLogin = (lastLogin - 1day); // prepare next comparison
} else {
// consecution interrupted, ignore the rest
break;
}
}
// enough to achieve something?
if (consecutiveLogins >= 5) {
user.addAchievement('5 CONSECUTIVE LOGINS');
}
}
}
You can basically add all achievements around login in this event.
You could track all logins and use that data to extrapolate the achievements. Tracking and searching individual logins can be slower. If you have very large user base and lots of logins, it may not be trivial to do these counts on login.
If you want to be faster you could track the last login date for a particular achievement and then increment a counter which sounds like what you were thinking.

Google App Engine: How do you count query results given an offset?

I'm implementing pagination on sorted models and, as it stands, my queries are fetching way too much data. The way I display the links to the pages is similar to Google: the current page is highlighted and there is a surrounding "padding" of pages that you can navigate to. For instance, if there are 20 pages, the padding is 5, and you're on page 10, the page links would look like this:
... 5 6 7 8 9 [10] 11 12 13 14 15 ...
The thing is, I need to calculate the number of pages AFTER the current page in order to know how many page links past the current page should be shown. To do this, I sum the number of items I would need for the padding pages, plus the current page, plus one (to know whether to show the "..."), and then fetch these results. This results in a massive query of results that, ultimately, I only need a small subset of.
Google App Engine's api provides a count() function that returns the number of results a query fetches. However, it does not allow me to specify an offset.
How do I work around this problem?
I'm considering fetching the first item on the next page after the current page, then executing count() on another query that sorts on the values of that item, if that makes sense. Am I on the right track or am I missing something completely? I'm relatively new to app engine, so go easy! Thanks :)
UPDATE:
Thank you, Peter.
Cursors are indeed the appropriate approach to use. Here's an example for anyone looking to accomplish the same:
# For example, the following query has 27 results.
book_query = Book.all().filter("name_lowercase < ", "b" )
# Let's fetch 10 books starting at offset 0...
r = book_query.fetch(10, 0)
# This returns a cursor to the book after the last fetched result, index 10
c = book_query.cursor()
# Now let's count the number of results after our fetch, limit 100.
# To use cursors, the query must be exactly the same.
book_query2 = Book.all().filter("name_lowercase < ", "b" ).with_cursor(c)
book_query2.count(100) # Returns 17
I haven't used them yet, but I believe Query Cursors are what you are looking for.

Resources