# Terminal
docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres
docker stop postgres
docker start postgres
brew install ctop && ctop
rails g model author name
rails g model publisher name
rails g model genre name
rails g scaffold books title author:belongs_to publisher:belongs_to genre:belongs_to
rails g migration add_index_to_tables
bundle add faker
bundle add pg_search
bundle add kaminari
# config/database.yml
development:
<<: *default
database: template_development
url: postgresql://postgres:postgres@localhost:5432/template_development
# db/seeds.rb
100.times do
Author.create(name: Faker::Book.author)
end
100.times do
Publisher.create(name: Faker::Book.author)
end
100.times do
Genre.create(name: Faker::Book.genre)
end
100.times do
[].tap do |array|
10_000.times do
time = Time.current
array << {
title: Faker::Book.title,
author_id: 1 + rand(100),
publisher_id: 1 + rand(100),
genre_id: 1 + rand(100),
updated_at: time,
created_at: time
}
end
Book.insert_all(array)
end
end
# 1_000_000.times do
# Book.create(
# title: Faker::Book.title,
# author_id: 1 + rand(100),
# publisher_id: 1 + rand(100),
# genre_id: 1 + rand(100)
# )
# end
# books_controller.rb
def index
@books = Book.search(params[:q])
.includes(:author, :publisher, :genre)
.page(params[:page])
.without_count
.per(8)
end
# models/book.rb
class Book < ApplicationRecord
belongs_to :author
belongs_to :publisher
belongs_to :genre
include PgSearch::Model
pg_search_scope :pg_search,
against: :title,
using: {
tsearch: { dictionary: 'english' }
},
associated_against: {
author: :name,
publisher: :name,
genre: :name
}
def self.search(query)
return all unless query.present?
# where(title: query)
# where("title ILIKE ?", "%#{query}%")
# where("to_tsvector('english', title) @@ plainto_tsquery('english', :q)", q: query)
# where("to_tsvector('english', title) @@ plainto_tsquery('english', :q)", q: query)
# .joins(:author).or(Book.where("authors.name ILIKE ?", "%#{query}%"))
# .joins(:publisher).or(Book.where("publishers.name ILIKE ?", "%#{query}%"))
# .joins(:genre).or(Book.where("genres.name ILIKE ?", "%#{query}%"))
pg_search(query)
end
end
# views/books/index.html.erb
<%= form_with url: books_path, method: :get do |form| %>
<div class='input-group'>
<%= form.text_field :q, value: params[:q], placeholder: 'Search', class: 'form-control' %>
<%= form.submit 'Search', class: 'btn btn-outline-secondary' %>
</div>
<% end %>
<table class='table'>
<thead>
<th>Title</th>
<th>Author</th>
<th>Publisher</th>
<th>Genre</th>
<th colspan=3>Actions</th>
</thead>
<tbody>
<% @books.each do |book| %>
<tr>
<td><%= highlight(book.title, query_array) %></td>
<td><%= highlight(book.author.name, query_array) %></td>
<td><%= highlight(book.publisher.name, query_array) %></td>
<td><%= highlight(book.genre.name, query_array) %></td>
<td><%= link_to "Show", book_path(book) %></td>
<td><%= link_to "Edit", edit_book_path(book) %></td>
<td><%= link_to "Delete", book_path(book), "data-turbo-method": :delete %></td>
</tr>
<% end %>
</tbody>
</table>
<%# paginate @books %>
<%= link_to_prev_page @books, 'Previous Page', class: 'btn btn-outline-primary' %>
<%= link_to_next_page @books, 'Next Page', class: 'btn btn-outline-primary' %>
# app/helpers/application_helper.rb
module ApplicationHelper
def query_array
params[:q].to_s.split(' ')
end
end
# db/migrate/XXXXXXXX_add_index_to_tables.rb
class AddIndexToTables < ActiveRecord::Migration[7.0]
def change
add_index :books, "to_tsvector('english', title)", using: :gin
add_index :authors, "to_tsvector('english', name)", using: :gin
add_index :publishers, "to_tsvector('english', name)", using: :gin
add_index :genres, "to_tsvector('english', name)", using: :gin
end
end