Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend ordering with null sorting #228

Merged
merged 5 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spec/query_builder/merge_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ describe "Avram::QueryBuilder#merge" do

it "merges orders" do
query_1 = new_query
.order_by(:id, :asc)
.order_by(Avram::OrderBy.new(:id, :asc))
query_2 = new_query
.order_by(:name, :desc)
.order_by(Avram::OrderBy.new(:name, :desc))

query_1.merge(query_2)
query_1.statement.should contain "ORDER BY id ASC, name DESC"
Expand Down
24 changes: 12 additions & 12 deletions spec/query_builder_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ describe Avram::QueryBuilder do
.raw_where(Avram::Where::Raw.new("name = ?", "Mikias"))
.join(Avram::Join::Inner.new(:users, :posts))
.join(Avram::Join::Inner.new(:users, :posts))
.order_by(:my_column, :asc)
.order_by(:my_column, :asc)
.order_by(Avram::OrderBy.new(:my_column, :asc))
.order_by(Avram::OrderBy.new(:my_column, :asc))

query.wheres.size.should eq(1)
query.raw_wheres.size.should eq(1)
Expand All @@ -20,7 +20,7 @@ describe Avram::QueryBuilder do
end

it "can reset order" do
query = new_query.order_by(:my_column, :asc)
query = new_query.order_by(Avram::OrderBy.new(:my_column, :asc))
query.statement.should eq "SELECT * FROM users ORDER BY my_column ASC"

query.reset_order
Expand Down Expand Up @@ -78,14 +78,14 @@ describe Avram::QueryBuilder do

it "can be ordered" do
query = new_query
.order_by(:name, :asc)
.order_by(:birthday, :asc)
.order_by(:email, :desc)
.order_by(Avram::OrderBy.new(:name, :asc))
.order_by(Avram::OrderBy.new(:birthday, :asc))
.order_by(Avram::OrderBy.new(:email, :desc))
query.statement.should eq "SELECT * FROM users ORDER BY name ASC, birthday ASC, email DESC"
query.args.should eq [] of String

query = new_query
.order_by(:name, :asc)
.order_by(Avram::OrderBy.new(:name, :asc))
query.statement.should eq "SELECT * FROM users ORDER BY name ASC"
end

Expand Down Expand Up @@ -196,7 +196,7 @@ describe Avram::QueryBuilder do
it "reverses the order of the query" do
query = Avram::QueryBuilder
.new(table: :users)
.order_by(:id, :asc)
.order_by(Avram::OrderBy.new(:id, :asc))
.reverse_order

query.statement.should eq "SELECT * FROM users ORDER BY id DESC"
Expand All @@ -205,8 +205,8 @@ describe Avram::QueryBuilder do
it "reverses both directions" do
query = Avram::QueryBuilder
.new(table: :users)
.order_by(:id, :asc)
.order_by(:name, :desc)
.order_by(Avram::OrderBy.new(:id, :asc))
.order_by(Avram::OrderBy.new(:name, :desc))
.reverse_order

query.statement.should eq "SELECT * FROM users ORDER BY name ASC, id DESC"
Expand All @@ -229,7 +229,7 @@ describe Avram::QueryBuilder do
it "returns true if the query is ordered" do
query = Avram::QueryBuilder
.new(table: :users)
.order_by(:id, :asc)
.order_by(Avram::OrderBy.new(:id, :asc))

query.ordered?.should eq true
end
Expand All @@ -248,7 +248,7 @@ describe Avram::QueryBuilder do
.select([:name, :age])
.join(Avram::Join::Inner.new(:users, :posts))
.where(Avram::Where::Equal.new(:name, "Paul"))
.order_by(:id, :asc)
.order_by(Avram::OrderBy.new(:id, :asc))
.limit(1)
.offset(2)
cloned_query = new_query
Expand Down
12 changes: 12 additions & 0 deletions spec/query_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,18 @@ describe Avram::Query do
query = Post::BaseQuery.new.where_comments(Comment::BaseQuery.new.created_at.asc_order)
query.to_sql[0].should contain "ORDER BY comments.created_at ASC"
end

it "orders nulls first" do
query = Post::BaseQuery.new.published_at.asc_order(:nulls_first)

query.to_sql[0].should contain "ORDER BY posts.published_at ASC NULLS FIRST"
end

it "orders nulls last" do
query = Post::BaseQuery.new.published_at.asc_order(:nulls_last)

query.to_sql[0].should contain "ORDER BY posts.published_at ASC NULLS LAST"
end
end

describe "cloning queries" do
Expand Down
8 changes: 4 additions & 4 deletions src/avram/criteria.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class Avram::Criteria(T, V)
@negate_next_criteria = false
end

def desc_order : T
rows.query.order_by(column, :desc)
def desc_order(null_sorting : Avram::OrderBy::NullSorting = :default) : T
rows.query.order_by(Avram::OrderBy.new(column, :desc, null_sorting))
rows
end

def asc_order : T
rows.query.order_by(column, :asc)
def asc_order(null_sorting : Avram::OrderBy::NullSorting = :default) : T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it!

rows.query.order_by(Avram::OrderBy.new(column, :asc, null_sorting))
rows
end

Expand Down
30 changes: 30 additions & 0 deletions src/avram/order_by.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Avram
class OrderBy
enum NullSorting
DEFAULT
NULLS_FIRST
NULLS_LAST

def to_s
super.gsub("_", " ")
end
end

enum Direction
ASC
DESC
end

getter column
getter direction
getter nulls

def initialize(@column : String | Symbol, @direction : Direction, @nulls : NullSorting = :default)
end

def reversed
@direction = @direction.asc? ? Direction::DESC : Direction::ASC
self
end
end
end
46 changes: 17 additions & 29 deletions src/avram/query_builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@ class Avram::QueryBuilder
@wheres = [] of Avram::Where::SqlClause
@raw_wheres = [] of Avram::Where::Raw
@joins = [] of Avram::Join::SqlClause
@orders = {
asc: [] of Symbol | String,
desc: [] of Symbol | String,
}
@orders = [] of Avram::OrderBy
@selections : String = "*"
@prepared_statement_placeholder = 0
@distinct : Bool = false
@delete : Bool = false
@distinct_on : String | Symbol | Nil = nil

VALID_DIRECTIONS = [:asc, :desc]

def initialize(@table : Symbol)
end

Expand All @@ -39,10 +34,8 @@ class Avram::QueryBuilder
join(join)
end

query_to_merge.orders.each do |direction, order_bys|
order_bys.each do |order|
order_by(order, direction)
end
query_to_merge.orders.each do |order|
order_by(order)
end
end

Expand Down Expand Up @@ -135,38 +128,35 @@ class Avram::QueryBuilder
self
end

def order_by(column, direction : Symbol)
raise "Direction must be :asc or :desc, got #{direction}" unless VALID_DIRECTIONS.includes?(direction)
@orders[direction] << column
def order_by(order : OrderBy)
@orders << order
self
end

def reset_order
@orders.values.each(&.clear)
@orders.clear
end

def reverse_order
@orders = {
asc: @orders[:desc],
desc: @orders[:asc],
}
@orders = @orders.map(&.reversed).reverse
self
end

def order_sql
if ordered?
"ORDER BY " + orders.map do |direction, columns|
next if columns.empty?
"#{columns.join(" #{direction.to_s.upcase}, ")} #{direction.to_s.upcase}"
end.reject(&.nil?).join(", ")
String.build do |str|
jwoertink marked this conversation as resolved.
Show resolved Hide resolved
str << "ORDER BY "
orders.each_with_index do |order, index|
str << ", " unless index.zero?
str << "#{order.column} #{order.direction}"
str << " #{order.nulls}" unless order.nulls.default?
end
end
end
end

def orders
{
asc: @orders[:asc].uniq,
desc: @orders[:desc].uniq,
}
@orders.uniq!(&.column)
end

def select_count
Expand Down Expand Up @@ -226,9 +216,7 @@ class Avram::QueryBuilder
end

def ordered?
@orders.values.any? do |columns|
columns.any?
end
@orders.size > 0
jwoertink marked this conversation as resolved.
Show resolved Hide resolved
end

private def select_sql
Expand Down
3 changes: 2 additions & 1 deletion src/avram/queryable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ module Avram::Queryable(T)
end

def order_by(column, direction) : self
query.order_by(column, direction)
direction = Avram::OrderBy::Direction.parse(direction.to_s)
jwoertink marked this conversation as resolved.
Show resolved Hide resolved
query.order_by(Avram::OrderBy.new(column, direction))
self
end

Expand Down