質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

検索

検索は、あるデータの集まりの中から 目的のデータを見つけ出すことです。

Q&A

解決済

2回答

2134閲覧

ransackで関連付けモデルも含めて全カラム検索したい!

say4hatii8

総合スコア28

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

検索

検索は、あるデータの集まりの中から 目的のデータを見つけ出すことです。

0グッド

0クリップ

投稿2018/11/16 03:01

編集2018/11/16 08:27


Nameモデル
・nameカラム
・ageカラム
・hobbyカラム

Postモデル
・name_idカラム
・contentカラム


「userはたくさんのpostを持つ」ということで、
has_manyとbelongs_toで関連付けを行っています。

■キーワード検索「山田 サッカー」でヒットさせたい。(データは既に追加済みです。)
ビューのフォームのname属性は、メイン部分の抜粋ですが以下のようにしています。
<%= f.search_field :name_or_age_or_hobby_or_posts_content_cont_all %>
(ageカラムの数値検索に関してArel.sqlとCASTで解決しています。)

■kaminariを利用しています。

■スペースで区切った複数ワード検索についてですが、splitとjoinを使って解決しています。

■上記のようですと、
@q = Name.includes(:posts).ransack(params[:q])
@names = @q.result(distinct: true).page(params[:page])
で検索を行ったときに、カラムごとに「山田」と「サッカー」を探しに行くので、もちろんヒットしません。

■やりたいことは、nameカラムで「山田」がヒットし、hobbyカラムで「サッカー」がヒットさせたいです。

■ransackerやカラム結合検索したのですが、いまも解決できていません。

なにかヒントでもおかしい点でもありましたら、ご連絡ください。
宜しくお願い致します。

【コードを追記致しました】
scaffoldでnameとpostを作成いたしました。
rails g scaffold name name:string age:integer hobby:string
rails g scaffold post content:text

下記、お答えいただく前のコードです。

■nameビューのindex.html.erb
検索フォームと結果表示

Ruby

1<%= search_form_for @q do |f| %> 2 <%= f.label :name_or_age_or_hobby_or_posts_content_cont_any, "検索" %> 3 <%= f.search_field :name_or_age_or_hobby_or_posts_content_cont_any %> 4<%= f.submit "Search" %> 5<% end %> 6#thead省略 7<tbody> 8 <% @names.each do |name| %> 9 <tr> 10 <td><%= name.name %></td> 11 <td><%= name.age %></td> 12 <td><%= name.hobby %></td> 13 <td><%= link_to 'Show', name %></td> 14 <td><%= link_to 'Edit', edit_name_path(name) %></td> 15 <td><%= link_to 'Destroy', name, method: :delete, data: { confirm: 'Are you sure?' } %></td> 16 </tr> 17 <% end %> 18</tbody>

■モデルname.rb

Ruby

1class Name < ApplicationRecord 2 has_many :posts 3 4 private 5 ransacker :age do 6 Arel.sql("CAST(names.age AS CHAR(8))") 7 end 8end

■モデルpost.rb

Ruby

1class Post < ApplicationRecord 2 belongs_to :name 3end

■nameコントローラーindexアクション

Ruby

1def index 2 if params[:q] == nil #読み込み時処理 3 @q = Name.includes(:posts).ransack(params[:q]) 4 @names = @q.result(distinct: true).page(params[:page]) 5 else #検索時の処理 6 params[:q][:name_or_age_or_hobby_or_posts_content_cont_any] = params[:q][:name_or_age_or_hobby_or_posts_content_cont_any].split(/[[:space:]]/) 7 #ransack検索 8 @q = Name.includes(:posts).ransack(params[:q]) 9 @names = @q.result(distinct: true).page(params[:page]) 10 #splitした配列をjoin 11 params[:q][:name_or_age_or_hobby_or_posts_content_cont_any] = params[:q][:name_or_age_or_hobby_or_posts_content_cont_any].join(' '); 12 end 13end

お答えいただきましたコードを自分なりに、想定されるところに挿入してみたのですが、
良い結果が得られず、頓挫してしまいました。

お答えいただきましたコードを利用する場合、splitやjoinにも工夫がやはり必要になりますか?

大変恐縮でございますが、宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

認識が異なっていたのとコードを記載したいためコメント返信ではなく別途回答します

「山田」だけ持っていてはNG、「サッカー」だけ持っていてもNG。
複数ワードでAND検索を仕掛けたいです。

つまり以下のようなANDを含めた構造が求められるわけでORだけでは実現できず
また左辺が一つである Ransackの基本記述では表現できないと思います

ruby

1name_or_age_or_hobby_or_posts_content_cont_any: '山田' 2AND 3name_or_age_or_hobby_or_posts_content_cont_any: 'サッカー'

解決案

  1. 上記構造を AND で繋がる Relation.merge を使って組み立てる
  2. 自前の検索スコープを設定する ransackable_scopes を使う

ruby

1class Name < ApplicationRecord 2 scope :by_any_texts, (lambda do |string| 3 string.split(' ').inject(self) do |scope, text| 4 query = search(name_or_age_or_hobby_or_posts_content_cont_any: text).result 5 scope.merge(query) 6 end 7 end) 8 9 def self.ransackable_scopes(_user = nil) 10 %i[by_any_texts] 11 end 12end

erb

1<%= f.search_field :by_any_texts &>

投稿2018/11/16 06:28

Ighrs

総合スコア656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

say4hatii8

2018/11/16 08:28

ありがとうございます。しかし、頓挫してしまいました。 質問にコードを記述させていただきましたので、ご確認宜しくお願い致します。
Ighrs

2018/11/16 09:08 編集

search は ransack 関数の別名でした、わかりにくくてすみません また2つのことを言っているとわかりにくくなるので複数ワード検索のみに絞ります :name_or_age_or_hobby_or_posts_content_cont_anyをひとつだけ渡す方法では実現できません なので params[:q] をそのままRansackに渡す方法は諦めてください texts = params[:q][:name_or_age_or_hobby_or_posts_content_cont_any].split(/[[:space:]]/) # ワードの数だけ条件を作る # 「山田がどれかに含まれる」「サッカーがどれかに含まれる」 text_queries = texts.map do |text| Name.includes(:posts).ransack(name_or_age_or_hobby_or_posts_content_cont_any: text).result(distinct: true) end # 「山田がどれかに含まれる」AND「サッカーがどれかに含まれる」という形を作る @names = text_queries.inject(Name.includes(:posts)) { |scope, query| scope.merge(query) } @names = @names.page(params[:page])
say4hatii8

2018/11/19 02:43

返信遅れて誠に申し訳ありません。 うまくいきました!!!(心の中で、すごい!の連呼です。) 感動致しました、と同時にさらに学習意欲がわきました。 回答いただきましたコードで既に「params[:q][:name_or_age_or_hobby_or_posts_content_cont_any]」がstringになっていたので、joinの記述があるコードを1行削除いたしました。 また「(No Ransack::Search object was provided to search_form_for!)」というエラーが続いたので、 「@q = Name.includes(:posts).ransack(params[:q])」を 「@names = text_queries.inject~」の上に1行追記いたしました。 誠にありがとうございました。
say4hatii8

2018/12/01 12:37

追記です。 今回、nameとpostで1対多の関係なのに、nameを検索対象にしていたことがまた失敗でした。 改良して、postを検索対象にして、nameを紐付けることにしました。 ありがとうございました。
guest

0

どれかに当たれば良いのであれば _all ではなく _any ではないでしょうか?

ruby

1f.search_field :name_or_age_or_hobby_or_posts_content_cont_any

投稿2018/11/16 03:10

Ighrs

総合スコア656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

say4hatii8

2018/11/16 04:25

回答ありがとうございます。 また質問の説明が足りませんでした。申し訳ありません。 「山田 サッカー」と検索したら、 この二つの語句、両方を持っているレコードを取得したいです。 ひとつのカラム内で「山田」と「サッカー」を持っているのではなく、 レコード(関連モデルも含めて)全体で「山田」と「サッカー」を持っているものを取得したいです。 「山田」だけ持っていてはNG、「サッカー」だけ持っていてもNG。 レコードの中のカラムの壁を取っ払って、 イメージとしては、データを全部繋げて、1行にして、 そこに対して、複数ワードでAND検索を仕掛けたいです。 仰る通り、「:name_or_age_or_hobby_or_posts_content_cont_any」でとりあえずたたき上げとして何かしら引っ掛かるようにしています。 宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問