How to create pagination for a list (get elements from a range of GenServer)

Hi, I have a big list in my GenServer which I want to load them as a pagination, for example I counted it and I have x records and I want to load it with a page size (like 20).

my user inputs: page 1 and this pagination should load (1 - 20) records and if my user inputs page 2 , it should loads (21 - 40).

I saw Stream.resource but I couldn’t use it

Please help me to understand this!!

Thanks

Not sure if this would be the best way

Stream.chunk_every(list_of_items, 20)
|> Enum.at(page - 1)
2 Likes

In BEAM lists are “linked” lists… so any page > 1 would require somehow to traverse the list… Either if you skip first N and take M elements… or if you “chunk_every” M and take Nth chunk of it.

We need some sort an indexing… What about storing in a map where key is the index? Then based on the request you quickly figure out the required keys and do Map.take/2?

Or maybe store somewhere that supports pagination? :ets and :mnesia support match specs… But to rephrase a regex joke “if you decide to use a match spec you now have two problems” :grinning_face_with_smiling_eyes:

Depends on the task =)

1 Like

If your elements are in memory, then you can just do:

page_size = 10
page_num = 3

list_of_elements
|> Enum.drop(page_size * (page_num - 1)) 
|> Enum.take(page_size)

This will give you the elements on the 3rd page (21 through 30).

However, keeping a large list in memory and implementing pagination in this way is potentially inefficient.
May ask you where your data is loaded from? It may be more efficient to offload pagination to the data source. For example, if the data source is an SQL database, you could fetch the elements for a given page with:

SELECT * FROM DATA
ORDER BY <SOMETHING>
OFFSET page_size * (page_num - 1)
LIMIT page_size

In this way only the elements of the desired page will be loaded every time, and with the proper indexes set up in your DB the query will be fast.

1 Like

Hi! This is a very useful library:

1 Like