Ben Scheib
Ben Scheib

Reputation: 392

How do I randomize objects with Sunspot in Rails with pagination that doesn't change?

We use Sunspot Solr with our Rails 3.1 app. We have a location model that we are searching. Here's the code for the search

location_search = Location.search do
  keywords params[:search][:keywords] if params[:search][:keywords].present?
  with :category_ids, category_id if category_id.present?


  unless params[:search][:keywords].present?
      order_by :premium, :desc
  end

  if params[:search][:sort].present?
    field, direction = params[:search][:sort].split('-')
  else
    order_by :score, :desc
    order_by :random
  end
  paginate :page => params[:search][:page], :per_page => per_page
end

My question is how do I get locations to be randomized in the results while keeping the pagination the same? Meaning, I want to sort the results randomly (not by record creation date or name) but I want page 2 of the results to be the same list even if I refresh the page (and pass 2 to params[:search][:page]). Ideally, the order would stay the same for the current users session.

The reason for this is locations on our site pay to be premium so premium results should come first, but those who don't pay should be randomized so we aren't favoring locations because of the order they were created or their order based on name.

Thanks! Help is really appreciated.

Upvotes: 0

Views: 535

Answers (3)

Houen
Houen

Reputation: 1059

You can use the same technique as I answered with here about a consistent random seed.

Sunspot supports using a random seed. See the docs here: https://github.com/sunspot/sunspot/wiki/Ordering-and-pagination#pagination-with-random-ordering

So what you would do (See my other answer for details) is generate and store a random number in the user session. Then use this number as a seed for your random retrieval method:

Sunspot.search(Post) do
  paginate(:page => 2, :per_page => 15)
  order_by(:random, :seed => session_specific_random_seed)
end

Upvotes: 0

zsiec
zsiec

Reputation: 76

Some time ago, I was also working on a paginated collection that had be configured to return a random sort order. I realized that because sunspot always sends Solr a random seed value, I would intermittently receive duplicate results between pages. In order to fix this, I thought it useful to be able to configure the seed value while searching.

The implementation I thought would make the most sense would retain the existing syntax specifications while also allowing a new parameter type to the order_by function.

For Example

Model.search do
  order_by :random, :seed => 12345, :direction => :asc 
end

I implemented this and it was merged into master for the sunspot_rails gem with this pull request: https://github.com/sunspot/sunspot/pull/328

Upvotes: 3

Kyle Maxwell
Kyle Maxwell

Reputation: 186

You can retrieve a much larger page than needed, shuffle it predictably, and then cut it down to size. Perhaps something like this (Random is a ruby 1.9 class):

SUPER_PAGE_MULTIPLIER = 5

location_search = Location.search do
  keywords params[:search][:keywords] if params[:search][:keywords].present?
  with :category_ids, category_id if category_id.present?


  unless params[:search][:keywords].present?
      order_by :premium, :desc
  end

  if params[:search][:sort].present?
    field, direction = params[:search][:sort].split('-')
  else
    order_by :score, :desc
    # order_by :random
  end
  paginate :page => params[:search][:page] / SUPER_PAGE_MULTIPLIER, :per_page => per_page * SUPER_PAGE_MULTIPLIER
end

session[:seed] ||= rand(100000)
rng = Random.new(session[:seed])

too_many_results = location_search.results
too_many_results.shuffle!(rng)

results = too_many_results.slice((params[:search][:page] % SUPER_PAGE_MULTIPLIER) * per_page, per_page)

Upvotes: 0

Related Questions