Skip to content

Commit bbe603f

Browse files
c910335robacarp
authored andcommitted
Add limit and offset to QueryBuilder, mild refactoring (#284)
* fix typo "include" => "extend" * refactor builder assemblers * add limit and offset * add limit and offset spec * remove SQLBuilder
1 parent cf9bc96 commit bbe603f

File tree

6 files changed

+122
-63
lines changed

6 files changed

+122
-63
lines changed

spec/granite/query/assembler/postgresql_spec.cr

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ require "../spec_helper"
2626
query.raw_sql.should match ignore_whitespace sql
2727

2828
assembler = query.assembler
29-
assembler.build_where
29+
assembler.where
3030
assembler.numbered_parameters.should eq ["bob", "23"]
3131
end
3232
end
@@ -41,5 +41,24 @@ require "../spec_helper"
4141
builder.order(id: :asc).raw_sql.should match ignore_whitespace sql
4242
end
4343
end
44+
45+
context "offset" do
46+
it "adds offset for select query" do
47+
sql = "select #{query_fields} from table order by id desc offset 8"
48+
builder.offset(8).raw_sql.should match ignore_whitespace sql
49+
end
50+
51+
it "adds offset for first query" do
52+
sql = "select #{query_fields} from table order by id desc limit 1 offset 3"
53+
builder.offset(3).assembler.first.raw_sql.should match ignore_whitespace sql
54+
end
55+
end
56+
57+
context "limit" do
58+
it "adds limit for select query" do
59+
sql = "select #{query_fields} from table order by id desc limit 5"
60+
builder.limit(5).raw_sql.should match ignore_whitespace sql
61+
end
62+
end
4463
end
4564
{% end %}

spec/granite/query/builder_spec.cr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,14 @@ describe Granite::Query::Builder(Model) do
1515
]
1616
query.order_fields.should eq expected
1717
end
18+
19+
it "stores limit" do
20+
query = builder.limit(7)
21+
query.limit.should eq 7
22+
end
23+
24+
it "stores offset" do
25+
query = builder.offset(17)
26+
query.offset.should eq 17
27+
end
1828
end

src/granite/query/assemblers/base.cr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ module Granite::Query::Assembler
2626
[Model.fields].flatten.join ", "
2727
end
2828

29+
def build_sql
30+
clauses = [] of String?
31+
yield clauses
32+
clauses.compact!.join " "
33+
end
34+
2935
abstract def count : Int64
3036
abstract def first(n : Int32 = 1) : Array(Model)
3137
abstract def delete

src/granite/query/assemblers/postgresql.cr

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
# This will likely require adapter specific subclassing :[.
33
module Granite::Query::Assembler
44
class Postgresql(Model) < Base(Model)
5-
def build_where
5+
@where : String?
6+
@order : String?
7+
@limit : String?
8+
@offset : String?
9+
@group_by : String?
10+
11+
def where
12+
return @where if @where
13+
614
clauses = @query.where_fields.map do |field, value|
715
add_aggregate_field field
816

@@ -14,19 +22,21 @@ module Granite::Query::Assembler
1422
end
1523
end
1624

17-
return "" if clauses.none?
25+
return nil if clauses.none?
1826

19-
"WHERE #{clauses.join " AND "}"
27+
@where = "WHERE #{clauses.join " AND "}"
2028
end
2129

22-
def build_order(use_default_order = true)
30+
def order(use_default_order = true)
31+
return @order if @order
32+
2333
order_fields = @query.order_fields
2434

2535
if order_fields.none?
2636
if use_default_order
2737
order_fields = default_order
2838
else
29-
return ""
39+
return nil
3040
end
3141
end
3242

@@ -40,7 +50,19 @@ module Granite::Query::Assembler
4050
end
4151
end
4252

43-
"ORDER BY #{order_clauses.join ", "}"
53+
@order = "ORDER BY #{order_clauses.join ", "}"
54+
end
55+
56+
def limit
57+
@limit ||= if limit = @query.limit
58+
"LIMIT #{limit}"
59+
end
60+
end
61+
62+
def offset
63+
@offset ||= if offset = @query.offset
64+
"OFFSET #{offset}"
65+
end
4466
end
4567

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

53-
def build_group_by
54-
if @aggregate_fields.any?
55-
"GROUP BY #{@aggregate_fields.join ", "}"
56-
else
57-
""
58-
end
75+
def group_by
76+
@group_by ||= if @aggregate_fields.any?
77+
"GROUP BY #{@aggregate_fields.join ", "}"
78+
end
5979
end
6080

6181
def count : Executor::Value(Model, Int64)
62-
where = build_where
63-
order = build_order(false)
64-
group = build_group_by
65-
66-
sql = <<-SQL
67-
SELECT COUNT(*)
68-
FROM #{table_name}
69-
#{where}
70-
#{group}
71-
#{order}
72-
SQL
82+
sql = build_sql do |s|
83+
s << "SELECT COUNT(*)"
84+
s << "FROM #{table_name}"
85+
s << where
86+
s << group_by
87+
s << order
88+
end
7389

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

7793
def first(n : Int32 = 1) : Executor::List(Model)
78-
sql = <<-SQL
79-
SELECT #{field_list}
80-
FROM #{table_name}
81-
#{build_where}
82-
#{build_order}
83-
LIMIT #{n}
84-
SQL
94+
sql = build_sql do |s|
95+
s << "SELECT #{field_list}"
96+
s << "FROM #{table_name}"
97+
s << where
98+
s << order
99+
s << "LIMIT #{n}"
100+
s << offset
101+
end
85102

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

89106
def delete
90-
sql = <<-SQL
91-
DELETE
92-
FROM #{table_name}
93-
#{build_where}
94-
SQL
107+
sql = build_sql do |s|
108+
s << "DELETE FROM #{table_name}"
109+
s << where
110+
end
95111

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

102118
def select
103-
sql = <<-SQL
104-
SELECT #{field_list}
105-
FROM #{table_name}
106-
#{build_where}
107-
#{build_order}
108-
SQL
119+
sql = build_sql do |s|
120+
s << "SELECT #{field_list}"
121+
s << "FROM #{table_name}"
122+
s << where
123+
s << order
124+
s << limit
125+
s << offset
126+
end
109127

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

src/granite/query/builder.cr

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ class Granite::Query::Builder(Model)
2424
Descending
2525
end
2626

27-
getter where_fields
28-
getter order_fields
27+
getter where_fields = {} of FieldName => FieldData
28+
getter order_fields = [] of NamedTuple(field: String, direction: Sort)
29+
getter offset : Int64?
30+
getter limit : Int64?
2931

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

3535
def assembler
@@ -40,6 +40,10 @@ class Granite::Query::Builder(Model)
4040
end
4141

4242
def where(**matches)
43+
where(matches)
44+
end
45+
46+
def where(matches)
4347
matches.each do |field, data|
4448
@where_fields[field.to_s] = data
4549
end
@@ -62,6 +66,10 @@ class Granite::Query::Builder(Model)
6266
end
6367

6468
def order(**dsl)
69+
order(dsl)
70+
end
71+
72+
def order(dsl)
6573
dsl.each do |field, dsl_direction|
6674
direction = Sort::Ascending
6775

@@ -75,6 +83,18 @@ class Granite::Query::Builder(Model)
7583
self
7684
end
7785

86+
def offset(num)
87+
@offset = num.to_i64
88+
89+
self
90+
end
91+
92+
def limit(num)
93+
@limit = num.to_i64
94+
95+
self
96+
end
97+
7898
def raw_sql
7999
assembler.select.raw_sql
80100
end
Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
1-
# DSL to be included into a model
1+
# DSL to be extended into a model
22
# To activate, simply
33
#
44
# class Model < Granite::Base
5-
# include Query::BuilderMethods
5+
# extend Query::BuilderMethods
66
# end
77
module Granite::Query::BuilderMethods
88
def __builder
99
Builder(self).new
1010
end
1111

12-
def count : Executor::Value(self, Int64)
13-
__builder.count
14-
end
15-
16-
def where(**match_data) : Builder
17-
__builder.where **match_data
18-
end
19-
20-
def first : self?
21-
__builder.first
22-
end
23-
24-
def first(n : Int32) : Executor::List(self)
25-
__builder.first n
26-
end
12+
delegate where, count, order, offset, limit, first, to: __builder
2713
end

0 commit comments

Comments
 (0)