diff --git a/app/assets/javascripts/discourse/components/composer-messages.js.es6 b/app/assets/javascripts/discourse/components/composer-messages.js.es6 index 4fd967ade22..8830fd19394 100644 --- a/app/assets/javascripts/discourse/components/composer-messages.js.es6 +++ b/app/assets/javascripts/discourse/components/composer-messages.js.es6 @@ -119,18 +119,15 @@ export default Ember.Component.extend({ // We don't care about similar topics unless creating a topic if (!composer.get('creatingTopic')) { return; } - const origBody = composer.get('reply') || ''; + // TODO pass the 200 in from somewhere + const raw = (composer.get('reply') || '').substr(0, 200); const title = composer.get('title') || ''; - // Ensure the fields are of the minimum length - if (origBody.length < Discourse.SiteSettings.min_body_similar_length) { return; } - if (title.length < Discourse.SiteSettings.min_title_similar_length) { return; } - - // TODO pass the 200 in from somewhere - const body = origBody.substr(0, 200); + // Ensure we have at least a title + if (title.length < this.siteSettings.min_title_similar_length) { return; } // Don't search over and over - const concat = title + body; + const concat = title + raw; if (concat === this._lastSimilaritySearch) { return; } this._lastSimilaritySearch = concat; @@ -143,14 +140,15 @@ export default Ember.Component.extend({ this._similarTopicsMessage = message; - composer.store.find('similar-topic', {title, raw: body}).then(newTopics => { + composer.store.find('similar-topic', { title, raw }).then(topics => { similarTopics.clear(); - similarTopics.pushObjects(newTopics.get('content')); + similarTopics.pushObjects(topics.get('content')); if (similarTopics.get('length') > 0) { message.set('similarTopics', similarTopics); this.send('popup', message); } else if (message) { + debugger this.send('hideMessage', message); } }); diff --git a/app/controllers/similar_topics_controller.rb b/app/controllers/similar_topics_controller.rb index 8070697e57c..e6249cb4d4f 100644 --- a/app/controllers/similar_topics_controller.rb +++ b/app/controllers/similar_topics_controller.rb @@ -16,25 +16,16 @@ class SimilarTopicsController < ApplicationController end def index - params.require(:title) - params.require(:raw) - title, raw = params[:title], params[:raw] - invalid_length = [:title, :raw].any? { |key| check_invalid_length(key, params[key]) } + title = params.require(:title) + raw = params[:raw] - # Only suggest similar topics if the site has a minimum amount of topics present - # and params are long enough. - return render json: [] if invalid_length || !Topic.count_exceeds_minimum? + if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? + return render json: [] + end topics = Topic.similar_to(title, raw, current_user).to_a topics.map! { |t| SimilarTopic.new(t) } render_serialized(topics, SimilarTopicSerializer, root: :similar_topics, rest_serializer: true) end - protected - - def check_invalid_length(key, attr) - str = (key == :raw) ? "body" : key.to_s - attr.length < SiteSetting.send("min_#{str}_similar_length") - end - end diff --git a/app/models/topic.rb b/app/models/topic.rb index 52aa1255e5f..12ee2ea2ae7 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -436,42 +436,46 @@ class Topic < ActiveRecord::Base archetype == Archetype.private_message end - MAX_SIMILAR_BODY_LENGTH = 200 - # Search for similar topics - def self.similar_to(title, raw, user = nil) - return [] unless title.present? - return [] unless raw.present? + MAX_SIMILAR_BODY_LENGTH ||= 200 - filter_words = Search.prepare_data(title + " " + raw[0...MAX_SIMILAR_BODY_LENGTH]); + def self.similar_to(title, raw, user = nil) + return [] if title.blank? + raw = raw.presence || "" + + search_data = "#{title} #{raw[0...MAX_SIMILAR_BODY_LENGTH]}".strip + filter_words = Search.prepare_data(search_data) ts_query = Search.ts_query(filter_words, nil, "|") - # Exclude category definitions from similar topic suggestions - - candidates = Topic.visible - .secured(Guardian.new(user)) + candidates = Topic + .visible .listable_topics - .joins('JOIN topic_search_data s ON topics.id = s.topic_id') + .secured(Guardian.new(user)) + .joins("JOIN topic_search_data s ON topics.id = s.topic_id") + .joins("LEFT JOIN categories c ON topics.id = c.topic_id") .where("search_data @@ #{ts_query}") + .where("c.topic_id IS NULL") .order("ts_rank(search_data, #{ts_query}) DESC") .limit(SiteSetting.max_similar_results * 3) - exclude_topic_ids = Category.pluck(:topic_id).compact! - if exclude_topic_ids.present? - candidates = candidates.where("topics.id NOT IN (?)", exclude_topic_ids) - end - candidate_ids = candidates.pluck(:id) - return [] unless candidate_ids.present? + return [] if candidate_ids.blank? - similar = Topic.select(sanitize_sql_array(["topics.*, similarity(topics.title, :title) + similarity(topics.title, :raw) AS similarity, p.cooked as blurb", title: title, raw: raw])) + similars = Topic .joins("JOIN posts AS p ON p.topic_id = topics.id AND p.post_number = 1") - .limit(SiteSetting.max_similar_results) .where("topics.id IN (?)", candidate_ids) - .where("similarity(topics.title, :title) + similarity(topics.title, :raw) > 0.2", raw: raw, title: title) - .order('similarity desc') + .order("similarity DESC") + .limit(SiteSetting.max_similar_results) - similar + if raw.present? + similars + .select(sanitize_sql_array(["topics.*, similarity(topics.title, :title) + similarity(p.raw, :raw) AS similarity, p.cooked AS blurb", title: title, raw: raw])) + .where("similarity(topics.title, :title) + similarity(p.raw, :raw) > 0.2", title: title, raw: raw) + else + similars + .select(sanitize_sql_array(["topics.*, similarity(topics.title, :title) AS similarity, p.cooked AS blurb", title: title])) + .where("similarity(topics.title, :title) > 0.2", title: title) + end end def update_status(status, enabled, user, opts = {}) @@ -481,7 +485,7 @@ class Topic < ActiveRecord::Base # Atomically creates the next post number def self.next_post_number(topic_id, reply = false, whisper = false) - highest = exec_sql("select coalesce(max(post_number),0) as max from posts where topic_id = ?", topic_id).first['max'].to_i + highest = exec_sql("SELECT coalesce(max(post_number),0) AS max FROM posts WHERE topic_id = ?", topic_id).first['max'].to_i if whisper diff --git a/config/site_settings.yml b/config/site_settings.yml index 85ba28dd91e..52363b76122 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -499,12 +499,6 @@ posting: locale_default: zh_CN: 4 zh_TW: 4 - min_body_similar_length: - client: true - default: 15 - locale_default: - zh_CN: 5 - zh_TW: 5 enable_private_messages: default: true client: true diff --git a/lib/search.rb b/lib/search.rb index dd70d9b5a2b..4baf419fd3b 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -50,9 +50,7 @@ class Search data = search_data.squish # TODO cppjieba_rb is designed for chinese, we need something else for Korean / Japanese if ['zh_TW', 'zh_CN', 'ja', 'ko'].include?(SiteSetting.default_locale) || SiteSetting.search_tokenize_chinese_japanese_korean - unless defined? CppjiebaRb - require 'cppjieba_rb' - end + require 'cppjieba_rb' unless defined? CppjiebaRb mode = (purpose == :query ? :query : :mix) data = CppjiebaRb.segment(search_data, mode: mode) data = CppjiebaRb.filter_stop_word(data).join(' ') diff --git a/spec/controllers/similar_topics_controller_spec.rb b/spec/controllers/similar_topics_controller_spec.rb index 605bed1d6a6..e14432ecced 100644 --- a/spec/controllers/similar_topics_controller_spec.rb +++ b/spec/controllers/similar_topics_controller_spec.rb @@ -10,10 +10,6 @@ describe SimilarTopicsController do expect { xhr :get, :index, raw: raw }.to raise_error(ActionController::ParameterMissing) end - it "requires a raw body" do - expect { xhr :get, :index, title: title }.to raise_error(ActionController::ParameterMissing) - end - it "returns no results if the title length is below the minimum" do Topic.expects(:similar_to).never SiteSetting.min_title_similar_length = 100 @@ -22,14 +18,6 @@ describe SimilarTopicsController do expect(json["similar_topics"].size).to eq(0) end - it "returns no results if the body length is below the minimum" do - Topic.expects(:similar_to).never - SiteSetting.min_body_similar_length = 100 - xhr :get, :index, title: title, raw: raw - json = ::JSON.parse(response.body) - expect(json["similar_topics"].size).to eq(0) - end - describe "minimum_topics_similar" do before do diff --git a/test/javascripts/helpers/site-settings.js b/test/javascripts/helpers/site-settings.js index e44e4434e14..7f48f999477 100644 --- a/test/javascripts/helpers/site-settings.js +++ b/test/javascripts/helpers/site-settings.js @@ -48,7 +48,6 @@ Discourse.SiteSettingsOriginal = { "min_private_message_title_length":2, "allow_uncategorized_topics":true, "min_title_similar_length":10, - "min_body_similar_length":15, "edit_history_visible_to_public":true, "delete_removed_posts_after":24, "traditional_markdown_linebreaks":false,