我在我的模型中使用了这种方法,为我的应用程序中的搜索功能提供支持:
def self.get_products(cat_ids, term, min_price, max_price, sort_column, sort_direction, current_store = nil)
products = Product.where(category_id: cat_ids).joins('LEFT JOIN stores ON stores.id = products.store_id')
products = products.order(sort_column + " " + sort_direction)
products = products.where(store_id: current_store.id) if current_store.present?
products = products.where("lower(product_title) like ?", "%#{term}%") if term.present?
products = products.where("price_pennies >= ?", (min_price.to_f/1.2).round * 100) if min_price.present?
products = products.where("price_pennies <= ?", (max_price.to_f/1.2).round * 100) if max_price.present?
products = products.where('stores.opened_to_customers = ?', true)
products
end
上述方法中参数的简要说明:
cat_ids: An array of all the relevant category_ids. In this case, using the awesome_nested_set gem helper
cat_ids = @category.self_and_descendants.pluck(:id)
term: The search query entered by the user
我觉得剩下的参数非常具有自我描述性。
这已经在2个月的过程中完美地工作了但是现在产品表中的行接近300,000,它已经变得非常慢并且很多时候它抛出了这个错误:错误R14内存配额超过(该应用程序托管在Heroku的)。
什么是缓存此查询的最佳方法?更重要的是,是否有更好的方法来编写此查询以提高速度并避免内存泄漏?
Ps:我使用memcached来缓存我的应用程序。我已经在其他地方使用了Rails Cache fetch,但由于这有很多参数,我对于如何缓存如此动态的东西感到困惑。
答案 0 :(得分:3)
如果您想知道如何准确地缓存结果,那么一种方法是生成一个取决于传递的参数的缓存键。
在下面的实现中,我将每个条件分成不同的变量,然后根据条件变量的字符串创建一个哈希键。
base_conditions = {
products: {category_id: cat_ids.sort},
stores: {opened_to_customers: true}
}
current_store_condition = \
if current_store.present?
{store_id: current_store.id}
end || ""
term_condition = \
if term.present?
["LOWER(product_title) LIKE ?", "%#{term.downcase}%"]
end || ""
price_range_min_condition = \
if min_price.present?
["price_pennies >= ?", (100 * min_price.fdiv(1.2)).round]
end || ""
price_range_max_condition = \
if max_price.present?
["price_pennies <= ?", (100 * max_price.fdiv(1.2)).round]
end || ""
不是所有这些都采用相同的方法,最好让这些条件来自专用方法。事情会更加整洁。
cache_key_string = [
base_conditions,
current_store_condition,
term_condition,
price_range_min_condition,
price_range_max_condition
].join('/')
cache_key = Digest::MD5.hexdigest(cache_key_string)
some_time_limit = 1.day # or some other suitable value
Rails.cache.fetch(cache_key, expires_in: some_time_limit) do
Product.
joins("LEFT OUTER JOIN stores ON stores.id = products.store_id").
where(base_conditions).
where(current_store_condition).
where(term_condition).
where(price_range_min_condition).
where(price_range_max_condition).
order("#{sort_column} #{sort_direction}").
all
end
此外,您在那里的LIKE
查询会很慢。我建议使用ThinkingSphinx或ElasticSearch。
另一种方法是使用分页并一次获得选定数量的结果。它对内存的压力会减小,每次都会得到更新的结果。要实现此目的,您可以将page
参数传递给您的方法,并执行以下操作:
limit = 20 # show 20 results at a time
Product.
joins(joins_string).
where(conditions).
order(order_string).
offset((page - 1) * limit). # value of first page is 1
limit(limit)