From c14bae74d5e7da70bce2163c8c81d511ea4b1d03 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Sun, 22 Oct 2023 18:13:26 +0200 Subject: [PATCH] attempt to add pg_search this commit is an attempt at building a search functionality using only the stuff provided by postgresql it is, unfortunately, painfully slow: PgSearch::Document Load (7869.1ms) SELECT "pg_search_documents".* FROM "pg_search_documents" INNER JOIN (SELECT "pg_search_documents"."id" AS pg_search_id, (ts_rank((to_tsvector('simple', coalesce("pg_search_documents"."content"::text, ''))), (to_tsquery('simple', ''' ' || 'awoo' || ' ''')), 0)) AS rank FROM "pg_search_documents" WHERE ((to_tsvector('simple', coalesce("pg_search_documents"."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || 'awoo' || ' ''')))) AS pg_search_ce9b9dd18c5c0023f2116f ON "pg_search_documents"."id" = pg_search_ce9b9dd18c5c0023f2116f.pg_search_id ORDER BY pg_search_ce9b9dd18c5c0023f2116f.rank DESC, "pg_search_documents"."id" ASC LIMIT $1 [["LIMIT", 10]] --- Gemfile | 2 ++ Gemfile.lock | 4 ++++ app/assets/stylesheets/search.scss | 3 +++ app/controllers/search_controller.rb | 9 ++++++++ app/helpers/search_helper.rb | 2 ++ app/models/answer.rb | 3 +++ app/models/question.rb | 3 +++ app/views/search/index.haml | 23 +++++++++++++++++++ config/routes.rb | 2 ++ ...231022155815_create_pg_search_documents.rb | 17 ++++++++++++++ db/schema.rb | 12 +++++++++- spec/helpers/search_helper_spec.rb | 15 ++++++++++++ spec/requests/search_spec.rb | 11 +++++++++ spec/views/search/index.html.erb_spec.rb | 5 ++++ 14 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/search.scss create mode 100644 app/controllers/search_controller.rb create mode 100644 app/helpers/search_helper.rb create mode 100644 app/views/search/index.haml create mode 100644 db/migrate/20231022155815_create_pg_search_documents.rb create mode 100644 spec/helpers/search_helper_spec.rb create mode 100644 spec/requests/search_spec.rb create mode 100644 spec/views/search/index.html.erb_spec.rb diff --git a/Gemfile b/Gemfile index 63e2ae23..b6e3712b 100644 --- a/Gemfile +++ b/Gemfile @@ -118,3 +118,5 @@ gem "openssl", "~> 3.2" gem "mail", "~> 2.7.1" gem "prometheus-client", "~> 4.2" + +gem "pg_search", "~> 2.3" diff --git a/Gemfile.lock b/Gemfile.lock index c9dfd28f..6fdf5200 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -289,6 +289,9 @@ GEM ast (~> 2.4.1) racc pg (1.5.4) + pg_search (2.3.6) + activerecord (>= 5.2) + activesupport (>= 5.2) pghero (3.3.4) activerecord (>= 6) prometheus-client (4.2.1) @@ -542,6 +545,7 @@ DEPENDENCIES oj openssl (~> 3.2) pg + pg_search (~> 2.3) pghero prometheus-client (~> 4.2) puma diff --git a/app/assets/stylesheets/search.scss b/app/assets/stylesheets/search.scss new file mode 100644 index 00000000..1b26d4a3 --- /dev/null +++ b/app/assets/stylesheets/search.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the search controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb new file mode 100644 index 00000000..bdb9a9ca --- /dev/null +++ b/app/controllers/search_controller.rb @@ -0,0 +1,9 @@ +class SearchController < ApplicationController + def index + @results = [] + query = params[:q] + return if query.blank? + + @results = PgSearch.multisearch(query).limit(10) + end +end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb new file mode 100644 index 00000000..b3ce20ac --- /dev/null +++ b/app/helpers/search_helper.rb @@ -0,0 +1,2 @@ +module SearchHelper +end diff --git a/app/models/answer.rb b/app/models/answer.rb index 71417252..9f76153a 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -1,6 +1,9 @@ class Answer < ApplicationRecord extend Answer::TimelineMethods + include PgSearch::Model + multisearchable against: [:content] + belongs_to :user, counter_cache: :answered_count belongs_to :question, counter_cache: :answer_count has_many :comments, dependent: :destroy diff --git a/app/models/question.rb b/app/models/question.rb index 0a6adec0..0ecf14be 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -1,6 +1,9 @@ class Question < ApplicationRecord include Question::AnswerMethods + include PgSearch::Model + multisearchable against: [:content] + belongs_to :user, optional: true has_many :answers, dependent: :destroy has_many :inboxes, dependent: :destroy diff --git a/app/views/search/index.haml b/app/views/search/index.haml new file mode 100644 index 00000000..6f8280c9 --- /dev/null +++ b/app/views/search/index.haml @@ -0,0 +1,23 @@ +.container-lg.container--main + .row + .col-sm-10.col-md-10.col-lg-9.mx-auto + .card + .card-body + = bootstrap_form_tag layout: :inline, method: :get do |f| + = f.text_field :q, skip_label: true, append: f.primary("Search") + - unless @results.blank? + .container-lg.container--main + .row + .col-sm-10.col-md-10.col-lg-9.mx-auto + - @results.each do |result| + - case result.searchable + - when Answer + = render "answerbox", a: result.searchable, display_all: false, subscribed_answer_ids: [] + - when Question + = render "shared/question", q: result.searchable, type: nil + += render 'shared/links' + +:ruby + provide(:title, generate_title('Search')) + parent_layout 'base' diff --git a/config/routes.rb b/config/routes.rb index e5fcf899..e3c4f3a4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -148,6 +148,8 @@ Rails.application.routes.draw do post "/inbox/create", to: "inbox#create", as: :inbox_create get "/inbox", to: "inbox#show", as: :inbox + get "/search", to: "search#index" + get "/user/:username", to: "user#show" get "/@:username", to: "user#show", as: :user get "/@:username/a/:id", to: "answer#show", as: :answer diff --git a/db/migrate/20231022155815_create_pg_search_documents.rb b/db/migrate/20231022155815_create_pg_search_documents.rb new file mode 100644 index 00000000..62872191 --- /dev/null +++ b/db/migrate/20231022155815_create_pg_search_documents.rb @@ -0,0 +1,17 @@ +class CreatePgSearchDocuments < ActiveRecord::Migration[6.1] + def up + say_with_time("Creating table for pg_search multisearch") do + create_table :pg_search_documents do |t| + t.text :content + t.belongs_to :searchable, polymorphic: true, index: true + t.timestamps null: false + end + end + end + + def down + say_with_time("Dropping table for pg_search multisearch") do + drop_table :pg_search_documents + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ea268655..26d80e0f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_10_18_172518) do +ActiveRecord::Schema.define(version: 2023_10_22_155815) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -127,6 +127,15 @@ ActiveRecord::Schema.define(version: 2023_10_18_172518) do t.index ["type"], name: "index_notifications_on_type" end + create_table "pg_search_documents", force: :cascade do |t| + t.text "content" + t.string "searchable_type" + t.bigint "searchable_id" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["searchable_type", "searchable_id"], name: "index_pg_search_documents_on_searchable" + end + create_table "profiles", force: :cascade do |t| t.bigint "user_id" t.string "display_name" @@ -388,5 +397,6 @@ ActiveRecord::Schema.define(version: 2023_10_18_172518) do t.index ["user_id"], name: "index_web_push_subscriptions_on_user_id" end + add_foreign_key "anonymous_blocks", "users", column: "target_user_id" add_foreign_key "profiles", "users" end diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb new file mode 100644 index 00000000..74b6daf0 --- /dev/null +++ b/spec/helpers/search_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the SearchHelper. For example: +# +# describe SearchHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe SearchHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/search_spec.rb b/spec/requests/search_spec.rb new file mode 100644 index 00000000..5e3640ac --- /dev/null +++ b/spec/requests/search_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +RSpec.describe "Searches", type: :request do + describe "GET /index" do + it "returns http success" do + get "/search/index" + expect(response).to have_http_status(:success) + end + end + +end diff --git a/spec/views/search/index.html.erb_spec.rb b/spec/views/search/index.html.erb_spec.rb new file mode 100644 index 00000000..bdeb4dc2 --- /dev/null +++ b/spec/views/search/index.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "search/index.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end