From 2d5bf0705acab3702f6bfd183ff71e63474a6078 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 9 Nov 2017 16:53:14 +1100 Subject: [PATCH] PERF: exact email match bypass instead of scanning full table when there is an exact email match ONLY return the actual user. --- lib/admin_user_index_query.rb | 21 +++++++++++++++---- .../components/admin_user_index_query_spec.rb | 12 +++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index 2cd23d0b1c5..6572fa2ccf9 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -101,13 +101,26 @@ class AdminUserIndexQuery end end + def filter_by_user_with_bypass(filter) + if filter =~ /.+@.+/ + # probably an email so try the bypass + user_id = UserEmail.where(email: filter.downcase).pluck(:user_id).first + if user_id + return @query.where('users.id = ?', user_id) + end + end + + @query.where('username_lower ILIKE :filter OR user_emails.email ILIKE :filter', filter: "%#{params[:filter]}%") + end + def filter_by_search - if params[:filter].present? - params[:filter].strip! - if ip = IPAddr.new(params[:filter]) rescue nil + filter = params[:filter] + if filter.present? + filter.strip! + if ip = IPAddr.new(filter) rescue nil @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) else - @query.where('username_lower ILIKE :filter OR user_emails.email ILIKE :filter', filter: "%#{params[:filter]}%") + filter_by_user_with_bypass(filter) end end end diff --git a/spec/components/admin_user_index_query_spec.rb b/spec/components/admin_user_index_query_spec.rb index c124cc46628..4a11b97008d 100644 --- a/spec/components/admin_user_index_query_spec.rb +++ b/spec/components/admin_user_index_query_spec.rb @@ -178,6 +178,18 @@ describe AdminUserIndexQuery do describe "filtering" do + context "exact email bypass" do + it "can correctly bypass expensive ilike query" do + user = Fabricate(:user, email: 'sam@Sam.com') + + query = AdminUserIndexQuery.new(filter: 'Sam@sam.com').find_users_query + expect(query.count).to eq(1) + expect(query.first.id).to eq(user.id) + + expect(query.to_sql.downcase).not_to include("ilike") + end + end + context "by email fragment" do before(:each) { Fabricate(:user, email: "test1@example.com") }