How to handle removal of previous elements in cursor based pagination? - pagination

Let's say I have the following list of objects:
[{id: 1}, {id: 2}, {id: 3}, {id: 4}]
and for the sake of simplicity, let's assume I want to show 2 elements per page.
I can query the first page by providing the following arguments: myQuery(first: 2)
The second page by using the endCursor of the first page (which is the id 2): myQuery(first:2, after:2)
Now let's say I am on the second page, and the element with the id 2 gets removed. I want to move to the previous page, by executing: myQuery(last:2, before:3)
This would lead to the first page having only one element (the one with the id 1), which for me seems as a bad user experience.
Is cursor based pagination not well suited for offset pagination, or how do you handle things? The only thing I can think of, is not refetching the data. In this case the user would still think the object with the id 2 is there.

It was not a clever question. The easiest solution would be to use myQuery(first: 2) for the first page. Because I always know the sate, at which page I am.

Related

React js, how to reload the whole component without caching the previous state value in the new rendering

I am implementing a movie search application; in which movies will be returned after clicking search.
In the result lists, user can give "like" to the movies displayed to them.
However, if the user selects to sort by like, the my sort function actually is sorting the "old" search result while some movie's number of like may be changed due to user input.
So, I would like to reload the pages so that the page will go back to the server to load new data and then do the sorting again.
May I know if there is a way to reload the pages by calling my current URL and then I add an extra param on the URL like (sort by no of like). So that I can know I should sort it by no of like in the server-side function
Or if there is a simple approach to deal with this case
Edit for better illustration
the search result is a list of movie
stage 1
1.
Titanic
like:3
2.
spiderman
like:3
stage 2 (user give a like to second one)
1.
Titanic
like:3
2.
spiderman
like:4 <----- no of like increased due to the user giving raising point)
stage 3 User suddenly wants to sort the results by no of like; then I sort the results on the fly.
1.
Titanic
like:3
2.
spiderman
like:3 <------ the results is not updated since the result is old
routing function I am using
router.push({
pathname: "/search",
query: {sort: sort}
)
but the reload is not clean. Component is still cached
[edit 2]
In each item in the result list, it is actually wrapped by an result card components
So, to sum up, the server passes the search results to the search page.
Then I iterate the results and then return a number of result cards.
In the result card, it allows users to make an action like raise like.
The reason I can keep it dynamic without calling the API is I kept these two hook.
const [likeCount, setLikeCount] = useState(movie.likeBy.length);
const [liked, setLiked] = useState(false);
But the problem is that when the user clicks the "sort by like", even it gets the new sorted search result. The result card component is still keeping previous data values. It makes the new render weird. That's why I want to make a complete reload that can clear all components' states.
And I found that router.push doesnt do the job
Hope this explains things clear
why do you need to reload the page to get new data you can do it in the same URL
let's assume your baseurl ===>>> "getmovies?sortby="
if you don't pass the value in sortby then it will fetch all data
after adding value in sortby query it will fetch sorted data
sorturl ===>>>"getmovies?sortby=like"
You can use optimistic UI (which is persistence), I have used it with GraphQL and Facebook uses something similar. As soon as a user likes a movie, React updates the cache which it uses to render the UI. It makes the API call in the background and if it is successful, the UI remains the same (hence optimistic).
Edit:
After you have updated your question, I think we can look into states. Currently you are updating states when user likes a movie but when they sort it again, we are using the old results object or array returned from the server at the first time. If we are not calling the server everytime a user sorts the results, we can store a result copy in a component state fetched from the API and consider it as a source of data for dynamic rendering. Now if a user likes a movie, you update the local state of the result object, it will cause render and updates the UI. When user try to sort the movies, you use local state which has all the latest information and sort it.

when startKey is unknown how to implement strategy for couchdb/pouchDb

Numerous readings indicate that skip should REALLY be avoided when doing pagination. As in the link most cite that using startKey and limit is the way to go. After I get the first page I know the startKey of that page, the lastKey of that page and the total entries. If I have a pagination control with page numbers as buttons and the user selects page 3, how do I get there? I have no idea what the startKey of page3. Perhaps, I do a simple view up front to go get the start keys for the each page.
This page nicely describes pagination:
http://docs.couchdb.org/en/1.6.1/couchapp/views/pagination.html
So, you can't really have a "Go to page 298", but to have links to the previous and next 5 pages, you can look up a larger number of preceeding and following documents and generate links accordingly. For example, if you have 10 posts per page, look up 50 following keys and take every 10th one.
As for making a "Go to page X", perhaps a background script that generates somesort of cache?

nodejs notification system

I'm doing a notification system for my website.
A notification systeme like facebook. Or stackoverflow.
I have 2 problems.
How store in db ? I can store ALL notifications in the user document ? or in a document apart (because i think monogdb is limited for size in a document ?) Or, store intelligently ? (using inc, or a value (see: true/false) in db, with query sophisticated)
How do for brought at the page ? For exemple, when i click in a link in my inbox for stackoverflow, i'm redirect to the page. But me, i have a system that is multipage for exemple: I have 100 friends. There are listed 30 per page. So when i click on the notification i can't redirect to the because it's impossible to know the good page (users can be removed).
Thank you very much !
And if you have another ideas, tell me. Thanks.
EDIT:
(sorry for my english, i'm french)
For the first problem, i realize that i have to wait the time comes to choose my structure. Because my notification is .. a little complicated, so advance to the feeling.
For the second, i solved the problem. I explain:
(I take the exemple of friends because it's easy to undestand.)
I stored my data like this:
{
friends: [
{_id: xxxxx, ts: xxxx},
{_id: xxxxx, ts: xxxx}
]
}
Imagine i display all friends: 30 per page.
The problems are:
when i want to display all friends i cant sort using mongo. (a little problem)
If i want to lead a user to this list (30 per page) at a special friend, always keeping the sort by ts. I can't know the page. The uniq solution is to take all document.
But: veryyy bad in performance.
So, i store like this:
{
friends: {
xxxx: {ts:xxx},
xxxx: {ts:xxx}
}
}
Know i can sort the document, with use skip and limit.
So if i want a portion, i do not need to take all documents.
To know the page, i just do the number of < or > to the ts, i have for exemple 11 friends who are > to the ts of the friends that i want, and do a count for all friends (ex: 50 friends) with 50 and 11, i can guess the page.
Is this solution is good ?
- i need a count
- a query to know the number of > or <
and i can take the page where is listed the friend, keeping the sort ts
You can don't understand why i use a count. I need because they are not store in the same docment.
2 EDIT:
The problem with this solution, is that i need to make query object and update object outside of the mongo query (ex: for do friends.xxxxxx: {$exists:true}
ps: And what advantages are to use ts instead of date for mongodb ?
I'm using ts but i think i will store date, and no ts.
3 EDIT
I will do like Sammaye. Store in separate document. Take a look at: http://mongly.com/Multiple-Collections-Versus-Embedded-Documents/#1 and http://openmymind.net/2012/1/30/MongoDB-Embedded-Documents-vs-Multiple-Collections/
#Stennie make a pretty complete answer.
However recently I did a similar thing in PHP for my website. The first thing to understand is whether you are doing a notification system or a wall (the two are very different), it seems unclear to me and I am not sure what you mean by:
How do for brought at the page ? For exemple, when i click in a link
in my inbox for stackoverflow, i'm redirect to the page. But me, i
have a system that is multipage for exemple: I have 100 friends. There
are listed 30 per page. So when i click on the notification i can't
redirect to the because it's impossible to know the good page (users
can be removed).
That is not very good English and is very confusing when I read it. If you can expand on that I am sure people can answer better.
For a notification system I found that a large collection of notification objects also worked. So I had a schema like:
{
_id: {},
to_user: ObjectId{},
user_id: ObjectId{}, // Originating user
custom_text: "has posted a new comment on your wall post",
read: false,
ts: MongoDate()
}
And this would literally be the document I have to produce notifications. Each time a user commits an action that generates a notification it writes a new row to the DB with to_user being populated each time with each user needing to be notified. As for multiple users commiting the same action I actually convert the user_id field in a list of OjbectId's so I can say:
Sam, Dan and Mike all commented on your wall post
I then query by ts storing the last ts that the user looked at in their row allowing me to do a range based query on the newest notifications each time. This works quite well for sharding and querying in my personal experience.
Hope it helps,
Whether to embed or link is a common question for data modelling in MongoDB. If your number of notifications is going to be unbounded, you are likely going to be better saving these in a separate collection.
The current 16Mb document limit actually isn't as much as an issue as some other considerations:
A performance issue you may encounter by including all notifications in a single document is that fast-growing documents may also need to be relocated in the database more frequently (see Padding Factor).
You may want to be applying multiple updates to a document (such as setting a "read" flag on notifications) in a very short period of time, which means more contention for updating the same document (see Atomic Operations).
In order to implement paging you can use limit() in combination with a range query or skip(). A range query (eg. based on an indexed notificationDate) will make more effective use of indexes and perform better than skip() as your collection grows.

"Previous" link - equivalent of LIMIT x OFFSET y?

I'm creating a page system using CouchDB, showing:
10 items per page
a link to the previous page (if any)
a link to the next page (if any)
From this article on the topic, I understand that using skip is suboptimal, and that I should instead use the startkey property to specify the first document, read 11 documents from there, display the first 10 and use the key of the 11th to display the link to the next page. What troubles me is the link to the previous page. The article says:
Populating the link to the previous page is as simple as carrying the current startkey over to the next page. If there’s no previous startkey, we are on the first page.
This works when going to the next page: when I move from page 4 to page 5 I can remember that the previous page was 4. But when I move back from page 5 to page 4, I have no way of carrying over the startkey of page 3. How can this work?
Is it possible (and recommended) to use endkey along with skip=10 and limit=1 to find the first element on the previous page, so that I may create a link back to it?
You can in fact only ask for 11 documents with no skip, and that is what Futon does (look at CouchDB logs).
The trick
Both the next and previous page link will be similar: startkey is the first or last element, with a skip=1 to avoid overlapping. You then have to correctly use the descending parameter to get previous documents or next documents.
The execution
Whenever you're asking for a page, CouchDB answers with eleven documents. Let's say the key of the first one is first and the key of the last one is last. The pagination links will look like:
"next": /db/_view/myview?descending=true&limit=11&startkey=last&skip=1
"back": /db/_view/myview?descending=false&limit=11&startkey=first&skip=1
Et voilà! You just have to reverse the documents before displaying them when descending is false. ("Finding your data with views" from the CouchDB guide explains nicely the relation between those parameters and B-Trees.)
Bonus
You can easily get the docid of the first or last page (limit=1 and descending true or false), and get a pagination system that looks a lot like something you would have with a classical database (first, last, previous, next).
Read 21 documents instead of 11 - One extra going forward, and ten going backwards. The very first one holds the key to the previous page.

How to paginate a growing list?

What's a good way to paginate a list that is constantly growing?
For example, a user may open page 2 of some listing, keep looking at it for a while and then open page 3. Meanwhile, new records have been added to the beginning of the list (page 1) and caused page 3 to contain items that could have been on page 2 previously.
Should such a list be paginated from the other end? Then how to deal with the front page and the fact that it would, without special attention, contain TOTAL NUMBER OF ITEMS % PAGE SIZE items (ie. not constant)?
Assuming you have a fixed page size, some specified ordering, and the user is specifying the page they wish to view, I would simply fetch the data accordingly. Trying to do anything more complicated than that will just end up causing you unnecessary headache and confuse the user.
I think the element you're missing here is the ordering. If you specify an ordering then the user will intuitively understand. Also, that's how the majority of pagination is done, so you're not deviating from what you're user really expects.
I'd add a warning to advise the user that new items have come in and allow them to refresh the list. If they choose not to then maintain list in the state it was in when they clicked on the "Page 2" button. You can use a timestamp on the URL to determine which version of the list to serve to which user.
I do not think it's a good idea to page through a growing list. You'd better recalculate items that should be displayed on a page everytime user performs an action.

Resources