Rails中如何筛选标题/描述含指定字符串或标签匹配的文章
解决方案:Rails多条件文章搜索(含关联标签)
环境与需求
- Ruby: 3.2.2
- Rails: 7.1.0
- PostgreSQL: 16
需求:实现文章搜索接口GET /search/articles?keywords=programming,筛选满足以下任一条件的文章:
- 标题(
name)包含指定关键词 - 描述包含指定关键词
- 关联标签的名称包含指定关键词
同时兼容Kaminari分页。
现有模型代码
class Tag < ApplicationRecord has_many :tagable_tags has_many :articles, through: :tagable_tags, source: :tagable, source_type: "Article" end class TagableTag < ApplicationRecord belongs_to :tag belongs_to :tagable, polymorphic: true end class Article < ApplicationRecord has_many :tagable_tags, as: :tagable, dependent: :destroy has_many :tags, through: :tagable_tags def self.find_by_keywords(keywords, page = 1, per = 50) sanitized_keywords = sanitize_sql_like(keywords.downcase) # 待完善逻辑 end end
原方案问题分析
之前尝试的查询使用includes(:tags).references(:tags)会触发内连接,导致无关联标签的文章被直接排除,无法匹配标题含关键词的场景。
完善后的find_by_keywords方法
class Article < ApplicationRecord has_many :tagable_tags, as: :tagable, dependent: :destroy has_many :tags, through: :tagable_tags def self.find_by_keywords(keywords, page = 1, per = 50) sanitized_keywords = sanitize_sql_like(keywords.downcase) keyword_pattern = "%#{sanitized_keywords}%" left_outer_joins(:tags) .where( "lower(articles.name) LIKE ? OR lower(articles.description) LIKE ? OR lower(tags.name) LIKE ?", keyword_pattern, keyword_pattern, keyword_pattern ) .distinct .page(page) .per(per) end end
关键说明
- 左外连接:
left_outer_joins(:tags)确保所有文章都被保留,即使没有关联标签,解决了无标签文章被过滤的问题。 - OR条件组合:同时匹配标题、描述、标签三个维度的关键词,满足任一条件即返回文章。
- 去重处理:
distinct避免同一文章因多个标签匹配关键词而被重复返回。 - 分页兼容:直接链式调用Kaminari的
page和per方法,天然支持分页功能。
测试用例验证
- 标题含关键词的无标签文章:匹配第一个OR条件,正常返回。
- 标题不含但标签含关键词的文章:匹配第三个OR条件,正常返回。
- 标题和标签均不含关键词的文章:不匹配任何条件,不会返回。
内容的提问来源于stack exchange,提问作者Stainz42
相关产品推荐
相关产品推荐

