ransackの複数モデル検索、複数タグ検索に少してこずったので、同じような人の参考になればと思って投稿します。
今回はUserモデル、Articleモデル、Tagモデル(acts-as-taggable-onっていうgemを使用)のケースで実装します。。投稿された記事のarticleのtextカラム、Userのnameカラム、そしてUserに紐づいているtagのnameカラムから(複数ワード)検索を行うというものです。
(今回はtagはUserに紐づいていてarticleには紐づいていません)
はじめにview側ですが、下記のようになります
<%= search_form_for(@search, url: articles_path) do |f| %>
<%= f.search_field :text_or_user_name_cont_all , class:"search_field", placeholder:"検索" %>
<%= f.submit '検索', id:"search_button" %>
<% end %>
このsearch_form_forやsearch_field、cont_allはransakの書き方となります。
この辺りの説明は省きます。
controller側ではransackのスコープ機能(ransackable_scopes)を呼び出しています。
引数で検索ワードを渡してarticleのmodel側で検索しています。
.
.
@search = Article.ransack(params[:q])
if params[:q].present? #paramsのnil対策
@search_articles_result = Article.by_any_texts(params[:q][:text_or_user_name_cont_all])
end
.
.
scope :by_any_texts, -> (string){
words = string.split(/[\p{blank}\s]+/)
searchs = search(text_or_user_name_cont_all: words).result(distinct: true)
search_tag = User.by_any_tag(words).flatten
search_result = searchs + search_tag
search_result.uniq
}
def self.ransackable_scopes(auth_object = nil)
%i[text_or_user_name_cont_all]
end
引数で渡されたparams[:q][:text_or_user_name_cont_all: words]をsplitして["word1","words2"]という形にします。
そのあとransackしますが、ransackのモデルを跨いでの検索はアソシエーションの記述感覚で簡単に実装できます。
今回の場合だとArticleのtextとUserのnameを検索したい場合は、text_or_user_nameとします。
書き方としては、関連するモデル名(user)+カラム名(name)で実装できます。
さらにuserに紐づいているtagも調べたいときはtext_or_user_name_or_user_tags_nameで検索できます。
tagsと複数形になるのはuserとtagが1対多の関係にあるためです。
ただ、今回のtag機能の実装はacts-as-taggable-onのgemを利用したためだと思いますが、複数のワード検索をしたときに引っ張ってきませんでした。(一単語だと引っ張ってきましたが複数ワードで検索するとダメでした、、、)
そこでtagに関しては別途Userモデルにscopeを作り、acts-as-taggable-onの検索機能(tagged_with)を使って実装しました。
scope :by_any_tag, -> (words){
user_tag = User.tagged_with(words, named_like_any: true)
user_tag.map{|tag| tag.articles}
}
article側でsplitしたwordsを引数として受け取ってUserに紐づいているタグを検索します。
その後にmapメソッドで検索結果に紐づいているarticleの配列を新しく作っています。
article側ではby_any_tagを呼び出して、二次元配列になっているのをflattenして、元々のtext_or_user_name_cont_allの検索結果とuser側で検索したtagの検索結果を足し合わせています。重複している結果があるかもしれないのでuniqして終了です。