![見出し画像](https://assets.st-note.com/production/uploads/images/121923185/rectangle_large_type_2_2a34f6bf83e4a051e50bdb29da283a4b.png?width=1200)
RailsとMySQLを用いた全文検索機能付きQ&Aページの作成方法
※※※注意※※※
この記事はChatGPTにベースを書いてもらっています。
はじめに
この記事では、RailsとMySQLを使用して全文検索機能を備えたQ&Aページの作成方法について解説します。RailsとMySQLの基本的な構築手順については、私の以前の記事(RailsとMySQLの構築手順)を参考にしてください。
1. 環境構築
まず、RailsとMySQLの環境を整えます。上述の記事を参照し、RailsのセットアップとMySQLとの接続を確認してください。
***
2. Q&Aモデルの作成
Q&Aシステムの核となるモデル(例えば、QuestionとAnswer)を作成します。rails generate modelコマンドを使用して、必要な属性を持つモデルを作成します。
Questionモデルの作成
rails generate model Question title:string content:text
rails db:migrate
Questionモデルにインデックスを貼る
以下のコマンドで、title カラムのみに全文検索インデックスを追加するマイグレーションファイルを生成します。
rails generate migration AddFulltextIndexToQuestionTitle
このコマンドを実行すると、db/migrate ディレクトリに新しいマイグレーションファイルが作成されます。ファイル名は [timestamp]_add_fulltext_index_to_question_title.rb のようになります。
次に、生成されたマイグレーションファイルを編集して、title カラムに全文検索インデックスを追加します。以下はその編集後のマイグレーションの例です:
class AddFulltextIndexToQuestionTitle < ActiveRecord::Migration[6.0]
def change
add_index :questions, :title, type: :fulltext
end
end
最後に、マイグレーションを実行してインデックスをデータベースに適用します。
rails db:migrate
これで、questions テーブルの title カラムに全文検索用のインデックスが設定されます。
Answerモデルの作成
rails generate model Answer content:text question:references
rails db:migrate
[補足]
以前の記事を参考にした方は、上記コマンドを実行し、dockerのコンテナに入ってから上記コマンドを実行してください。
docker-compose exec web /bin/bash
コンテナから抜けるコマンドは下記です。
exit
***
3. ルーティングとコントローラの設定
Q&Aページにアクセスするためのルーティングを設定し、対応するコントローラーを作成します。routes.rbにリソースを追加して、コントローラーでアクションを定義します。
ルーティングの追加
config/routes.rbにQ&A機能に必要なルーティングを追加します。questionsリソースにanswersリソースをネストさせることで、質問に対する回答をグループ化します。
# config/routes.rb
Rails.application.routes.draw do
resources :questions do
resources :answers, only: [:create, :destroy]
end
# 全文検索用のルートも追加
get 'search', to: 'questions#search'
end
コントローラーの設定
QuestionsControllerとAnswersControllerを生成し、適切なアクションを追加します。全文検索を行うsearchアクションもQuestionsControllerに定義します。
# app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
def index
@questions = Question.all
end
# GET /questions/1
def show
end
# GET /questions/new
def new
@question = Question.new
end
# GET /questions/1/edit
def edit
end
# POST /questions
def create
@question = Question.new(question_params)
if @question.save
redirect_to @question, notice: '質問が正常に作成されました。'
else
render :new
end
end
# PATCH/PUT /questions/1
def update
if @question.update(question_params)
redirect_to @question, notice: '質問が正常に更新されました。'
else
render :edit
end
end
# DELETE /questions/1
def destroy
@question.destroy
redirect_to questions_url, notice: '質問が正常に削除されました。'
end
# GET /search
def search
@questions = Question.search(params[:search])
render :index
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
@question = Question.find(params[:id])
end
# Only allow a list of trusted parameters through.
def question_params
params.require(:question).permit(:title, :content)
end
end
# app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def create
@question = Question.find(params[:question_id])
@answer = @question.answers.create(answer_params)
redirect_to question_path(@question)
end
def destroy
@answer = Answer.find(params[:id])
@answer.destroy
redirect_to question_path(@answer.question)
end
private
def answer_params
params.require(:answer).permit(:content)
end
end
***
4. 全文検索機能の実装
MySQLの全文検索機能を利用して、Q&Aの検索機能を実装します。gem 'mysql2'がインストールされていることを確認し、モデルに検索メソッドを追加します。
# app/models/question.rb
class Question < ApplicationRecord
# タイトルに対する全文検索を行うメソッド
def self.search(search)
if search
where('MATCH(title) AGAINST(?)', search)
else
all
end
end
end
***
5. フロントエンドの設定
Q&Aページのフロントエンドを設定します。questions#indexをホームページとして設定し、検索フォームと質問一覧を表示します。
質問の一覧ページ
<!-- app/views/questions/index.html.erb -->
<h1>質問一覧</h1>
<%= link_to '新しい質問をする', new_question_path %>
<%= form_tag search_questions_path, method: :get do %>
<%= text_field_tag :search, params[:search], placeholder: "質問を検索" %>
<%= submit_tag "検索", name: nil %>
<% end %>
<% @questions.each do |question| %>
<div>
<h2><%= link_to question.title, question %></h2>
<p><%= truncate(question.content, length: 100) %></p>
</div>
<% end %>
[検索フォームの作成]
このフォームから送信されたキーワードは、searchアクションを通じて全文検索に使用されます。
<%= form_tag search_questions_path, method: :get do %>
<%= text_field_tag :search, params[:search], placeholder: "質問を検索" %>
<%= submit_tag "検索", name: nil %>
<% end %>
[質問一覧の表示]
質問一覧を@questions変数から取得し、それぞれの質問に対するリンクを表示します。
<% @questions.each do |question| %>
<h2><%= link_to question.title, question %></h2>
<p><%= question.content %></p>
<% end %>
質問の詳細ページ
<!-- app/views/questions/show.html.erb -->
<h1><%= @question.title %></h1>
<p><%= @question.content %></p>
<%= link_to '編集', edit_question_path(@question) %> |
<%= link_to '一覧に戻る', questions_path %>
<h2>回答</h2>
<%= render @question.answers %>
<%= form_for([@question, @question.answers.build]) do |f| %>
<p>
<%= f.label :content, "あなたの回答" %><br>
<%= f.text_area :content %>
</p>
<%= f.submit "回答する" %>
<% end %>
[詳細ページと回答フォームの追加]
質問の詳細ページには、質問の内容と、それに対する回答の一覧、回答するためのフォームを配置します。
新規質問作成ページ
<!-- app/views/questions/new.html.erb -->
<h1>新しい質問を作成する</h1>
<%= form_for(@question) do |f| %>
<% if @question.errors.any? %>
<div>
<h2><%= pluralize(@question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% @question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content %><br>
<%= f.text_area :content %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to '一覧に戻る', questions_path %>
質問の編集ページ
<!-- app/views/questions/edit.html.erb -->
<h1>質問を編集する</h1>
<%= form_for(@question) do |f| %>
<% if @question.errors.any? %>
<div>
<h2><%= pluralize(@question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% @question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content %><br>
<%= f.text_area :content %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to '詳細に戻る', @question %> |
<%= link_to '一覧に戻る', questions_path %>
回答の部分テンプレート
<!-- app/views/answers/_answer.html.erb -->
<div>
<p><%= answer.content %></p>
<% if answer.persisted? %>
<%= link_to '回答を削除する', [answer.question, answer], method: :delete, data: { confirm: '本当に削除しますか?' } %>
<% end %>
</div>
スタイルとインタラクションの追加
BootstrapやTailwindCSSのようなCSSフレームワークを使用して、UIを整えます。JavaScriptを用いてユーザーインタラクションを向上させることも検討しましょう。
***
6. テストとデプロイ
システムが正しく動作するかテストし、問題がなければデプロイします。
[補足]
正しく動作しない場合は、一度コンテナを再起動すると改善するかもしれません。
***
まとめ
この記事では、RailsとMySQLを用いた全文検索機能付きのQ&Aページの作成方法を紹介しました。この基本的なフレームワークを使用して、さらに機能を追加することもできます。
***
あとがき
ChatGPTに社内情報を学習させたいとの要望をもらったので、それに関する記事を書いてみました。
本当はベクトルデータベースとか使って構築するのがいいんでしょうけど、とっつきにくいですし…簡単に実装するならMySQLの全文検索を使った方がいいかなと思ったのでRails+MySQLの構成にしています。
いつかLLMの記事も書きたいです。
次は登録したQ&AをAPIで取得する記事を書きます!
その次はChatGPTのプロンプトにQ&A情報を持たせて回答させる、なんてこともやりたいなーと思ってます。
不明点などあればコメントください。
では、また次回。
お疲れ様でした!