Rails:提高简单搜索方法的性能

时间:2018-04-03 11:37:38

标签: ruby-on-rails ruby activerecord ruby-on-rails-5.1

这些天我一直在构建应用程序。功能完全没什么特别的,我必须连接到客户端的SOAP Web服务,获取一些数据,将其保存到我的pg数据库中,并根据这些数据构建搜索功能。

搜索必须在两个表上执行,两者合并为80K行。它需要在这两个表中的几个字段中查找输入文本中的每个单词,这两个表具有一对多的经典关联。

之前我弄脏了我正在考虑我必须完成功能的选择(ransack,searchkick,scoped_search等),但我最后尝试的只是vanilla Active Record而且我很惊讶地发现我可以实现比我想象的更容易的功能,并且具有可接受的响应时间,对于本地最昂贵的查询,大约400ms的活动记录时间。

所以问题是,这个应用程序在Heroku中的性能比在本地更差(我正在使用流浪盒btw开发)。平均而言,查询需要比本地查询长2-3倍,因此用户体验从可接受到穷人。我想知道是否有人可以帮助改善我的查询。我也担心获取数据的后台工作如何在本地以及存储器的某些问题方面表现不佳,但这是一个不同的故事。

相关的片段是:

实现搜索方法的

part_master.rb

class PartMaster < ApplicationRecord
has_many :part_variants, foreign_key: 'sap_cod', primary_key: 'sap_cod'
has_many :locations, foreign_key: 'sap_cod', primary_key: 'sap_cod'

scope :con_stock, -> { where("stock > 0") }
scope :planta, -> (planta) { where planta_cod: planta}

def self.search(params)
  recordset = PartMaster.joins(:part_variants).all
  recordset = recordset.con_stock if params[:stock].present?
  recordset = recordset.planta(params[:planta]) if params[:planta].present?
  recordset = search_keywords(params[:search], recordset)
  recordset
end

private 

def self.search_keywords(query, recordset)
  keywords = query.to_s.strip.split
  if query
    keywords.each do |keyword|
      recordset = recordset.where('part_masters.sap_cod ILIKE :q OR 
                          unaccent(descripcion_maestro) ILIKE unaccent(:q)
                          OR fabricante ILIKE :q OR ref_fabricante ILIKE :q 
                          OR fabricante_prov ILIKE :q OR ref_prov ILIKE :q', 
                          q: "%#{keyword}%")
    end
    recordset.distinct.order(:sap_cod)
   end
 end
end

这是从控制器调用该方法:

  def index
   parts = params[:search].present? ? PartMaster.search(params) : 
           PartMaster.none
   @parts = parts.page(params[:page]).per(50)
  end

我在每个可搜索字段中都有一个索引。

编辑:最后,我在答案中尝试了混合提案。我在每个表中创建了一个字段,它是搜索相关字段的串联,有2个OR语句而不是5个,我还在两个新字段中都放置了trigram GIN索引。我没有看到任何改进,与ActiveRecord相对应的时间非常相似,可能稍微好一点。

问题是,使用EXPLAIN的查询输出无法显示有关正在使用的索引的任何信息。

Hash Join  (cost=2243.29..6067.41 rows=2697 width=132)
Hash Cond: ((part_variants.sap_cod)::text = (part_masters.sap_cod)::text)
Join Filter: ((part_masters.combinada_maestro ~~* '%rodamiento%'::text)         OR (part_variants.combinada_info ~~* '%rodamiento%'::text))
->  Seq Scan on part_variants  (cost=0.00..1264.96 rows=54896 width=18)
->  Hash  (cost=1128.13..1128.13 rows=34813 width=132)
     ->  Seq Scan on part_masters  (cost=0.00..1128.13 rows=34813             width=132)
(6 rows)

2 个答案:

答案 0 :(得分:0)

提高AR查询速度的建议使用模型中的直接Postgresql查询

关键字循环示例

query = "SELECT * FROM part_masters WHERE......"

PartMaster.connection.execute(query, :skip_logging)

答案 1 :(得分:0)

我同意spikermann。循环中的多个OR也没有帮助。

如果您只想使用香草解决方案而不是添加SOLR或任何其他引擎,则可以使用一个单独的字段来保存您要搜索的字符串的副本。 (例如姓名,描述,......)。仅搜索此字段。当名称,描述或其他值发生变化时,您需要一些方法来更新字段。