I have been struggling for a while with problems along the same lines - performing efficient queries in rails. I am currently trying to perform a query on a model with 500,000 records and then pull out some descriptive statistics regarding the results returned.
As an overview: I want to pull out a number of products which match a set of criteria. I would then like to...
- Count the number of records (if there aren't any I want to supress certain actions)
- Identify the max and min prices of the matching records and calculate the number of items falling between certain ranges
As it stands this set of commands takes a lot longer than I was hoping for (26000ms running locally on my desktop computer) and involves either 8 or 9 active record actions each of which take around 3000ms
Is there something I am doing wrongly to make this so slow to process? Any suggestions would be fantastic
The code in my controller is:
filteredmatchingproducts = Allproduct.select("id, product_name, price")
.where('product_name LIKE ?
OR (product_name LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ?)
OR product_name LIKE ? OR product_name LIKE ? OR product_name LIKE ? OR product_name LIKE ? OR (product_name LIKE ? AND product_name NOT LIKE ?) OR product_name LIKE ?',
'%Bike Box', '%Bike Bag%', '%Pannier%', '%Shopper%', '%Shoulder%', '%Shopping%', '%Backpack%' , '%Wheel Bag%', '%Bike sack%', '%Wheel cover%', '%Wheel case%', '%Bike case%', '%Wahoo%', '%Bicycle Travel Case%')
.order('price ASC')
@selected_products = filteredmatchingproducts.paginate(:page => params[:page])
@productsfound = filteredmatchingproducts.count
@min_price = filteredmatchingproducts.first
@max_price = filteredmatchingproducts.last
@price_range = @max_price.price - @min_price.price
@max_pricerange1 = @min_price.price + @price_range/4
@max_pricerange2 = @min_price.price + @price_range/2
@max_pricerange3 = @min_price.price + 3*@price_range/4
@max_pricerange4 = @max_price.price
if @min_price == nil
#don't do anything - just avoid error
else
@restricted_products_pricerange1 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', 0 , @max_pricerange1).count
@restricted_products_pricerange2 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', @max_pricerange1 + 0.01 , @max_pricerange2).count
@restricted_products_pricerange3 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', @max_pricerange2 + 0.01 , @max_pricerange3).count
@restricted_products_pricerange4 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', @max_pricerange3 + 0.01 , @max_pricerange4).count
end
EDIT For clarity, the fundamental question I have is - why does each of these queries need to be performed on the large Allproduct database, is there not a way to perform the latter queries on the result of the former ones (I.e. use filteredmatchingproducts itself not recalculate it for each query)? In other programming languages I am used to being able to remember variables and perform operations of those remembered values, rather than having to work them out again before performing the operations - is this not the mindset in Rails?