diff --git a/config/site_settings.yml b/config/site_settings.yml
index 09b886ee5f..46699dcd6c 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -1792,6 +1792,9 @@ backups:
hidden: true
search:
+ use_pg_headlines_for_excerpt:
+ default: false
+ hidden: true
search_ranking_normalization:
default: "0"
hidden: true
diff --git a/lib/search.rb b/lib/search.rb
index 35adbf943c..efc870b465 100644
--- a/lib/search.rb
+++ b/lib/search.rb
@@ -2,6 +2,7 @@
class Search
DIACRITICS ||= /([\u0300-\u036f]|[\u1AB0-\u1AFF]|[\u1DC0-\u1DFF]|[\u20D0-\u20FF])/
+ HIGHLIGHT_CSS_CLASS = 'search-highlight'
cattr_accessor :preloaded_topic_custom_fields
self.preloaded_topic_custom_fields = Set.new
@@ -726,12 +727,18 @@ class Search
def single_topic(id)
if @opts[:restrict_to_archetype].present?
archetype = @opts[:restrict_to_archetype] == Archetype.default ? Archetype.default : Archetype.private_message
- post = Post.joins(:topic)
- .where("topics.id = :id AND topics.archetype = :archetype AND posts.post_number = 1", id: id, archetype: archetype)
- .first
+
+ post = posts_scope
+ .joins(:topic)
+ .find_by(
+ "topics.id = :id AND topics.archetype = :archetype AND posts.post_number = 1",
+ id: id,
+ archetype: archetype
+ )
else
- post = Post.find_by(topic_id: id, post_number: 1)
+ post = posts_scope.find_by(topic_id: id, post_number: 1)
end
+
return nil unless @guardian.can_see?(post)
@results.add(post)
@@ -1096,7 +1103,7 @@ class Search
def aggregate_posts(post_sql)
return [] unless post_sql
- posts_eager_loads(Post)
+ posts_scope(posts_eager_loads(Post))
.joins("JOIN (#{post_sql}) x ON x.id = posts.topic_id AND x.post_number = posts.post_number")
.order('row_number')
end
@@ -1128,7 +1135,7 @@ class Search
def topic_search
if @search_context.is_a?(Topic)
- posts = posts_eager_loads(posts_query(limit))
+ posts = posts_scope(posts_eager_loads(posts_query(limit)))
.where('posts.topic_id = ?', @search_context.id)
posts.each do |post|
@@ -1150,4 +1157,17 @@ class Search
query.includes(topic: topic_eager_loads)
end
+ def posts_scope(default_scope = Post.all)
+ if SiteSetting.use_pg_headlines_for_excerpt
+ default_scope
+ .joins("INNER JOIN post_search_data pd ON pd.post_id = posts.id")
+ .select(
+ "TS_HEADLINE(#{default_ts_config}, pd.raw_data, PLAINTO_TSQUERY('#{@term.present? ? PG::Connection.escape_string(@term) : nil}'), 'ShortWord=0, MaxFragments=1, MinWords=50, MaxWords=51, StartSel='''', StopSel=''''') AS headline",
+ default_scope.arel.projections
+ )
+ else
+ default_scope
+ end
+ end
+
end
diff --git a/lib/search/grouped_search_results.rb b/lib/search/grouped_search_results.rb
index ba112b5034..cac0c52897 100644
--- a/lib/search/grouped_search_results.rb
+++ b/lib/search/grouped_search_results.rb
@@ -85,8 +85,12 @@ class Search
}
if post.post_search_data.version > SearchIndexer::MIN_POST_REINDEX_VERSION
- opts[:cooked] = post.post_search_data.raw_data
- opts[:scrub] = false
+ if SiteSetting.use_pg_headlines_for_excerpt
+ return post.headline
+ else
+ opts[:cooked] = post.post_search_data.raw_data
+ opts[:scrub] = false
+ end
else
opts[:cooked] = post.cooked
end
diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb
index d0def198fb..5a7de3bf61 100644
--- a/spec/components/search_spec.rb
+++ b/spec/components/search_spec.rb
@@ -410,27 +410,31 @@ describe Search do
end
let(:expected_blurb) do
- "...quire content longer than the typical test post raw content. It really is some long content, folks. elephant"
+ "hundred characters to satisfy any test conditions that require content longer than the typical test post raw content. It really is some long content, folks. elephant"
end
it 'returns the post' do
+ SiteSetting.use_pg_headlines_for_excerpt = true
+
result = Search.execute('elephant',
type_filter: 'topic',
include_blurbs: true
)
- expect(result.posts).to contain_exactly(reply)
- expect(result.blurb(reply)).to eq(expected_blurb)
+ expect(result.posts.map(&:id)).to contain_exactly(reply.id)
+ expect(result.blurb(result.posts.first)).to eq(expected_blurb)
end
it 'returns the right post and blurb for searches with phrase' do
+ SiteSetting.use_pg_headlines_for_excerpt = true
+
result = Search.execute('"elephant"',
type_filter: 'topic',
include_blurbs: true
)
- expect(result.posts).to contain_exactly(reply)
- expect(result.blurb(reply)).to eq(expected_blurb)
+ expect(result.posts.map(&:id)).to contain_exactly(reply.id)
+ expect(result.blurb(result.posts.first)).to eq(expected_blurb)
end
it 'applies a small penalty to closed topic when ranking' do
diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb
index bb49a6ad22..7a54ced1a2 100644
--- a/spec/requests/search_controller_spec.rb
+++ b/spec/requests/search_controller_spec.rb
@@ -99,6 +99,8 @@ describe SearchController do
end
it "can search correctly" do
+ SiteSetting.use_pg_headlines_for_excerpt = true
+
get "/search/query.json", params: {
term: 'awesome'
}
@@ -109,11 +111,11 @@ describe SearchController do
expect(data['posts'].length).to eq(2)
expect(data['posts'][0]['id']).to eq(awesome_post_2.id)
- expect(data['posts'][0]['blurb']).to eq(awesome_post_2.raw)
+ expect(data['posts'][0]['blurb']).to eq("this is my really awesome post")
expect(data['topics'][0]['id']).to eq(awesome_post_2.topic_id)
expect(data['posts'][1]['id']).to eq(awesome_post.id)
- expect(data['posts'][1]['blurb']).to eq(awesome_post.raw)
+ expect(data['posts'][1]['blurb']).to eq("this is my really awesome post")
expect(data['topics'][1]['id']).to eq(awesome_post.topic_id)
end