如何在Rails中使用PgSearch多搜索返回完整模型对象?
Rails中使用PgSearch multisearch返回完整模型对象的解决方案
问题描述
我在Rails中使用PgSearch的multisearch功能做搜索,目前只能返回content、searchable_type和searchable_id这几个字段,但我需要返回User和Blog模型的完整对象,期望的返回格式如下:
"data": [ { "id": 2, "searchable_type": "User", "email": "abc", "first_name": "abc", "last_name": "xyz", "created_at": "2022-08-05T09:40:18.986Z", "updated_at": "2022-08-05T09:40:18.986Z" }, { "id": 3, "searchable_type": "Blog", "title": "user", "created_at": "2022-08-05T09:40:18.986Z", "updated_at": "2022-08-05T09:40:18.986Z" } ]
我的现有代码如下:
Search控制器
class Api::V1::SearchController < Api::V1::ApiController def index @query = params[:value] @results = PgSearch.multisearch(@query) render json: { data: @results}, status: :ok end end
Blog模型
class Blog < ApplicationRecord include PgSearch::Model multisearchable against: [:id, :title, :created_at, :updated_at ] end
User模型
class User < ApplicationRecord include PgSearch::Model multisearchable against: [:id, :email, :first_name, :last_name ] end
解决步骤
1. 获取关联的原始模型对象
PgSearch的multisearch返回的是PgSearch::Document实例,每个实例都通过searchable关联指向对应的User或Blog对象。你需要把这些文档转换成对应的模型实例,同时注意避免N+1查询问题:
修改Search控制器的index方法:
class Api::V1::SearchController < Api::V1::ApiController def index @query = params[:value] # 预加载关联对象,避免多次查询数据库 @documents = PgSearch.multisearch(@query).includes(:searchable) # 提取对应的模型实例,compact过滤掉已删除的无效对象 @results = @documents.map(&:searchable).compact render json: { data: @results }, status: :ok end end
2. 自定义JSON返回格式(可选)
如果需要严格匹配你期望的格式(比如统一添加searchable_type字段、排除敏感字段),可以用以下两种方式处理:
方式一:用as_json直接自定义
修改控制器的render逻辑:
formatted_results = @results.map do |item| # 排除User模型的敏感字段,比如password_digest base_attrs = item.is_a?(User) ? item.as_json(except: [:password_digest]) : item.as_json base_attrs.merge(searchable_type: item.class.name) end render json: { data: formatted_results }, status: :ok
方式二:用序列化器(推荐复杂场景)
如果需要复用格式或者字段较多,可以生成对应的序列化器:
rails generate serializer User rails generate serializer Blog
然后分别定义序列化器字段:
# app/serializers/user_serializer.rb class UserSerializer < ActiveModel::Serializer attributes :id, :email, :first_name, :last_name, :created_at, :updated_at # 自定义添加searchable_type字段 attribute :searchable_type do 'User' end end
# app/serializers/blog_serializer.rb class BlogSerializer < ActiveModel::Serializer attributes :id, :title, :created_at, :updated_at attribute :searchable_type do 'Blog' end end
最后在控制器里使用序列化器:
render json: { data: @results }, each_serializer: ->(item) { "#{item.class.name}Serializer".constantize }, status: :ok
内容的提问来源于stack exchange,提问作者awais iqbal
相关产品推荐
相关产品推荐

