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

View File

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

View File

@ -1,9 +1,10 @@
class SqlBuilder
def initialize(template)
def initialize(template,klass=nil)
@args = {}
@sql = template
@sections = {}
@klass = klass
end
[:set, :where2,:where,:order_by,:limit,:left_join,:join,:offset].each do |k|
@ -40,9 +41,17 @@ class SqlBuilder
sql.sub!("/*#{k}*/", joined)
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

View File

@ -4,32 +4,48 @@ require_dependency 'sql_builder'
describe SqlBuilder do
before do
@builder = SqlBuilder.new("select * from (select :a A union all select :b) as X /*where*/ /*order_by*/ /*limit*/ /*offset*/")
describe "attached" do
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
it "should allow for 1 param exec" do
@builder.exec(a: 1, b: 2).values[0][0].should == '1'
end
describe "detached" do
before do
@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
@builder.where(":a = 1")
@builder.exec(a: 1, b: 2).values[0][0].should == '1'
end
it "should allow for 1 param exec" do
@builder.exec(a: 1, b: 2).values[0][0].should == '1'
end
it "should allow where chaining" do
@builder.where(":a = 1")
@builder.where("2 = 1")
@builder.exec(a: 1, b: 2).to_a.length.should == 0
end
it "should allow for a single where" do
@builder.where(":a = 1")
@builder.exec(a: 1, b: 2).values[0][0].should == '1'
end
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"
it "should allow where chaining" do
@builder.where(":a = 1")
@builder.where("2 = 1")
@builder.exec(a: 1, b: 2).to_a.length.should == 0
end
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