如何让ActiveRecord原生查询的member_ids返回整数数组而非字符串?
问题:如何让PostgreSQL查询返回的member_ids直接为整数数组(无需Ruby端循环解析)
原查询代码
@tags = ActiveRecord::Base.connection.execute( <<~SQL SELECT t.id, t.name, t.member_tags_count, ( SELECT json_agg(mt.member_id) as member_ids FROM member_tags mt WHERE mt.tag_id = t.id) FROM tags t ORDER BY LOWER(t.name) SQL ) render json: @tags
原查询结果
该查询耗时1.9ms,返回结果示例:
#<PG::Result:0x000000010e368580 status=PGRES_TUPLES_OK ntuples=31 nfields=4 cmd_tuples=31> (ruby) @tags.first {"id"=>1, "name"=>"Avengers", "member_tags_count"=>3, "member_ids"=>"[1, 3, 7]"}
核心问题
API要求member_ids为整数数组,但当前返回的是字符串格式的JSON数组,希望无需循环@tags执行JSON.parse,直接让member_ids以数组形式返回。
当前临时方案(耗时较高)
以下方案返回正确格式,但耗时是原查询的4倍(5.7ms),代码繁琐:
@tags = Tag .joins(:member_tags) .order('LOWER(name)') .group(:id) .pluck( :id, :name, :member_tags_count, 'array_agg(member_tags.member_id)' ).map do |column| { id: column[0], name: column[1], member_tags_count: column[2], member_ids: column[3] } end render json: @tags
返回结果示例:
(ruby) @tags.first {:id=>1, :name=>"Avengers", :member_tags_count=>3, :member_ids=>[1, 3, 7]}
优化方案(保持原查询耗时)
直接修改原SQL中的聚合函数,将json_agg替换为array_agg,PostgreSQL会返回原生数组类型,ActiveRecord的PG适配器会自动将其解析为Ruby整数数组,无需Ruby端额外处理:
@tags = ActiveRecord::Base.connection.execute( <<~SQL SELECT t.id, t.name, t.member_tags_count, ( SELECT array_agg(mt.member_id) as member_ids FROM member_tags mt WHERE mt.tag_id = t.id) FROM tags t ORDER BY LOWER(t.name) SQL ) render json: @tags
效果说明
- 耗时仍保持1.9ms左右,和原查询效率一致
- 返回结果中
member_ids直接为整数数组:(ruby) @tags.first {"id"=>1, "name"=>"Avengers", "member_tags_count"=>3, "member_ids"=>[1, 3, 7]}
原理
json_agg返回的是JSON格式的字符串,PG::Result会默认按字符串处理;而array_agg返回的是PostgreSQL原生整数数组类型,ActiveRecord会自动映射为Ruby的Array<Integer>类型,直接满足API的格式要求。
内容的提问来源于stack exchange,提问作者Coderama
相关产品推荐
相关产品推荐

