ActiveRecord.find(array_of_ids), preserving order
Oddly, no one has suggested something like this:
index = Something.find(array_of_ids).group_by(&:id)
array_of_ids.map { |i| index[i].first }
As efficient as it gets besides letting SQL backend do it.
Edit: To improve on my own answer, you can also do it like this:
Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
#index_by
and #slice
are pretty handy additions in ActiveSupport for arrays and hashes respectively.
The answer is for mysql only
There is a function in mysql called FIELD()
Here is how you could use it in .find():
>> ids = [100, 1, 6]
=> [100, 1, 6]
>> WordDocument.find(ids).collect(&:id)
=> [1, 6, 100]
>> WordDocument.find(ids, :order => "field(id, #{ids.join(',')})")
=> [100, 1, 6]
For new Version
>> WordDocument.where(id: ids).order("field(id, #{ids.join ','})")
Update: This will be removed in Rails 6.1 Rails source code
As Mike Woodhouse stated in his answer, this occurs becase, under the hood, Rails is using an SQL query with a WHERE id IN... clause
to retrieve all of the records in one query. This is faster than retrieving each id individually, but as you noticed it doesn't preserve the order of the records you are retrieving.
In order to fix this, you can sort the records at the application level according to the original list of IDs you used when looking up the record.
Based on the many excellent answers to Sort an array according to the elements of another array, I recommend the following solution:
Something.find(array_of_ids).sort_by{|thing| array_of_ids.index thing.id}
Or if you need something a bit faster (but arguably somewhat less readable) you could do this:
Something.find(array_of_ids).index_by(&:id).values_at(*array_of_ids)