effective_search 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bec49baf4ca011a578402ee1ea21d618d1a4a5a9cd169f3ead349a19a8957d8
4
- data.tar.gz: 9feaacfd570da93b5ccc1d4dd5c11f13e372c4c71bbc4c338d9d8a69fffd6b1d
3
+ metadata.gz: 83d826d688fa3d81c054904cb345337677d2be1ba45d16114c774be17c4d7034
4
+ data.tar.gz: 51895c70ac58d0d5972e44e0cd97491924b8f537cbb32320ffec60705f3fc7cb
5
5
  SHA512:
6
- metadata.gz: 42cbc5f549c28d8f0ea19f6cb5b91e1175171e2518936aca2c89fabeb010cea5225e2e5a2f4c0698d74e4837258bba2d76626087717dfe6f13974c58ebfcd389
7
- data.tar.gz: d6a3bbe5359e1daa77e87227f781888146b05aefd8fef1a2abb521e5eb16a4c93b54e6b3adc0c124828ec752b32fe33577faee1ff320f3835d1d918aa971cafb
6
+ metadata.gz: 2c5a18018ad7a3501a00316107ddfb27d5d518081021e927b2a853ccc7cac7091f09b7401981284c2deb054e1131a2ab4e91e8a93a1085f14b1c9217ebbfa737
7
+ data.tar.gz: b4257f2cd361ba2a71d78966ec7cb779bfbcd2f24d7d599f9f42a47aff406df2dea3c63d2757538c7016a3d7a3b4f7e2b04c280fa44b59ff54779a0a046afef1
@@ -14,15 +14,21 @@ module Effective
14
14
  @search.search!
15
15
 
16
16
  if @search.present?
17
- @search_count = @search.results.limit(nil).offset(nil).count
17
+ @search_page = EffectiveResources.normalize_page(params[:page])
18
+ raise ActiveRecord::RecordNotFound, "Page #{params[:page].inspect} is invalid" unless @search_page
18
19
 
19
- @search_page = EffectiveResources.validate_page!(
20
- params[:page],
21
- collection_count: @search_count,
22
- per_page: @search.per_page
23
- )
20
+ results = @search.results(page: @search_page)
21
+ .limit(@search.per_page + 1)
22
+ .with_pg_search_highlight
23
+ .load
24
+ .to_a
24
25
 
25
- @page_title = "Search results for ‘#{@search}’".html_safe
26
+ raise ActiveRecord::RecordNotFound, "Page #{@search_page} does not exist" if @search_page > 1 && results.empty?
27
+
28
+ @search_has_next_page = (results.length > @search.per_page)
29
+ @search_results = results.first(@search.per_page)
30
+
31
+ @page_title = "Search results for ‘#{@search}’"
26
32
  else
27
33
  @page_title = "Search"
28
34
  end
@@ -1,5 +1,37 @@
1
1
  module EffectiveSearchHelper
2
2
 
3
+ def effective_search_paginate(page:, has_next_page:, url: nil)
4
+ return unless page > 1 || has_next_page
5
+
6
+ uri = URI(url || request.fullpath)
7
+ query = Rack::Utils.parse_nested_query(uri.query)
8
+ path = uri.path + '?'
9
+
10
+ previous = content_tag(:li, class: ['page-item', ('disabled' if page <= 1)].compact.join(' ')) do
11
+ if page > 1
12
+ link_to('Previous', path + query.merge('page' => page - 1).to_query,
13
+ class: 'page-link', 'aria-label': 'Previous', title: 'Previous', rel: 'prev'
14
+ )
15
+ else
16
+ content_tag(:span, 'Previous', class: 'page-link', 'aria-disabled': 'true')
17
+ end
18
+ end
19
+
20
+ following = content_tag(:li, class: ['page-item', ('disabled' unless has_next_page)].compact.join(' ')) do
21
+ if has_next_page
22
+ link_to('Next', path + query.merge('page' => page + 1).to_query,
23
+ class: 'page-link', 'aria-label': 'Next', title: 'Next', rel: 'next'
24
+ )
25
+ else
26
+ content_tag(:span, 'Next', class: 'page-link', 'aria-disabled': 'true')
27
+ end
28
+ end
29
+
30
+ content_tag(:nav, class: 'd-flex justify-content-center', 'aria-label': 'Search results pages') do
31
+ content_tag(:ul, safe_join([previous, following]), class: 'pagination')
32
+ end
33
+ end
34
+
3
35
  def effective_search_nav_item
4
36
  if EffectiveResources.authorized?(self, :index, EffectiveSearch.Search)
5
37
  render('effective/search/form_nav_item')
@@ -6,6 +6,8 @@
6
6
  module EffectiveSearchSearch
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ MAX_LENGTH = 200
10
+
9
11
  module ClassMethods
10
12
  def effective_search_search?; true; end
11
13
  end
@@ -15,9 +17,22 @@ module EffectiveSearchSearch
15
17
 
16
18
  attr_accessor :current_user
17
19
  attr_accessor :view_context
18
-
19
20
  attr_accessor :term
20
- validates :term, length: { minimum: 3, allow_blank: true }
21
+
22
+ validate do
23
+ value = term.to_s
24
+
25
+ unless value.valid_encoding?
26
+ errors.add(:term, 'must use UTF-8 encoding')
27
+ next
28
+ end
29
+
30
+ next if value.blank?
31
+
32
+ errors.add(:term, 'contains an invalid null byte') if value.include?("\0")
33
+ errors.add(:term, :too_short, count: 3) if value.length < 3
34
+ errors.add(:term, :too_long, count: MAX_LENGTH) if value.length > MAX_LENGTH
35
+ end
21
36
  end
22
37
 
23
38
  def to_s
@@ -40,15 +55,14 @@ module EffectiveSearchSearch
40
55
  end
41
56
 
42
57
  def present?
43
- term.present?
58
+ valid? && term.to_s.present?
44
59
  end
45
60
 
46
61
  # Search and assigns the collection
47
62
  # Assigns the entire collection() if there are no search terms
48
63
  # Otherwise validate the search terms
49
64
  def search!
50
- @search_results = build_collection()
51
- @search_results = @search_results.none if present? && !valid?
65
+ @search_results = valid? ? build_collection() : PgSearch::Document.none
52
66
  @search_results
53
67
  end
54
68
 
@@ -120,7 +134,7 @@ module EffectiveSearchSearch
120
134
 
121
135
  protected
122
136
 
123
- # Returns an ActiveRecord collection of PgSearch::Document
137
+ # Returns an ActiveRecord collection of PgSearch::Document
124
138
  def build_collection
125
139
  raise('expected pg_search gem') unless defined?(PgSearch)
126
140
 
@@ -2,7 +2,6 @@
2
2
  = render('effective/search/form', search: search)
3
3
 
4
4
  - if search.present?
5
- - results = search.results(page: @search_page)
6
5
  - search_contents = search.search_contents.select { |resource| search.authorized?(resource) }
7
6
  - permalinks = search.permalinks.select { |resource| search.authorized?(resource) }
8
7
 
@@ -20,12 +19,11 @@
20
19
 
21
20
  -# Search results
22
21
  %div{class: (permalinks.present? ? 'col-xl-8 col-lg-7' : 'col')}
23
- - if results.length == 0
22
+ - if @search_results.empty?
24
23
  .alert.alert-info There are no results for your search. Please try again.
25
24
 
26
- - if results.length > 0
27
- - collection = results.with_pg_search_highlight.select { |result| search.authorized?(result) }
25
+ - if @search_results.any?
26
+ - collection = @search_results.select { |result| search.authorized?(result) }
28
27
  = render(collection: collection, partial: 'effective/search/result', locals: { search: search })
29
28
 
30
- %nav.d-flex.justify-content-center
31
- = bootstrap_paginate(results, per_page: search.per_page, collection_count: @search_count)
29
+ = effective_search_paginate(page: @search_page, has_next_page: @search_has_next_page)
@@ -0,0 +1,38 @@
1
+ class UpgradeEffectiveSearchZeroEight < ActiveRecord::Migration[7.0]
2
+ disable_ddl_transaction!
3
+
4
+ def up
5
+ return unless connection.adapter_name == 'PostgreSQL'
6
+
7
+ unless column_exists?(:pg_search_documents, :search_vector)
8
+ execute(<<~SQL.squish)
9
+ ALTER TABLE pg_search_documents
10
+ ADD COLUMN search_vector tsvector
11
+ GENERATED ALWAYS AS (
12
+ to_tsvector('simple'::regconfig, coalesce(content, ''::text))
13
+ ) STORED
14
+ SQL
15
+ end
16
+
17
+ add_index(
18
+ :pg_search_documents,
19
+ :search_vector,
20
+ using: :gin,
21
+ algorithm: :concurrently,
22
+ if_not_exists: true
23
+ )
24
+ end
25
+
26
+ def down
27
+ return unless connection.adapter_name == 'PostgreSQL'
28
+
29
+ remove_index(
30
+ :pg_search_documents,
31
+ :search_vector,
32
+ algorithm: :concurrently,
33
+ if_exists: true
34
+ )
35
+
36
+ remove_column(:pg_search_documents, :search_vector) if column_exists?(:pg_search_documents, :search_vector)
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EffectiveSearch
4
+ module ActionTextSearchIndex
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ after_commit :refresh_pg_search_document, on: [:create, :update, :destroy]
9
+ end
10
+
11
+ private
12
+
13
+ def refresh_pg_search_document
14
+ searchable = record
15
+ return unless searchable&.persisted?
16
+ return unless searchable.respond_to?(:update_pg_search_document)
17
+
18
+ options = searchable.class.try(:pg_search_multisearchable_options)
19
+ return unless Array(options.try(:[], :against)).map(&:to_s).include?(name.to_s)
20
+ return if persisted? && previous_changes.exclude?('body')
21
+
22
+ association_name = "rich_text_#{name}"
23
+
24
+ if searchable.class.reflect_on_association(association_name)
25
+ searchable.association(association_name).reset
26
+ elsif searchable.class.reflect_on_association(:rich_texts)
27
+ searchable.association(:rich_texts).reset
28
+ end
29
+
30
+ searchable.update_pg_search_document
31
+ end
32
+ end
33
+ end
@@ -7,5 +7,11 @@ module EffectiveSearch
7
7
  eval File.read("#{config.root}/config/effective_search.rb")
8
8
  end
9
9
 
10
+ initializer 'effective_search.action_text' do
11
+ ActiveSupport.on_load(:action_text_rich_text) do
12
+ include EffectiveSearch::ActionTextSearchIndex
13
+ end
14
+ end
15
+
10
16
  end
11
17
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveSearch
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'effective_resources'
2
+ require 'effective_search/action_text_search_index'
2
3
  require 'effective_search/engine'
3
4
  require 'effective_search/version'
4
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
@@ -57,14 +57,14 @@ dependencies:
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: '0'
60
+ version: 2.42.0
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
- version: '0'
67
+ version: 2.42.0
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: sqlite3
70
70
  requirement: !ruby/object:Gem::Requirement
@@ -229,8 +229,10 @@ files:
229
229
  - config/effective_search.rb
230
230
  - config/routes.rb
231
231
  - db/migrate/101_create_effective_search.rb
232
+ - db/migrate/102_upgrade_effective_search_zero_eight.rb
232
233
  - db/seeds.rb
233
234
  - lib/effective_search.rb
235
+ - lib/effective_search/action_text_search_index.rb
234
236
  - lib/effective_search/engine.rb
235
237
  - lib/effective_search/version.rb
236
238
  - lib/generators/effective_search/install_generator.rb