diff --git a/app/assets/javascripts/discourse/views/search/search_view.js b/app/assets/javascripts/discourse/views/search/search_view.js index 3ea1b69ef6c..9625a37cb91 100644 --- a/app/assets/javascripts/discourse/views/search/search_view.js +++ b/app/assets/javascripts/discourse/views/search/search_view.js @@ -36,9 +36,10 @@ Discourse.SearchView = Discourse.View.extend({ // If we need to perform another search newSearchNeeded: (function() { this.set('noResults', false); - if (this.present('term')) { + var term = this.get('term'); + if (term && term.length >= Discourse.SiteSettings.min_search_term_length) { this.set('loading', true); - this.searchTerm(this.get('term'), this.get('typeFilter')); + this.searchTerm(term, this.get('typeFilter')); } else { this.set('results', null); } diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 5077dec26fe..ae0f32379c6 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -3,7 +3,8 @@ require_dependency 'search' class SearchController < ApplicationController def query - render_json_dump(Search.query(params[:term], params[:type_filter]).as_json) + search_result = Search.query(params[:term], params[:type_filter], SiteSetting.min_search_term_length) + render_json_dump(search_result.as_json) end end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 6bcf8305eb0..d0c02f69cd3 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -29,6 +29,7 @@ class SiteSetting < ActiveRecord::Base client_setting(:max_post_length, 16000) client_setting(:min_topic_title_length, 5) client_setting(:max_topic_title_length, 255) + client_setting(:min_search_term_length, 3) client_setting(:flush_timings_secs, 5) client_setting(:supress_reply_directly_below, true) client_setting(:email_domains_blacklist, 'mailinator.com') diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 91956b106c6..bd01b4ba52e 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -254,6 +254,7 @@ en: max_post_length: "Maximum post length in characters" min_topic_title_length: "Minimum topic title length in characters" max_topic_title_length: "Maximum topic title length in characters" + min_search_term_length: "Minimum search term length in characters" allow_duplicate_topic_titles: "Allow topics with identical titles" unique_posts_mins: "How many minutes before a user can make a post with the same content again" enforce_global_nicknames: "Enforce global nickname uniqueness (WARNING: only change this during initial setup)" diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index d08373c16e7..efef2be9d4f 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -261,6 +261,7 @@ fr: max_post_length: "longueur maximale des messages" min_topic_title_length: "longueur minimale des titres de discussion" max_topic_title_length: "longueur maximale des titres de discussion" + min_search_term_length: "longueur mimimale du texte saisie avant de lancer une recherche" allow_duplicate_topic_titles: "deux utilisateurs peuvent-ils créer des discussions avec le même titre" unique_posts_mins: "Combien de temps avant qu'un utilisateur puisse poster le même contenu à nouveau" enforce_global_nicknames: "Imposer un pseudo global. Note: fonctionne uniquement pendant la phase initiale de paramétrage." diff --git a/lib/search.rb b/lib/search.rb index b034bfa1f0b..11a2026cfdd 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -1,9 +1,5 @@ module Search - def self.min_search_term_length - 3 - end - def self.per_facet 5 end @@ -97,15 +93,15 @@ module Search end end - def self.query(term, type_filter=nil) + def self.query(term, type_filter=nil, min_search_term_length=3) return nil if term.blank? - sanitized_term = PG::Connection.escape_string(term.gsub(/[:()&!]/,'')) # Instead of original term.gsub(/[^0-9a-zA-Z_ ]/, '') # We are stripping only symbols taking place in FTS and simply sanitizing the rest. + sanitized_term = PG::Connection.escape_string(term.gsub(/[:()&!]/,'')) # really short terms are totally pointless - return nil if sanitized_term.blank? || sanitized_term.length < self.min_search_term_length + return nil if sanitized_term.blank? || sanitized_term.length < min_search_term_length terms = sanitized_term.split terms.map! {|t| "#{t}:*"} @@ -176,12 +172,14 @@ module Search result = grouped.map do |type, results| more = type_filter.blank? && (results.size > Search.per_facet) results = results[0..([results.length, Search.per_facet].min - 1)] if type_filter.blank? - - {type: type, - name: I18n.t("search.types.#{type}"), - more: more, - results: results} + { + type: type, + name: I18n.t("search.types.#{type}"), + more: more, + results: results + } end + result end diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 089915b8125..2d031cc7ecc 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -71,6 +71,11 @@ describe Search do Search.query(nil).should be_blank end + it 'does not search when the search term is too small' do + ActiveRecord::Base.expects(:exec_sql).never + Search.query('evil', nil, 5).should be_blank + end + it 'escapes non alphanumeric characters' do Search.query('foo :!$);}]>@\#\"\'').should be_blank # There are at least three levels of sanitation for Search.query! end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index e6305b279c5..8717590eeea 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -3,14 +3,13 @@ require 'spec_helper' describe SearchController do it 'performs the query' do - Search.expects(:query).with('test', nil) + Search.expects(:query).with('test', nil, 3) xhr :get, :query, term: 'test' end it 'performs the query with a filter' do - Search.expects(:query).with('test', 'topic') + Search.expects(:query).with('test', 'topic', 3) xhr :get, :query, term: 'test', type_filter: 'topic' end - end