Skip to content

Commit

Permalink
Add limit and offset to QueryBuilder, mild refactoring (#284)
Browse files Browse the repository at this point in the history
* fix typo "include" => "extend"

* refactor builder assemblers

* add limit and offset

* add limit and offset spec

* remove SQLBuilder
  • Loading branch information
c910335 authored and robacarp committed Oct 8, 2018
1 parent cf9bc96 commit bbe603f
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 63 deletions.
21 changes: 20 additions & 1 deletion spec/granite/query/assembler/postgresql_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require "../spec_helper"
query.raw_sql.should match ignore_whitespace sql

assembler = query.assembler
assembler.build_where
assembler.where
assembler.numbered_parameters.should eq ["bob", "23"]
end
end
Expand All @@ -41,5 +41,24 @@ require "../spec_helper"
builder.order(id: :asc).raw_sql.should match ignore_whitespace sql
end
end

context "offset" do
it "adds offset for select query" do
sql = "select #{query_fields} from table order by id desc offset 8"
builder.offset(8).raw_sql.should match ignore_whitespace sql
end

it "adds offset for first query" do
sql = "select #{query_fields} from table order by id desc limit 1 offset 3"
builder.offset(3).assembler.first.raw_sql.should match ignore_whitespace sql
end
end

context "limit" do
it "adds limit for select query" do
sql = "select #{query_fields} from table order by id desc limit 5"
builder.limit(5).raw_sql.should match ignore_whitespace sql
end
end
end
{% end %}
10 changes: 10 additions & 0 deletions spec/granite/query/builder_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,14 @@ describe Granite::Query::Builder(Model) do
]
query.order_fields.should eq expected
end

it "stores limit" do
query = builder.limit(7)
query.limit.should eq 7
end

it "stores offset" do
query = builder.offset(17)
query.offset.should eq 17
end
end
6 changes: 6 additions & 0 deletions src/granite/query/assemblers/base.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ module Granite::Query::Assembler
[Model.fields].flatten.join ", "
end

def build_sql
clauses = [] of String?
yield clauses
clauses.compact!.join " "
end

abstract def count : Int64
abstract def first(n : Int32 = 1) : Array(Model)
abstract def delete
Expand Down
100 changes: 59 additions & 41 deletions src/granite/query/assemblers/postgresql.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
# This will likely require adapter specific subclassing :[.
module Granite::Query::Assembler
class Postgresql(Model) < Base(Model)
def build_where
@where : String?
@order : String?
@limit : String?
@offset : String?
@group_by : String?

def where
return @where if @where

clauses = @query.where_fields.map do |field, value|
add_aggregate_field field

Expand All @@ -14,19 +22,21 @@ module Granite::Query::Assembler
end
end

return "" if clauses.none?
return nil if clauses.none?

"WHERE #{clauses.join " AND "}"
@where = "WHERE #{clauses.join " AND "}"
end

def build_order(use_default_order = true)
def order(use_default_order = true)
return @order if @order

order_fields = @query.order_fields

if order_fields.none?
if use_default_order
order_fields = default_order
else
return ""
return nil
end
end

Expand All @@ -40,7 +50,19 @@ module Granite::Query::Assembler
end
end

"ORDER BY #{order_clauses.join ", "}"
@order = "ORDER BY #{order_clauses.join ", "}"
end

def limit
@limit ||= if limit = @query.limit
"LIMIT #{limit}"
end
end

def offset
@offset ||= if offset = @query.offset
"OFFSET #{offset}"
end
end

def log(*stuff)
Expand All @@ -50,48 +72,42 @@ module Granite::Query::Assembler
[{field: Model.primary_name, direction: "ASC"}]
end

def build_group_by
if @aggregate_fields.any?
"GROUP BY #{@aggregate_fields.join ", "}"
else
""
end
def group_by
@group_by ||= if @aggregate_fields.any?
"GROUP BY #{@aggregate_fields.join ", "}"
end
end

def count : Executor::Value(Model, Int64)
where = build_where
order = build_order(false)
group = build_group_by

sql = <<-SQL
SELECT COUNT(*)
FROM #{table_name}
#{where}
#{group}
#{order}
SQL
sql = build_sql do |s|
s << "SELECT COUNT(*)"
s << "FROM #{table_name}"
s << where
s << group_by
s << order
end

Executor::Value(Model, Int64).new sql, numbered_parameters, default: 0_i64
end

def first(n : Int32 = 1) : Executor::List(Model)
sql = <<-SQL
SELECT #{field_list}
FROM #{table_name}
#{build_where}
#{build_order}
LIMIT #{n}
SQL
sql = build_sql do |s|
s << "SELECT #{field_list}"
s << "FROM #{table_name}"
s << where
s << order
s << "LIMIT #{n}"
s << offset
end

Executor::List(Model).new sql, numbered_parameters
end

def delete
sql = <<-SQL
DELETE
FROM #{table_name}
#{build_where}
SQL
sql = build_sql do |s|
s << "DELETE FROM #{table_name}"
s << where
end

log sql, numbered_parameters
Model.adapter.open do |db|
Expand All @@ -100,12 +116,14 @@ module Granite::Query::Assembler
end

def select
sql = <<-SQL
SELECT #{field_list}
FROM #{table_name}
#{build_where}
#{build_order}
SQL
sql = build_sql do |s|
s << "SELECT #{field_list}"
s << "FROM #{table_name}"
s << where
s << order
s << limit
s << offset
end

Executor::List(Model).new sql, numbered_parameters
end
Expand Down
28 changes: 24 additions & 4 deletions src/granite/query/builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ class Granite::Query::Builder(Model)
Descending
end

getter where_fields
getter order_fields
getter where_fields = {} of FieldName => FieldData
getter order_fields = [] of NamedTuple(field: String, direction: Sort)
getter offset : Int64?
getter limit : Int64?

def initialize(@boolean_operator = :and)
@where_fields = {} of FieldName => FieldData
@order_fields = [] of NamedTuple(field: String, direction: Sort)
end

def assembler
Expand All @@ -40,6 +40,10 @@ class Granite::Query::Builder(Model)
end

def where(**matches)
where(matches)
end

def where(matches)
matches.each do |field, data|
@where_fields[field.to_s] = data
end
Expand All @@ -62,6 +66,10 @@ class Granite::Query::Builder(Model)
end

def order(**dsl)
order(dsl)
end

def order(dsl)
dsl.each do |field, dsl_direction|
direction = Sort::Ascending

Expand All @@ -75,6 +83,18 @@ class Granite::Query::Builder(Model)
self
end

def offset(num)
@offset = num.to_i64

self
end

def limit(num)
@limit = num.to_i64

self
end

def raw_sql
assembler.select.raw_sql
end
Expand Down
20 changes: 3 additions & 17 deletions src/granite/query/builder_methods.cr
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
# DSL to be included into a model
# DSL to be extended into a model
# To activate, simply
#
# class Model < Granite::Base
# include Query::BuilderMethods
# extend Query::BuilderMethods
# end
module Granite::Query::BuilderMethods
def __builder
Builder(self).new
end

def count : Executor::Value(self, Int64)
__builder.count
end

def where(**match_data) : Builder
__builder.where **match_data
end

def first : self?
__builder.first
end

def first(n : Int32) : Executor::List(self)
__builder.first n
end
delegate where, count, order, offset, limit, first, to: __builder
end

0 comments on commit bbe603f

Please sign in to comment.