Rails:从多态关联集合获取ActiveRecord::Relation的方法
我明白你遇到的痛点:用PgSearch.multisearch做跨模型搜索时,要么碰到N+1查询,要么只能得到数组结果,没法拿到ActiveRecord::Relation来做后续的链式调用。针对这个问题,我们可以分场景来解决:
1. 优先方案:避免N+1的数组结果(满足大部分分页需求)
你之前尝试的includes(:searchable)其实已经能解决N+1问题,只是最终返回的是数组——但在分页场景下,这个方案其实足够好用,因为分页已经在pg_search_documents表层面完成了,效率很高:
# 先分页,再预加载多态关联,最后提取对应的记录 search_docs = PgSearch.multisearch('your_query').page(params[:page]).per(10).includes(:searchable) results = search_docs.map(&:searchable)
这里的includes(:searchable)会批量加载所有关联的记录,彻底避免N+1查询。虽然结果是数组,但分页逻辑是正确的,而且数组在视图渲染、后续处理中大多能满足需求。
2. 获取ActiveRecord::Relation的方案(分模型处理)
如果你的业务逻辑必须要ActiveRecord::Relation,那要注意:跨不同模型的结果无法合并成单一的Relation(因为每个Relation绑定对应的数据库表)。但我们可以按模型类型分组,分别获取每个模型的Relation:
# 第一步:先对搜索文档分页(这一步是高效的,直接在pg_search_documents表分页) search_docs = PgSearch.multisearch('your_query').page(params[:page]).per(10) # 第二步:按模型类型分组,收集对应记录的ID grouped_ids = search_docs.group_by(&:searchable_type).transform_values { |docs| docs.pluck(:searchable_id) } # 第三步:批量查询每个模型的记录,得到对应的Relation model_relations = {} grouped_ids.each do |model_name, ids| model_class = model_name.constantize model_relations[model_name] = model_class.where(id: ids) end # 比如获取Post模型的Relation: posts_relation = model_relations['Post'] # 获取Comment模型的Relation: comments_relation = model_relations['Comment']
这样每个模型的结果都是ActiveRecord::Relation,可以继续链式调用(比如添加筛选、排序等)。如果需要保持pg_search返回的相关性排序,你可以基于search_docs的顺序,对每个Relation的结果重新排序。
3. 单一模型搜索的简化方案
如果你的搜索只针对单个模型(而不是跨模型的multisearch),那直接用模型的pg_search_scope就可以,返回的天然就是ActiveRecord::Relation,支持分页:
# 假设你在Post模型里定义了pg_search_scope :search Post.search('your_query').page(params[:page]).per(10)
内容的提问来源于stack exchange,提问作者Alejandro Aristizábal

