FEATURE: Use PG ts_headline for highlighting topic title in search.

This commit is contained in:
Guo Xiang Tan 2020-08-07 12:43:09 +08:00
parent 1d2ba8fd52
commit e60c74d3c1
No known key found for this signature in database
GPG Key ID: FBD110179AAC1F20
10 changed files with 93 additions and 37 deletions

View File

@ -0,0 +1,18 @@
import Site from "discourse/models/site";
import { censor } from "pretty-text/censored-words";
import { emojiUnescape } from "discourse/lib/text";
import { isRTL } from "discourse/lib/text-direction";
export function fancyTitle(title, supportMixedTextDirection) {
let fancyTitle = censor(
emojiUnescape(title) || "",
Site.currentProp("censored_regexp")
);
if (supportMixedTextDirection) {
const titleDir = isRTL(title) ? "rtl" : "ltr";
return `<span dir="${titleDir}">${fancyTitle}</span>`;
}
return fancyTitle;
}

View File

@ -2,10 +2,7 @@ import getURL from "discourse-common/lib/get-url";
import I18n from "I18n";
import Category from "discourse/models/category";
import User from "discourse/models/user";
import { isRTL } from "discourse/lib/text-direction";
import { censor } from "pretty-text/censored-words";
import { emojiUnescape } from "discourse/lib/text";
import Site from "discourse/models/site";
import { fancyTitle } from "discourse/lib/topic-fancy-title";
import { longDate } from "discourse/lib/formatter";
import { none } from "@ember/object/computed";
import { computed } from "@ember/object";
@ -78,16 +75,7 @@ const Bookmark = RestModel.extend({
@discourseComputed("title")
fancyTitle(title) {
let fancyTitle = censor(
emojiUnescape(title) || "",
Site.currentProp("censored_regexp")
);
if (this.siteSettings.support_mixed_text_direction) {
const titleDir = isRTL(title) ? "rtl" : "ltr";
return `<span dir="${titleDir}">${fancyTitle}</span>`;
}
return fancyTitle;
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
},
@discourseComputed("created_at")

View File

@ -16,6 +16,7 @@ import { Promise } from "rsvp";
import Site from "discourse/models/site";
import User from "discourse/models/user";
import showModal from "discourse/lib/show-modal";
import { fancyTitle } from "discourse/lib/topic-fancy-title";
const Post = RestModel.extend({
@discourseComputed("url")
@ -102,6 +103,19 @@ const Post = RestModel.extend({
);
},
@discourseComputed(
"siteSettings.use_pg_headlines_for_excerpt",
"topic_title_headline"
)
useTopicTitleHeadline(enabled, title) {
return enabled && title;
},
@discourseComputed("topic_title_headline")
topicTitleHead(title) {
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
},
afterUpdate(res) {
if (res.category) {
this.site.updateCategory(res.category);

View File

@ -7,13 +7,12 @@ import { flushMap } from "discourse/models/store";
import RestModel from "discourse/models/rest";
import { propertyEqual, fmt } from "discourse/lib/computed";
import { longDate } from "discourse/lib/formatter";
import { isRTL } from "discourse/lib/text-direction";
import ActionSummary from "discourse/models/action-summary";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { censor } from "pretty-text/censored-words";
import { emojiUnescape } from "discourse/lib/text";
import PreloadStore from "discourse/lib/preload-store";
import { userPath } from "discourse/lib/url";
import { fancyTitle } from "discourse/lib/topic-fancy-title";
import discourseComputed, {
observes,
on
@ -119,16 +118,7 @@ const Topic = RestModel.extend({
@discourseComputed("fancy_title")
fancyTitle(title) {
let fancyTitle = censor(
emojiUnescape(title) || "",
Site.currentProp("censored_regexp")
);
if (this.siteSettings.support_mixed_text_direction) {
const titleDir = isRTL(title) ? "rtl" : "ltr";
return `<span dir="${titleDir}">${fancyTitle}</span>`;
}
return fancyTitle;
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
},
// returns createdAt if there's no bumped date

View File

@ -88,7 +88,15 @@
<a href={{result.url}} {{action "logClick" result.topic_id}} class="search-link">
{{topic-status topic=result.topic disableActions=true showPrivateMessageIcon=true}}
<span class="topic-title">{{#highlight-search highlight=q}}{{html-safe result.topic.fancyTitle}}{{/highlight-search}}</span>
<span class="topic-title">
{{#if result.useTopicTitleHeadline}}
{{html-safe result.topicTitleHead}}
{{else}}
{{#highlight-search highlight=q}}
{{html-safe result.topic.fancyTitle}}
{{/highlight-search}}
{{/if}}
</span>
</a>
<div class="search-category">

View File

@ -3,7 +3,19 @@
class SearchPostSerializer < BasicPostSerializer
has_one :topic, serializer: SearchTopicListItemSerializer
attributes :like_count, :blurb, :post_number
attributes :like_count, :blurb, :post_number, :topic_title_headline
def include_topic_title_headline?
if SiteSetting.use_pg_headlines_for_excerpt
object.topic_title_headline.present?
else
false
end
end
def topic_title_headline
object.topic_title_headline
end
def blurb
options[:result].blurb(object)

View File

@ -1796,6 +1796,7 @@ search:
use_pg_headlines_for_excerpt:
default: false
hidden: true
client: true
search_ranking_normalization:
default: "0"
hidden: true

View File

@ -1166,10 +1166,15 @@ class Search
def posts_scope(default_scope = Post.all)
if SiteSetting.use_pg_headlines_for_excerpt
search_term = @term.present? ? PG::Connection.escape_string(@term) : nil
ts_config = default_ts_config
default_scope
.joins("INNER JOIN post_search_data pd ON pd.post_id = posts.id")
.joins("INNER JOIN topics t1 ON t1.id = posts.topic_id")
.select(
"TS_HEADLINE(#{default_ts_config}, pd.raw_data, PLAINTO_TSQUERY(#{default_ts_config}, '#{@term.present? ? PG::Connection.escape_string(@term) : nil}'), 'ShortWord=0, MaxFragments=1, MinWords=50, MaxWords=51, StartSel=''<span class=\"#{HIGHLIGHT_CSS_CLASS}\">'', StopSel=''</span>''') AS headline",
"TS_HEADLINE(#{ts_config}, t1.fancy_title, PLAINTO_TSQUERY(#{ts_config}, '#{search_term}'), 'StartSel=''<span class=\"#{HIGHLIGHT_CSS_CLASS}\">'', StopSel=''</span>''') AS topic_title_headline",
"TS_HEADLINE(#{ts_config}, pd.raw_data, PLAINTO_TSQUERY(#{ts_config}, '#{search_term}'), 'ShortWord=0, MaxFragments=1, MinWords=50, MaxWords=51, StartSel=''<span class=\"#{HIGHLIGHT_CSS_CLASS}\">'', StopSel=''</span>''') AS headline",
default_scope.arel.projections
)
else

View File

@ -422,7 +422,11 @@ describe Search do
)
expect(result.posts.map(&:id)).to contain_exactly(reply.id)
expect(result.blurb(result.posts.first)).to eq(expected_blurb)
post = result.posts.first
expect(result.blurb(post)).to eq(expected_blurb)
expect(post.topic_title_headline).to eq(topic.fancy_title)
end
it 'returns the right post and blurb for searches with phrase' do

View File

@ -101,6 +101,10 @@ describe SearchController do
it "can search correctly" do
SiteSetting.use_pg_headlines_for_excerpt = true
awesome_post_3 = Fabricate(:post,
topic: Fabricate(:topic, title: 'this is an awesome title')
)
get "/search/query.json", params: {
term: 'awesome'
}
@ -109,14 +113,26 @@ describe SearchController do
data = response.parsed_body
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("this is my really <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">awesome</span> post")
expect(data['topics'][0]['id']).to eq(awesome_post_2.topic_id)
expect(data['posts'].length).to eq(3)
expect(data['posts'][1]['id']).to eq(awesome_post.id)
expect(data['posts'][1]['blurb']).to eq("this is my really <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">awesome</span> post")
expect(data['topics'][1]['id']).to eq(awesome_post.topic_id)
expect(data['posts'][0]['id']).to eq(awesome_post_3.id)
expect(data['posts'][0]['blurb']).to eq(awesome_post_3.raw)
expect(data['posts'][0]['topic_title_headline']).to eq(
"This is an <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">awesome</span> title"
)
expect(data['topics'][0]['id']).to eq(awesome_post_3.topic_id)
expect(data['posts'][1]['id']).to eq(awesome_post_2.id)
expect(data['posts'][1]['blurb']).to eq(
"this is my really <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">awesome</span> post"
)
expect(data['topics'][1]['id']).to eq(awesome_post_2.topic_id)
expect(data['posts'][2]['id']).to eq(awesome_post.id)
expect(data['posts'][2]['blurb']).to eq(
"this is my really <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">awesome</span> post"
)
expect(data['topics'][2]['id']).to eq(awesome_post.topic_id)
end
it "can search correctly with advanced search filters" do