Skip to content

Commit

Permalink
Merge pull request #57 from sascha-karnatz/rearrange-page-search
Browse files Browse the repository at this point in the history
Rearrange page search
  • Loading branch information
tvdeyen authored Oct 17, 2024
2 parents 7418988 + 8a19294 commit de265fe
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 296 deletions.
30 changes: 0 additions & 30 deletions app/extensions/alchemy/pg_search/ingredient_extension.rb

This file was deleted.

14 changes: 0 additions & 14 deletions app/extensions/alchemy/pg_search/pg_search_document_extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ module Alchemy::PgSearch::PgSearchDocumentExtension
def self.prepended(base)
base.belongs_to :page, class_name: "::Alchemy::Page", foreign_key: "page_id", optional: true
end

##
# get a list of excerpts of the searched phrase
# The JSON_AGG - method will transform the grouped content entries into json which have to be "unpacked".
# @return [array<string>]
def excerpts
return [] if content.blank?
begin
parsed_content = JSON.parse content
parsed_content.kind_of?(Array) ? parsed_content : []
rescue JSON::ParserError
[]
end
end
end

PgSearch::Document.prepend(Alchemy::PgSearch::PgSearchDocumentExtension)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Alchemy::PgSearch::ElementExtension
module Alchemy::Search::ElementExtension
def self.prepended(base)
base.attr_writer :searchable
end
Expand All @@ -12,4 +12,4 @@ def searchable?
end
end

Alchemy::Element.prepend(Alchemy::PgSearch::ElementExtension)
Alchemy::Element.prepend(Alchemy::Search::ElementExtension)
14 changes: 14 additions & 0 deletions app/extensions/alchemy/search/ingredient_extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Alchemy::Search::IngredientExtension
def searchable_content
send(Alchemy.searchable_ingredients[type.to_sym])&.squish
end

def searchable?
Alchemy.searchable_ingredients.has_key?(type.to_sym) &&
(definition.key?(:searchable) ? definition[:searchable] : true) &&
!!element&.searchable?
end
end

# add the PgSearch model to all ingredients
Alchemy::Ingredient.prepend(Alchemy::Search::IngredientExtension)
10 changes: 10 additions & 0 deletions app/extensions/alchemy/search/page_extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Enable Postgresql full text indexing.
#
module Alchemy::Search::PageExtension
def searchable?
(definition.key?(:searchable) ? definition[:searchable] : true) &&
searchable && public? && !layoutpage?
end
end

Alchemy::Page.prepend(Alchemy::Search::PageExtension)
10 changes: 2 additions & 8 deletions app/views/alchemy/search/_result.html.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
<li class="search_result">
<% page = result.page %>
<% page = result.searchable %>
<h3><%= link_to page.name, show_alchemy_page_path(page) %></h3>
<% if result.excerpts.any? %>
<% result.excerpts.each do |excerpt| %>
<p><%= highlighted_excerpt(excerpt, params[:query]) %></p>
<% end %>
<% else %>
<p><%= page.meta_description %></p>
<% end %>
<p><%= highlighted_excerpt(result.content, params[:query]) %></p>
<p><%= link_to page.urlname, show_alchemy_page_path(page) %></p>
</li>
57 changes: 31 additions & 26 deletions lib/alchemy-pg_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@ module Alchemy
mattr_accessor :search_class
@@search_class = PgSearch

module PgSearch
SEARCHABLE_INGREDIENTS = %w[Text Richtext Picture]
mattr_accessor :searchable_ingredients
@@searchable_ingredients = {
"Alchemy::Ingredients::Text": :value,
"Alchemy::Ingredients::Headline": :value,
"Alchemy::Ingredients::Richtext": :stripped_body,
"Alchemy::Ingredients::Picture": :caption,
}

module PgSearch
extend Config

##
# is ingredient searchable?
# @param ingredient_type [string]
# @return [boolean]
def self.is_searchable?(ingredient_type)
SEARCHABLE_INGREDIENTS.include?(ingredient_type.gsub(/Alchemy::Ingredients::/, ""))
end

##
# index all supported Alchemy models
# index all supported Alchemy pages
def self.rebuild
[Alchemy::Page, Alchemy::Ingredient].each do |model|
::PgSearch::Multisearch.rebuild(model)
ActiveRecord::Base.transaction do
::PgSearch::Document.delete_all
Alchemy::Page.all.each{ |page| index_page(page) }
end
end

Expand All @@ -39,14 +38,16 @@ def self.remove_page(page)
#
# @param page [Alchemy::Page]
def self.index_page(page)
remove_page page

page.update_pg_search_document
page.all_elements.includes(:ingredients).find_each do |element|
element.ingredients.select { |i| Alchemy::PgSearch.is_searchable?(i.type) }.each do |ingredient|
ingredient.update_pg_search_document
end
end

document = page.pg_search_document
return if document.nil?

ingredient_content = page.all_elements.includes(ingredients: {element: :page}).map do |element|
element.ingredients.select { |i| i.searchable? }.map(&:searchable_content).join(" ")
end.join(" ")

document.update_column(:content, "#{document.content} #{ingredient_content}".squish)
end

##
Expand All @@ -56,13 +57,17 @@ def self.index_page(page)
# @param ability [nil|CanCan::Ability]
# @return [ActiveRecord::Relation]
def self.search(query, ability: nil)
query = ::PgSearch.multisearch(query)
.select("JSON_AGG(content) as content", :page_id)
.reorder("")
.group(:page_id)
.joins(:page)
query = ::PgSearch.multisearch(query).includes(:searchable)

query = query.merge(Alchemy::Page.accessible_by(ability, :read)) if ability
if ability
# left_joins method is not usable here, because the order of the joins are incorrect
# and would result in a SQL error. We can receive the correct query order with these
# odd left join string
# Ref: https://guides.rubyonrails.org/active_record_querying.html#using-a-string-sql-fragment
query = query
.joins("LEFT JOIN alchemy_pages ON alchemy_pages.id = pg_search_documents.page_id")
.merge(Alchemy::Page.accessible_by(ability, :read))
end

query
end
Expand Down
4 changes: 2 additions & 2 deletions spec/dummy/config/alchemy/elements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
searchable: false
- role: public
type: Richtext
default: "This is some public text."
default: "This is some <i>public</i> richtext."
- role: confidential
type: Richtext
searchable: false
default: "This is some confidential text."
default: "This is some <i>confidential</i> richtext."
- role: image
type: Picture
- role: secret_image
Expand Down
6 changes: 6 additions & 0 deletions spec/dummy/config/alchemy/page_layouts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
- article
- secrets

- name: mixed
elements:
- mixed
autogenerate:
- mixed

- name: search
searchresults: true
unique: true
Expand Down
13 changes: 9 additions & 4 deletions spec/features/fulltext_search_feature_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@
image.save
end

before do
Alchemy::PgSearch.rebuild
end

it "displays search results from text ingredients" do
visit("/suche?query=search")
visit("/suche?query=headline")
within(".search_results") do
expect(page).to have_content("This is a headline everybody should be able to search for.")
end
end

it "displays search results from richtext essences" do
visit("/suche?query=search")
it "displays search results from richtext ingredient" do
visit("/suche?query=text%20block")
within(".search_results") do
expect(page).to have_content("This is a text block everybody should be able to search for.")
end
Expand Down Expand Up @@ -59,7 +63,6 @@
it "does not display results placed on global pages" do
# A layout page is configured and the page is indexed after publish
public_page.update!(layoutpage: true)
Alchemy::PgSearch.index_page public_page

visit("/suche?query=search")
expect(page).to have_css("h2.no_search_results")
Expand Down Expand Up @@ -131,6 +134,7 @@

before do
nested_element.ingredient_by_role("headline").update!({ value: "Content from nested element" })
Alchemy::PgSearch.rebuild
end

it "displays search results from nested elements" do
Expand Down Expand Up @@ -184,6 +188,7 @@
page_version: create(:alchemy_page, :public).public_version,
)
end
Alchemy::PgSearch.rebuild
end

context "when default config is used" do
Expand Down
Loading

0 comments on commit de265fe

Please sign in to comment.