問題一覧をDBに取り組む
手始めに1000問くらいあるExcelファイルを用意した。
ExcelファイルからDBにとりくむ方法を調べた。
https://qiita.com/mmmasuke/items/545afaf5876d3dc52670
このサイトの通りにCSVに保存し直してみる
rails aborted!
NameError: uninitialized constant CSV
/Users/kubomasato/projects/search_app/db/seeds.rb:1:in `<main>'
/Users/kubomasato/projects/search_app/bin/rails:9:in `<top (required)>'
/Users/kubomasato/projects/search_app/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'
Tasks: TOP => db:seed
require "csv"
を入れ損ねていた。これを入力して再度実行
問題一覧の一挙登録ができた。
次は〜ページ以上、〜ページ未満の範囲を選択できるようにする
以下、以上だけであれば実装したことがあるが、今回のような範囲検索はしたことがないので、検索。
https://qiita.com/nojinoji/items/e1b174220da8c81a1756
このサイトが参考になりそう。
(このサイトに記載されているソート機能も覚えておきたい。前回苦戦したランダム並び替えに使えそう)
今のところ検索ふぉーむはこうなっている
ただこのままでは、定期テスト時に
地理は121〜150
歴史は170〜200
などというテスト範囲時にいちいち地理と歴史の問題一覧を出力する必要がある。
これでは(まだ未実装だが)印刷する際など余計な紙が出てきてしまう。
もちろん分けて出力したい人もいるだろうから、どちらもできるようにしたい。
そもそも、地理と歴史で複数の条件になる。
だから
地理で121〜150 or 歴史 170 〜200 で検索できるか。
を調べる必要がある。
だから「地理 && 121~150」||「歴史 && 170〜200」みたいな処理をしたい
ransackではand検索になっているはずなので
or検索をする必要がある。
「ransuck or検索」で検索。
http://j-ogawa.hatenadiary.jp/entry/2014/03/25/000334
こちらのサイトがやりたいことと同じことをやろうとしているんだけど...
User.search(
m: 'or',
g: {
'0' => { m: 'or', power_gteq: 100, magic_gteq: 30 },
'1' => { m: 'and', level_gteq: 20, hp_lteq: 30 }
}
).result
...見たことない記述だ?
保留にしつつ、別のサイトへ
http://nekorails.hatenablog.com/entry/2017/05/31/173925
q = {name_eq: "ほげ"}
Product.ransack(q).result.to_sql
=> "SELECT `products`.* FROM `products` WHERE `products`.`name` = 'ほげ'"
q = {
"c" => {
"0" => {
"a" => { "0" => { "name" => "name" } },
"p" => "eq",
"v" => { "0" => { "value" => "ほげ" } }
}
}
}
Product.ransack(q).result.to_sql
=> "SELECT `products`.* FROM `products` WHERE `products`.`name` = 'ほげ'"
これだ。
アドバンストモードとやらを使っているらしい。
ビューにするとこうなるらしい。
# シンプルモード
<%= f.search_field :name_eq %>
# アドバンストモード
# conditions(条件)
<%= f.condition_fields do |c| %>
# attributes(属性)
<%= c.attribute_fields do |a| %>
<%= a.attribute_select %>
<% end %>
# predicate(述語)
<%= c.predicate_select %>
# values(値)
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
...何がどうなっているのかさっぱりだ。
その後の解説を読んでいく。
今、自分の記述は
def search_product
@p = Content.ransack(params[:q]) # 検索オブジェクトを生成
end
こうなっている。
この@pに何が入っているかの解説があった。
conditionが条件で
その中身にattribute=探すカラム
predicate=取り出す条件
value=入力された値
となっていることが確認できた。
と言うことは先ほどのやつはこうなるのか、
なるほど、今見ると、コメントアウトでしっかり書いてあったけど、このcとかaとかpとかvの意味が一応わかったぞ。
ビューファイルの方は...
うーん、この_selectって何しているんだ?
これだけでnameカラムから探すとか、eqを使うっていう意味になっていないはずなんだけど...
とりあえず、先に読み進める
シンプルとアドバンスで共存できるみたい。
記述がものすごく長くなりそうだからそれはいい。
or検索もできそう。
gでグループ分けするみたいだから
こんな感じにグループ分けするのかな?
科目数とかもユーザーが気軽増やせるようにしたいから後で考え直さないと
さて、処理的にできそうなのはいいけど、
ビューファイルの件はまだ全くわかっていないので、
続き
Product.ransack(name_eq: "ほげ", id_eq: 1).base.conditions
=> [Condition <attributes: ["name"], predicate: eq, values: ["ほげ"]>, Condition <attributes: ["id"], predicate: eq, values: [1]>]
q = {
"c" => {
"0" => {
"a" => { "0" => { "name" => "name" } },
"p" => "eq",
"v" => { "0" => { "value" => "ほげ" } },
},
"1" => {
"a" => { "0" => { "name" => "id" } },
"p" => "eq",
"v" => { "0" => { "value" => 1 } },
}
}
}
Product.ransack(q).base.conditions
=> [Condition <attributes: ["name"], predicate: eq, values: ["ほげ"]>, Condition <attributes: ["id"], predicate: eq, values: [1]>]
c(条件)が2つ以上ある時は上のように
0,1で区切るみたい。
なるほど先ほどの
User.search(
m: 'or',
g: {
'0' => { m: 'or', power_gteq: 100, magic_gteq: 30 },
'1' => { m: 'and', level_gteq: 20, hp_lteq: 30 }
}
).result
こちら様も
グループ:0
パワーが100以上、もしくは、マジックが30以上
グループ:1
レベルが20以上、かつ、HPが30以下
で、グループ0もしくはグループ1になっているわけかな
(p100↑ or m30↑) or (lv20↑ and hp30↓)
あれ、最初のなんで「or」なんだろう。andの間違いかな、
何しているかわかったはいいけど
やっぱりビューのほうはわかっていない
# index.html.erb
<%= search_form_for @search do |f| %>
# conditions
# conditionsやattributesのようにコレクションになるものに対しては、`f.condition_fields`などの`f.*_fields`を利用してね。
# params[:q][:c]["0"]に対応するよ("0"はコレクションの連番の1つ目を表すよ。)
<%= f.condition_fields do |c| %>
# attributes
# params[:q][:c]["0"][:a]に対応するよ。
<%= c.attribute_fields do |a| %>
# 属性のセレクトボックスだよ。
# `id`などの全ての属性が選択可能だよ。
# params[:q][:c]["0"][:a]["0"][:name]に対応するよ。
<%= a.attribute_select %>
<% end %>
# predicate
# 述語のセレクトボックスだよ。
# `eq`などの全ての述語が選択可能だよ。
# params[:q][:c]["0"][:p]に対応するよ。
<%= c.predicate_select %>
# values
# params[:q][:c]["0"][:v]に対応するよ。
<%= c.value_fields do |v| %>
# 値のサーチフィールドだよ。
# params[:q][:c]["0"][:v]["0"]["value"]に対応するよ。
<%= v.search_field :value %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
・・・セレクトボックスやサーチフィールドになるのはいいんだけど、そのほかの記述方法が書いていないのかな。
とりあえずいじってみるか
<%= search_form_for @p, url: products_search_path do |f| %>
<%= f.label :question_cont, '問題文検索' %>
<%= f.search_field :question_cont, class:"items-search-text", placeholder:"問題文に含まれている文字を検索" %>
<br>
今、最初の問題文検索はこうなっているから、
同じようにして表示されるかどうかやってみよう。
<%= search_form_for @p, url: products_search_path do |f| %>
<%# conditions %>
<%= f.condition_fields do |c| %>
<%# attributes %>
<%# <%= c.attribute_fieldsd do |a| %>
<%= a.attribute :question %>
<%# <% end %>
<%# predicate %>
<%= c.predicate :_cont %>
<%# values %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
・・・まあでないよね。
でもエラーすら吐かないのはなんでだろう。
なーんも表示されないのもなんでだろう。
def index
@search = Product.ransack(params[:q])
@search.build_condition if @search.conditions.empty?
@products = @search.result
end
これをやっていなかったのでやってみる。
そうすると、ちゃんとエラーをはいてくれた。
とりあえずお手本通りに
<%= f.label :question_cont, '問題文検索' %>
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.attribute_select %>
<% end %>
<%= c.predicate_select %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
こうする。
gyazo.com
こうなる。
うん、お手本通り、
そうしたら、
conditionと
attributeは固定でいい。
固定の仕方を調べてみたけど
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :question %>
<% end %>
<%= c.hidden_field :p, value: "cont" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
こうしたら
f.search_field :question_cont
これと同じことができた。
classとかplaceholderはどこにつけるんだ?
よくわからんから、今思いつく作業である
<%= f.label :question_cont, '問題文検索' %>
<%= f.search_field :question_cont, class:"items-search-text", placeholder:"問題文に含まれている文字を検索" %>
<%= f.label :answer_cont, '解答検索' %>
<%= f.search_field :answer_cont, class:"items-search-text", placeholder:"解答に含まれている文字を検索" %>
<%= f.label :subject_eq, '科目:' %>
<%= f.collection_select :subject_eq, @product_subject, :subject, :subject, include_blank: '指定なし'%>
<%= f.label :page, 'ページ:' %>
<%= f.number_field :page_gteq %>
<%= f.number_field :page_lt %>
これをアドバンストに対応したものに変えてみる
<%= search_form_for @p, url: products_search_path do |f| %>
<%= f.label :question_cont, '問題文検索' %>
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :question %>
<% end %>
<%= c.hidden_field :p, value: "cont" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
<br>
<%= f.label :question_cont, '解答検索' %>
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :answer %>
<% end %>
<%= c.hidden_field :p, value: "cont" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
<br>
<%= f.label :subject_eq, '科目:' %>
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :subject %>
<% end %>
<%= c.hidden_field :p, value: "eq" %>
<%= c.value_fields do |v| %>
<%= v.collection_select :value, @product_subject, :subject, :subject %>
<% end %>
<% end %>
<%= f.label :page, 'ページ:' %>
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "qteq" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
から
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "lt" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
まで
こうしてみた。
みてくれはこうなる。
gyazo.com
相変わらずplaceholderがわからんけど、
本題を先に片付けていこう。
ここで、
現段階での検査構造を整理してみる
こうかな。
ここから、アドバンストに対応した記述に
グルーピングしていけばいいはず。
<%= search_form_for @p, url: products_search_path do |f| %>
<%# グループ1 問題文、解答検索 %>
<%= f.grouping_fields do |g| %>
<%= g.label :question_cont, '問題文検索' %>
<%= g.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :question %>
<% end %>
<%= c.hidden_field :p, value: "cont" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
<br>
<%# 解答 %>
<%= g.label :question_cont, '解答検索' %>
<%= g.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :answer %>
<% end %>
<%= c.hidden_field :p, value: "cont" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
<% end %>
<br>
<%# グループ2 科目ごと %>
<%= f.grouping_fields do |g| %>
<%# グループ2-1 地理 %>
<%= g.grouping_fields do |g1| %>
<%= g1.label :subject_eq, '科目:' %>
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :subject %>
<% end %>
<%= c.hidden_field :p, value: "eq" %>
<%= c.value_fields do |v| %>
<%= v.collection_select :value, @product_subject, :subject, :subject %>
<% end %>
<% end %>
<%= g1.label :page, 'ページ:' %>
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "qteq" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
から
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "lt" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
まで
<% end %>
<br>
<%# グループ2-2 歴史 %>
<%= g.grouping_fields do |g1| %>
<%= g1.label :subject_eq, '科目:' %>
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :subject %>
<% end %>
<%= c.hidden_field :p, value: "eq" %>
<%= c.value_fields do |v| %>
<%= v.collection_select :value, @product_subject, :subject, :subject %>
<% end %>
<% end %>
<%= g1.label :page, 'ページ:' %>
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "qteq" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
から
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "lt" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
まで
<% end %>
<br>
<%# グループ2-3 公民 %>
<%= g.grouping_fields do |g1| %>
<%= g1.label :subject_eq, '科目:' %>
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :subject %>
<% end %>
<%= c.hidden_field :p, value: "eq" %>
<%= c.value_fields do |v| %>
<%= v.collection_select :value, @product_subject, :subject, :subject %>
<% end %>
<% end %>
<%= g1.label :page, 'ページ:' %>
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "qteq" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
から
<%= g1.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.hidden_field :name, value: :page %>
<% end %>
<%= c.hidden_field :p, value: "lt" %>
<%= c.value_fields do |v| %>
<%= v.search_field :value %>
<% end %>
<% end %>
まで
<% end %>
<%# グループ2のOR検索の設定 %>
<%# <%= g.hidden_select :m, value: "or"%>
<%= g.hidden_field :m, value: "or" %>
<% end %>
なんだこの長さは・・・でもビューは
gyazo.com
良さそうだ
検索してみる
gyazo.com
できているといいなあ
ああ、エラーだあ
gyazo.com
なんだあこれは・・・
negativeが定義されていない?
どういうことだあ・・・