【问题标题】:Rails: Combine multiple tables search conditionsRails:组合多个表搜索条件
【发布时间】:2016-12-02 10:15:56
【问题描述】:
def self.search(search)
  Post.where("caption LIKE ?", "%#{search}%") 
  Post.joins(:tags).where('tags.name LIKE ?', "%#{search}%" )
end

在这里,我试图将这两个条件与 OR 语句结合起来,即我希望能够同时搜索帖子标题和标签名称。我如何将它们加入一行?谢谢!

PostsController

def index
  if params[:search]
    @posts = Post.search(params[:search]).order("created_at DESC").paginate(page: params[:page], per_page: 10)
  else
    @posts = Post.all.recent.paginate(page: params[:page], per_page: 10)
  end
  respond_to do |format|
    format.html
    format.js
  end
end

post.rb

has_many :taggings
has_many :tags, through: :taggings

tag.rb

has_many :taggings
has_many :posts, through: :taggings

tagging.rb

belongs_to :post
belongs_to :tag

【问题讨论】:

    标签: ruby-on-rails search


    【解决方案1】:

    您必须连接两个表并编写搜索条件。确保使用 LEFT JOIN,因为 JOIN 永远不会给你一个没有任何标签的帖子。 所以,不要这样做:

    # This produces an SQL query with JOIN (not LEFT JOIN).
    # Posts without tags will never be found.
    Post.joins(:tags).where(...)
    

    改为使用Post.includes(:tags) whith references 子句:

    # you have to use 'references' clause here to produce a normal LEFT JOIN query instead of eager loading associations
    Post.includes(:tags).where("posts.caption LIKE :search OR tags.name LIKE :search", search: "%#{search}%" ).references(:tags)
    

    或者您可以使用joins手动编写查询条件:

    Post.joins('LEFT OUTER JOIN taggings ON taggings.post_id = posts.id LEFT OUTER JOIN tags ON taggings.tag_id = tags.id')
    .where("posts.caption LIKE :search OR tags.name LIKE :search", search: "%#{search}%").uniq
    

    如果您使用的是 Rails 5,有一个新方法 left_outer_joins 可以做到这一点:

    Post.left_outer_joins(:tags).where("posts.caption LIKE :search OR tags.name LIKE :search", search: "%#{search}%")
    

    【讨论】:

    • 我用joins 尝试了一个,它就像一个魅力(虽然我不得不在PostsController 中将.order("created_at DESC") 更改为.order("posts.created_at DESC"),因为我收到了“模糊列”错误) .谢谢!
    • 对于带有 has_many :through 关联的模型,您能否更新joins 行?我已经用模型的摘录更新了我的帖子
    • 编辑了手动写入的连接条件。其余的应该没问题。
    • 嗯,必须做一些修改才能让它工作。起初它没有返回没有任何标签的帖子,但后来我将其更改为Post.joins('LEFT OUTER JOIN taggings ON taggings.post_id = posts.id LEFT OUTER JOIN tags ON taggings.tag_id = tags.id').where("posts.caption LIKE :search OR tags.name LIKE :search", search: "%#{search}%").uniq,现在它可以完美运行了。我也在苦苦寻找.uniq,现在我已经了解了它,它帮助我解决了我在另一个模型中遇到的另一个与 sql 相关的问题!谢谢!
    • 你是对的。这是我的错误。编辑了答案。谢谢!
    【解决方案2】:
    Post.joins(:tags).where("posts.caption LIKE :search OR tags.name LIKE :search", search: "%#{search}%" )
    

    【讨论】:

    • 我尝试了与Post.joins(:tags).where('posts.caption || tags.name LIKE ?', "%#{search}%" ) 类似的方法。两者都不起作用。它似乎只通过标签名称搜索
    • 是的,我做到了。它与我提到的结果相同,即它仅通过标签名称搜索
    • 如果有区分大小写的问题,您可能想用 ILIKE 更改 LIKE
    • 如果你使用 LIKE 'Text' 将不匹配 '%text%'
    【解决方案3】:

    试试这个:

    Post.joins(:tags).where("caption LIKE ? OR tags.name LIKE ?", "%#{search}%", "%#{search}%")
    

    【讨论】:

      【解决方案4】:

      对于高级过滤,我会使用范围。您可以使用查询创建两个范围并在控制器中调用它们。

      型号:

      scope :search1, -> (search) {where("caption LIKE :search", "%#{search}%")}
      scope :search2, -> (search) {joins(:tags).where('tags.name LIKE :search', "%#{search}%" )}
      

      控制器:

      @posts = Post.where(nil)
      @posts = @posts.search1(params[:search]) if params[:search].present?
      @posts = @posts.search2(params[:search]) if params[:search].present?
      @posts = @posts.recent.paginate(page: params[:page], per_page: 10)
      

      更多关于范围的信息:http://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/

      他的帖子对我帮助很大,我建议你阅读它:)

      【讨论】:

        猜你喜欢
        • 2017-04-23
        • 2016-08-03
        • 1970-01-01
        • 2014-12-22
        • 1970-01-01
        • 1970-01-01
        • 2021-12-29
        • 2012-03-07
        • 1970-01-01
        相关资源
        最近更新 更多