UserSearch refactor

Added .sql_builder to all AR models
This commit is contained in:
Sam Saffron 2013-02-10 23:37:24 +11:00
parent 6fb78809c2
commit af810f38dd
4 changed files with 69 additions and 48 deletions

View File

@ -1,20 +1,19 @@
class UserSearch class UserSearch
def self.search term, topic_id = nil def self.search term, topic_id = nil
User.find_by_sql sql(term, topic_id) sql = User.sql_builder(
end "select id, username, name, email from users u
/*left_join*/
/*where*/
/*order_by*/")
private
def self.sql term, topic_id
sql = "select id, username, name, email from users u "
if topic_id if topic_id
sql << "left join (select distinct p.user_id from posts p where topic_id = :topic_id) s on sql.left_join "(select distinct p.user_id from posts p where topic_id = :topic_id) s on s.user_id = u.id", topic_id: topic_id
s.user_id = u.id "
end end
if term.present? if term.present?
sql << "where username ilike :term_like or sql.where("username ilike :term_like or
to_tsvector('simple', name) @@ to_tsvector('simple', name) @@
to_tsquery('simple', to_tsquery('simple',
regexp_replace( regexp_replace(
@ -22,22 +21,18 @@ class UserSearch
cast(plainto_tsquery(:term) as text) cast(plainto_tsquery(:term) as text)
,'\''(?: |$)', ':*''', 'g'), ,'\''(?: |$)', ':*''', 'g'),
'''', '', 'g') '''', '', 'g')
) " )", term: term, term_like: "#{term}%")
sql.order_by "case when username_lower = :term then 0 else 1 end asc"
end end
sql << "order by case when username_lower = :term then 0 else 1 end asc, "
if topic_id if topic_id
sql << " case when s.user_id is null then 0 else 1 end desc, " sql.order_by "case when s.user_id is null then 0 else 1 end desc"
end end
sql << " case when last_seen_at is null then 0 else 1 end desc, last_seen_at desc, username asc limit(20)" sql.order_by "case when last_seen_at is null then 0 else 1 end desc, last_seen_at desc, username asc limit(20)"
sanitize_sql_array(sql, topic_id: topic_id, term_like: "#{term}%", term: term) sql.exec
end end
def self.sanitize_sql_array *args end
ActiveRecord::Base.send(:sanitize_sql_array, args)
end
end

View File

@ -0,0 +1 @@
require 'sql_builder'

View File

@ -1,9 +1,10 @@
class SqlBuilder class SqlBuilder
def initialize(template) def initialize(template,klass=nil)
@args = {} @args = {}
@sql = template @sql = template
@sections = {} @sections = {}
@klass = klass
end end
[:set, :where2,:where,:order_by,:limit,:left_join,:join,:offset].each do |k| [:set, :where2,:where,:order_by,:limit,:left_join,:join,:offset].each do |k|
@ -40,9 +41,17 @@ class SqlBuilder
sql.sub!("/*#{k}*/", joined) sql.sub!("/*#{k}*/", joined)
end end
ActiveRecord::Base.exec_sql(sql,@args) if @klass
@klass.find_by_sql(ActiveRecord::Base.send(:sanitize_sql_array, [sql, @args]))
else
ActiveRecord::Base.exec_sql(sql,@args)
end
end
end
class ActiveRecord::Base
def self.sql_builder(template)
SqlBuilder.new(template, self)
end end
end end

View File

@ -4,32 +4,48 @@ require_dependency 'sql_builder'
describe SqlBuilder do describe SqlBuilder do
before do describe "attached" do
@builder = SqlBuilder.new("select * from (select :a A union all select :b) as X /*where*/ /*order_by*/ /*limit*/ /*offset*/") before do
@builder = Post.sql_builder("select * from posts /*where*/ /*limit*/")
end
it "should find a post by id" do
p = Fabricate(:post)
@builder.where('id = :id and topic_id = :topic_id', id: p.id, topic_id: p.topic_id)
p2 = @builder.exec.first
p2.id.should == p.id
p2.should == p
end
end end
it "should allow for 1 param exec" do describe "detached" do
@builder.exec(a: 1, b: 2).values[0][0].should == '1' before do
end @builder = SqlBuilder.new("select * from (select :a A union all select :b) as X /*where*/ /*order_by*/ /*limit*/ /*offset*/")
end
it "should allow for a single where" do it "should allow for 1 param exec" do
@builder.where(":a = 1") @builder.exec(a: 1, b: 2).values[0][0].should == '1'
@builder.exec(a: 1, b: 2).values[0][0].should == '1' end
end
it "should allow where chaining" do it "should allow for a single where" do
@builder.where(":a = 1") @builder.where(":a = 1")
@builder.where("2 = 1") @builder.exec(a: 1, b: 2).values[0][0].should == '1'
@builder.exec(a: 1, b: 2).to_a.length.should == 0 end
end
it "should allow order by" do it "should allow where chaining" do
@builder.order_by("A desc").limit(1) @builder.where(":a = 1")
.exec(a:1, b:2).values[0][0].should == "2" @builder.where("2 = 1")
end @builder.exec(a: 1, b: 2).to_a.length.should == 0
it "should allow offset" do end
@builder.order_by("A desc").offset(1)
.exec(a:1, b:2).values[0][0].should == "1" it "should allow order by" do
@builder.order_by("A desc").limit(1)
.exec(a:1, b:2).values[0][0].should == "2"
end
it "should allow offset" do
@builder.order_by("A desc").offset(1)
.exec(a:1, b:2).values[0][0].should == "1"
end
end end
end end