Reputation: 3347
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?
Upvotes: 3
Views: 5622
Reputation: 3594
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!
Upvotes: 4