mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -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 I18n from "I18n";
|
||||||
import Category from "discourse/models/category";
|
import Category from "discourse/models/category";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import { isRTL } from "discourse/lib/text-direction";
|
import { fancyTitle } from "discourse/lib/topic-fancy-title";
|
||||||
import { censor } from "pretty-text/censored-words";
|
|
||||||
import { emojiUnescape } from "discourse/lib/text";
|
|
||||||
import Site from "discourse/models/site";
|
|
||||||
import { longDate } from "discourse/lib/formatter";
|
import { longDate } from "discourse/lib/formatter";
|
||||||
import { none } from "@ember/object/computed";
|
import { none } from "@ember/object/computed";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
@ -78,16 +75,7 @@ const Bookmark = RestModel.extend({
|
|||||||
|
|
||||||
@discourseComputed("title")
|
@discourseComputed("title")
|
||||||
fancyTitle(title) {
|
fancyTitle(title) {
|
||||||
let fancyTitle = censor(
|
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("created_at")
|
@discourseComputed("created_at")
|
||||||
|
@ -16,6 +16,7 @@ import { Promise } from "rsvp";
|
|||||||
import Site from "discourse/models/site";
|
import Site from "discourse/models/site";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
import { fancyTitle } from "discourse/lib/topic-fancy-title";
|
||||||
|
|
||||||
const Post = RestModel.extend({
|
const Post = RestModel.extend({
|
||||||
@discourseComputed("url")
|
@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) {
|
afterUpdate(res) {
|
||||||
if (res.category) {
|
if (res.category) {
|
||||||
this.site.updateCategory(res.category);
|
this.site.updateCategory(res.category);
|
||||||
|
@ -7,13 +7,12 @@ import { flushMap } from "discourse/models/store";
|
|||||||
import RestModel from "discourse/models/rest";
|
import RestModel from "discourse/models/rest";
|
||||||
import { propertyEqual, fmt } from "discourse/lib/computed";
|
import { propertyEqual, fmt } from "discourse/lib/computed";
|
||||||
import { longDate } from "discourse/lib/formatter";
|
import { longDate } from "discourse/lib/formatter";
|
||||||
import { isRTL } from "discourse/lib/text-direction";
|
|
||||||
import ActionSummary from "discourse/models/action-summary";
|
import ActionSummary from "discourse/models/action-summary";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { censor } from "pretty-text/censored-words";
|
|
||||||
import { emojiUnescape } from "discourse/lib/text";
|
import { emojiUnescape } from "discourse/lib/text";
|
||||||
import PreloadStore from "discourse/lib/preload-store";
|
import PreloadStore from "discourse/lib/preload-store";
|
||||||
import { userPath } from "discourse/lib/url";
|
import { userPath } from "discourse/lib/url";
|
||||||
|
import { fancyTitle } from "discourse/lib/topic-fancy-title";
|
||||||
import discourseComputed, {
|
import discourseComputed, {
|
||||||
observes,
|
observes,
|
||||||
on
|
on
|
||||||
@ -119,16 +118,7 @@ const Topic = RestModel.extend({
|
|||||||
|
|
||||||
@discourseComputed("fancy_title")
|
@discourseComputed("fancy_title")
|
||||||
fancyTitle(title) {
|
fancyTitle(title) {
|
||||||
let fancyTitle = censor(
|
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// returns createdAt if there's no bumped date
|
// returns createdAt if there's no bumped date
|
||||||
|
@ -88,7 +88,15 @@
|
|||||||
|
|
||||||
<a href={{result.url}} {{action "logClick" result.topic_id}} class="search-link">
|
<a href={{result.url}} {{action "logClick" result.topic_id}} class="search-link">
|
||||||
{{topic-status topic=result.topic disableActions=true showPrivateMessageIcon=true}}
|
{{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>
|
</a>
|
||||||
|
|
||||||
<div class="search-category">
|
<div class="search-category">
|
||||||
|
@ -3,7 +3,19 @@
|
|||||||
class SearchPostSerializer < BasicPostSerializer
|
class SearchPostSerializer < BasicPostSerializer
|
||||||
has_one :topic, serializer: SearchTopicListItemSerializer
|
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
|
def blurb
|
||||||
options[:result].blurb(object)
|
options[:result].blurb(object)
|
||||||
|
@ -1796,6 +1796,7 @@ search:
|
|||||||
use_pg_headlines_for_excerpt:
|
use_pg_headlines_for_excerpt:
|
||||||
default: false
|
default: false
|
||||||
hidden: true
|
hidden: true
|
||||||
|
client: true
|
||||||
search_ranking_normalization:
|
search_ranking_normalization:
|
||||||
default: "0"
|
default: "0"
|
||||||
hidden: true
|
hidden: true
|
||||||
|
@ -1166,10 +1166,15 @@ class Search
|
|||||||
|
|
||||||
def posts_scope(default_scope = Post.all)
|
def posts_scope(default_scope = Post.all)
|
||||||
if SiteSetting.use_pg_headlines_for_excerpt
|
if SiteSetting.use_pg_headlines_for_excerpt
|
||||||
|
search_term = @term.present? ? PG::Connection.escape_string(@term) : nil
|
||||||
|
ts_config = default_ts_config
|
||||||
|
|
||||||
default_scope
|
default_scope
|
||||||
.joins("INNER JOIN post_search_data pd ON pd.post_id = posts.id")
|
.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(
|
.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
|
default_scope.arel.projections
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
@ -422,7 +422,11 @@ describe Search do
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(result.posts.map(&:id)).to contain_exactly(reply.id)
|
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
|
end
|
||||||
|
|
||||||
it 'returns the right post and blurb for searches with phrase' do
|
it 'returns the right post and blurb for searches with phrase' do
|
||||||
|
@ -101,6 +101,10 @@ describe SearchController do
|
|||||||
it "can search correctly" do
|
it "can search correctly" do
|
||||||
SiteSetting.use_pg_headlines_for_excerpt = true
|
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: {
|
get "/search/query.json", params: {
|
||||||
term: 'awesome'
|
term: 'awesome'
|
||||||
}
|
}
|
||||||
@ -109,14 +113,26 @@ describe SearchController do
|
|||||||
|
|
||||||
data = response.parsed_body
|
data = response.parsed_body
|
||||||
|
|
||||||
expect(data['posts'].length).to eq(2)
|
expect(data['posts'].length).to eq(3)
|
||||||
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'][1]['id']).to eq(awesome_post.id)
|
expect(data['posts'][0]['id']).to eq(awesome_post_3.id)
|
||||||
expect(data['posts'][1]['blurb']).to eq("this is my really <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">awesome</span> post")
|
expect(data['posts'][0]['blurb']).to eq(awesome_post_3.raw)
|
||||||
expect(data['topics'][1]['id']).to eq(awesome_post.topic_id)
|
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
|
end
|
||||||
|
|
||||||
it "can search correctly with advanced search filters" do
|
it "can search correctly with advanced search filters" do
|
||||||
|
Loading…
Reference in New Issue
Block a user