diff --git a/app/models/user_search.rb b/app/models/user_search.rb index 5ed020f202a..de0af9c706b 100644 --- a/app/models/user_search.rb +++ b/app/models/user_search.rb @@ -14,15 +14,12 @@ class UserSearch if @term.present? if SiteSetting.enable_names? - users = users.where("username_lower LIKE :term_like OR - TO_TSVECTOR('simple', name) @@ - TO_TSQUERY('simple', - REGEXP_REPLACE( - REGEXP_REPLACE( - CAST(PLAINTO_TSQUERY(:term) AS TEXT) - ,'\''(?: |$)', ':*''', 'g'), - '''', '', 'g') - )", term: @term, term_like: @term_like) + query = Search.ts_query(@term, "simple") + users = users.includes(:user_search_data) + .references(:user_search_data) + .where("username_lower LIKE :term_like OR user_search_data.search_data @@ #{query}", + term: @term, term_like: @term_like) + .order(User.sql_fragment("CASE WHEN username_lower LIKE ? THEN 0 ELSE 1 END ASC", @term_like)) else users = users.where("username_lower LIKE :term_like", term_like: @term_like) end diff --git a/lib/search.rb b/lib/search.rb index cc7001f2024..2a0826de1ce 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -242,10 +242,11 @@ class Search self.class.query_locale end - def self.ts_query(term) + def self.ts_query(term, locale = nil) + locale = Post.sanitize(locale) if locale all_terms = term.gsub(/[:()&!'"]/,'').split query = Post.sanitize(all_terms.map {|t| "#{PG::Connection.escape_string(t)}:*"}.join(" & ")) - "TO_TSQUERY(#{query_locale}, #{query})" + "TO_TSQUERY(#{locale || query_locale}, #{query})" end def ts_query diff --git a/spec/models/user_search_spec.rb b/spec/models/user_search_spec.rb index beaeb03406d..3e13ef361aa 100644 --- a/spec/models/user_search_spec.rb +++ b/spec/models/user_search_spec.rb @@ -6,7 +6,7 @@ describe UserSearch do let(:topic2) { Fabricate :topic } let(:topic3) { Fabricate :topic } let(:user1) { Fabricate :user, username: "mrb", name: "Michael Madsen", last_seen_at: 10.days.ago } - let(:user2) { Fabricate :user, username: "mrblue", name: "Eddie Bunker", last_seen_at: 9.days.ago } + let(:user2) { Fabricate :user, username: "mrblue", name: "Eddie Code", last_seen_at: 9.days.ago } let(:user3) { Fabricate :user, username: "mrorange", name: "Tim Roth", last_seen_at: 8.days.ago } let(:user4) { Fabricate :user, username: "mrpink", name: "Steve Buscemi", last_seen_at: 7.days.ago } let(:user5) { Fabricate :user, username: "mrbrown", name: "Quentin Tarantino", last_seen_at: 6.days.ago } @@ -15,6 +15,8 @@ describe UserSearch do let(:moderator) { Fabricate :moderator, username: "themod" } before do + ActiveRecord::Base.observers.enable :all + Fabricate :post, user: user1, topic: topic Fabricate :post, user: user2, topic: topic2 Fabricate :post, user: user3, topic: topic @@ -31,6 +33,7 @@ describe UserSearch do # this is a seriously expensive integration test, re-creating this entire test db is too expensive # reuse it "operates correctly" do + # normal search results = search_for(user1.name.split(" ").first) results.size.should == 1 @@ -89,6 +92,12 @@ describe UserSearch do results = search_for("Tarantino") results.size.should == 1 + results = search_for("coding") + results.size.should == 0 + + results = search_for("z") + results.size.should == 0 + # When searching by name is disabled, it will not return the record SiteSetting.enable_names = false results = search_for("Tarantino") @@ -99,6 +108,7 @@ describe UserSearch do results = search_for("mrB") results.first.should == user1 + end end