From f560321626d1e08114bb78a9d4bbfe34fcf935e4 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Thu, 28 Jul 2011 23:08:37 -0700 Subject: [PATCH] optimized people search --- app/models/person.rb | 28 +++++-------------- app/models/profile.rb | 8 ++++++ ...20110729045734_add_full_name_to_profile.rb | 22 +++++++++++++++ db/schema.rb | 8 +++--- spec/models/person_spec.rb | 1 - spec/models/profile_spec.rb | 12 ++++++++ 6 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 db/migrate/20110729045734_add_full_name_to_profile.rb diff --git a/app/models/person.rb b/app/models/person.rb index 756a00ef7..cc6748c9d 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -47,39 +47,25 @@ class Person < ActiveRecord::Base def self.search_query_string(query) if postgres? where_clause = <<-SQL - profiles.first_name ILIKE ? OR - profiles.last_name ILIKE ? OR - people.diaspora_handle ILIKE ? + profiles.full_name ILIKE ? OR + profiles.diaspora_handle ILIKE ? SQL else where_clause = <<-SQL - profiles.first_name LIKE ? OR - profiles.last_name LIKE ? OR - people.diaspora_handle LIKE ? OR - profiles.first_name LIKE ? OR - profiles.last_name LIKE ? + profiles.full_name LIKE ? OR + people.diaspora_handle LIKE ? SQL end - sql = "" - tokens = [] - - query_tokens = query.to_s.strip.split(" ") - query_tokens.each_with_index do |raw_token, i| - token = "#{raw_token}%" - up_token = "#{raw_token.titleize}%" - sql << " OR " unless i == 0 - sql << where_clause - tokens.concat([token, token, token]) - tokens.concat([up_token, up_token]) unless postgres? - end - [sql, tokens] + q_tokens = query.to_s.strip.gsub(/(\s|$|^)/) { "%#{$1}" } + [where_clause, [q_tokens, q_tokens]] end def self.search(query, user) return [] if query.to_s.blank? || query.to_s.length < 3 sql, tokens = self.search_query_string(query) + Person.searchable.where(sql, *tokens).joins( "LEFT OUTER JOIN contacts ON contacts.user_id = #{user.id} AND contacts.person_id = people.id" ).includes(:profile diff --git a/app/models/profile.rb b/app/models/profile.rb index b7f497b43..0160f6689 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -45,6 +45,7 @@ class Profile < ActiveRecord::Base end before_save do self.build_tags + self.construct_full_name end def subscribers(user) @@ -120,6 +121,13 @@ class Profile < ActiveRecord::Base end end + # Constructs a full name by joining #first_name and #last_name + # @returns [String] A full name + def construct_full_name + self.full_name = [self.first_name, self.last_name].join(' ').downcase + self.full_name + end + protected def strip_names self.first_name.strip! if self.first_name diff --git a/db/migrate/20110729045734_add_full_name_to_profile.rb b/db/migrate/20110729045734_add_full_name_to_profile.rb new file mode 100644 index 000000000..047eed869 --- /dev/null +++ b/db/migrate/20110729045734_add_full_name_to_profile.rb @@ -0,0 +1,22 @@ +class AddFullNameToProfile < ActiveRecord::Migration + def self.up + add_column :profiles, :full_name, :string, :limit => 70 + + add_index :profiles, :full_name + add_index :profiles, [:full_name, :searchable] + remove_index :profiles, [:first_name, :last_name, :searchable] + remove_index :profiles, [:first_name, :searchable] + remove_index :profiles, [:last_name, :searchable] + + execute("UPDATE profiles SET full_name=LOWER(CONCAT(first_name, ' ', last_name))") + end + + def self.down + remove_index :profiles, :column => :full_name + remove_column :profiles, :full_name + + add_index :profiles, [:first_name, :searchable] + add_index :profiles, [:last_name, :searchable] + add_index :profiles, [:first_name, :last_name, :searchable] + end +end diff --git a/db/schema.rb b/db/schema.rb index 02a0cb94c..357eaa570 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110710102747) do +ActiveRecord::Schema.define(:version => 20110729045734) do create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -295,11 +295,11 @@ ActiveRecord::Schema.define(:version => 20110710102747) do t.datetime "created_at" t.datetime "updated_at" t.string "location" + t.string "full_name", :limit => 70 end - add_index "profiles", ["first_name", "last_name", "searchable"], :name => "index_profiles_on_first_name_and_last_name_and_searchable" - add_index "profiles", ["first_name", "searchable"], :name => "index_profiles_on_first_name_and_searchable" - add_index "profiles", ["last_name", "searchable"], :name => "index_profiles_on_last_name_and_searchable" + add_index "profiles", ["full_name", "searchable"], :name => "index_profiles_on_full_name_and_searchable" + add_index "profiles", ["full_name"], :name => "index_profiles_on_full_name" add_index "profiles", ["person_id"], :name => "index_profiles_on_person_id" create_table "service_users", :force => true do |t| diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index b1def8023..6ee99e513 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -36,7 +36,6 @@ describe Person do end end - describe '#diaspora_handle' do context 'local people' do it 'uses the pod config url to set the diaspora_handle' do diff --git a/spec/models/profile_spec.rb b/spec/models/profile_spec.rb index 366895861..7ee762c6f 100644 --- a/spec/models/profile_spec.rb +++ b/spec/models/profile_spec.rb @@ -29,6 +29,18 @@ describe Profile do end end + describe '#contruct_full_name' do + it 'generates a full name given first and last names' do + profile = Factory(:person).profile + profile.first_name = "casimiro" + profile.last_name = "grippi" + + profile.full_name.should_not == "casimiro grippi" + profile.save + profile.full_name.should == "casimiro grippi" + end + end + describe "of last_name" do it "strips leading and trailing whitespace" do profile = Factory.build(:profile, :last_name => " Ohba ")