Search code examples
ruby-on-railsruby-on-rails-2

Batch search for named_scope


This is an existing code written by someone else and am trying to enhance it. I am a java developer working on Ruby on Rails, so kindly be considerate.

I have entities like this

User 

Delivery entity,

Delivery 
belongs_to :user 

named_scope :for_abcs, :conditions => {'deliveries.xyz_type' => ['Xyz1', 'Xyz2']},

many such named-scopes are defined.

Now to fetch the deliveries its written like this

 @deliveries = current_user.deliveries.send("for_abcs").with(:xyz, :sender, :receiver)
...
...
...
# few other  conditions added to @deliveries 

finally

@deliveries.sort(...)

This sort is taking huge sql and giving performance issues. I want to use find_each, but find_each is only for Active Entity in Ruby on Rails, How can I achieve this (if possible) without much code change)

Earlier I used to do

Delevery.find_each 

wherever it is

Delivery.find

Now I cant do as it is an array, what is the workaround or right procedure to do that in Ruby on Rails.

EDIT : What I tried :

deliveries_temp = []
@deliveries.find_each(:batch_size=>999) do |delivery_temp|
    deliveries_temp.push(delivery_temp)
end

This gave me error

undefined method `find_each' for []:Array

type(@deliveries) returned ActiveRecord::NamedScope::Scope , rails version 2.3.18


Solution

  • After lot of research for a week and learning about named_scopes by checking its source code. I understood what the problem was. The @deliveries is an object of class ActiveRecord::NamedScope::Scope . This class do not have find_each method. So I wrote a new named_scope for limit and offset in Delivery model file as follows :

    named_scope :limit_and_offset, lambda { |lim,off| { :limit => lim, :offset=>off } }
    

    After this , I called it in a loop passing offset and limit , for ex. first loop has offset=0, limit=999 , second loop has offset=999, limit=999 . I will add all the results into an emptry array. This loop continues till the result size is less than the limit value . This is working exactly the way I wanted , in batches.

    set = 1
    total_deliveries = []
    set_limit=999
    original_condition = @deliveries
    loop do
        offset = (set-1) * set_limit
        temp_condition = original_condition.limit_and_offset(set_limit,offset)
        temp_deliveries = temp_condition.find(:all)
        total_deliveries+= temp_deliveries
        set += 1
        break if temp_deliveries.size < set_limit
    end
    @deliveries = total_deliveries.sort do |a, b|