mirror of
https://github.com/discourse/discourse.git
synced 2024-12-01 04:59:22 -06:00
FEATURE: Use PG ts_headline
for highlighting topic title in search.
This commit is contained in:
parent
1d2ba8fd52
commit
e60c74d3c1
@ -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;
|
||||
}
|
@ -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")
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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)
|
||||
|
@ -1796,6 +1796,7 @@ search:
|
||||
use_pg_headlines_for_excerpt:
|
||||
default: false
|
||||
hidden: true
|
||||
client: true
|
||||
search_ranking_normalization:
|
||||
default: "0"
|
||||
hidden: true
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user