.order ("RANDOM ()") with will_paginate gem

I am wondering if there is a way to still use .order("RANDOM()")

with will_paginate

so that on page load and it orders pages, all messages will remain the same on every page until the home page is reloaded.

so that all messages do not localhost:3000/posts?page=1

remain unchanged until localhost:3000(root_path)

they are visited again.

The problem is that it will post messages to paginate, but it updates them for every page selected, so you will often see messages on page 1 as well on page 2.


source to share

1 answer

One way to do this is to set a random seed that your database orders so that it returns the same sequence of random numbers every time. You can keep this seed in your users session and reset only when you want. However, there is a complication - even if setting a random seed every time causes the same ordering of random numbers, there is no guarantee that your database will execute it in your rows in the same order every time, unless you force it to do so.

SELECT items.*
FROM (SELECT setseed(0.2)) t
   , (SELECT name, rank() OVER (ORDER BY name DESC)
      FROM foos ORDER BY name DESC) items
   JOIN generate_series(1, (SELECT COUNT(*) FROM foos))
     ON items.rank = generate_series


As you can tell, this is quite complicated and it forces your database to materialize your entire table into memory. It will work for small datasets, but if you have a large dataset, this is out of the question!

Instead, I suggest you go with a solution more like the tadman suggested above : generate the results page, store the IDs in the session and when you need to create the next page just ignore whatever you've already shown to the user. The code will look like this:

class ThingsController < ApplicationController
  def index
    @page = params[:page].to_i
    session[:pages] ||= {}

    if ids = session[:pages][@page]
      # Grab the items we already showed, and ensure they show up in the same order.
      @things = Things.where(id: ids).sort_by { |thing| ids.index(thing.id) }
      # Generate a new page of things, filtering anything we've already shown.
      @things = Things.where(["id NOT IN (?)", shown_thing_ids])
                      .limit(30) # your page size
      # Save the IDs into our session so the above case will work.
      session[:pages][@page] = @things.map(&:id)

  def shown_thing_ids


This method uses a session to store the IDs that were shown on each page, so you can guarantee the same set of items and the display order will show if the user comes back. For a new page, it excludes all elements that are already displayed. You can reset the cache whenever you want:



Hope it helps! You can also use Redis or Memcache to store your page data, but session is a good choice if you want the order to be random for each user.



All Articles