Ruby-on-rails – How to get an ActiveRecord::Relation from an array of records sorted in a specific order of their IDs

activerecordrubyruby-on-railsruby-on-rails-3

I have a Rails engine that defines a class method top(count) on a model of choice. What this method does is grab count IDs from a sorted set (ZSET) in Redis. The idea is that each item of this model has a score, and this method is to return the "best" records. The method essentially looks like this:

def self.top(count = 1)
  ids = redis.zrevrange(self.score_set, 0, count - 1)
  items = self.find ids

  if count == 1
    return items.first
  else
    return items.sort { |x, y| ids.index(x.id) <=> ids.index(y.id) }
  end
end

As you can see, I currently sort the records after the fact because databases (or the adapters) tend not to maintain order when finding by multiple IDs. For instance, Model.where('ID IN (?)', ids) will sort by ascending IDs rather than the order of the passed IDs.

Sorting the items returns an Array, but I'd like this method to return an ActiveRecord::Relation instead so that my users can chain it with other ActiveRecord query methods. Is there a way I can use the built-in ActiveRecord query methods to maintain order from my array of IDs? Or alternatively, is there a way to construct an ActiveRecord::Relation from an array of records?

Best Answer

It really depends on the underlying database you are using. I do not think rails has a pretty way to do this.

I have an application where I am doing the same thing as you. Storing ids in a Redis ZSET and then needing to fetch them in that order. Since we are using MySql we can take advantage of the field function.

Model.where('id in (?)', ids).order("field(id, #{order})")

PS: Make sure your order variable is sanitized!

Related Topic