Skip to content

Commit

Permalink
Add Queryable#group_count functionality
Browse files Browse the repository at this point in the history
 By using `Model.group(&.column).group_count`, it allows for a Hash of
 mapped values to their respective counts

 Resolves #777
  • Loading branch information
grepsedawk committed Jan 1, 2022
1 parent 9b184d4 commit 63effb0
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
91 changes: 91 additions & 0 deletions spec/avram/queryable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,97 @@ describe Avram::Queryable do
end
end

describe "#group_count" do
context "when no records exist" do
it "0 grouped columns returns { [] => 0 }" do
query = UserQuery.new

query.group_count.should eq({ [] of String => 0 })
end

# Return an empty set when grouped, which mirrors
# postgresql handling of empty aggregated results
it "1 grouped columns returns { [] => 0 }" do
query = UserQuery.new.group(&.name)

query.group_count.should eq({} of Array(PG::PGValue) => Int64)
end

it "2 grouped columns returns { [] => 0 }" do
query = UserQuery.new.group(&.name)

query.group_count.should eq({} of Array(PG::PGValue) => Int64)
end
end

context "when 1 record exists" do
before_each do
UserFactory.create do |user|
user.age(32)
user.name("Taylor")
end
end

it "0 grouped columns returns { [] => 1 }" do
query = UserQuery.new

query.group_count.should eq({ [] of String => 1 })
end

it "1 grouped columns (age) returns grouping" do
query = UserQuery.new.group &.age

query.group_count.should eq({ [32] => 1 })
end

it "1 grouped columns (age, name) returns grouping" do

query = UserQuery.new.group(&.age).group(&.name)

query.group_count.should eq({ [32, "Taylor"] => 1 })
end
end

context "when matrix [32, Daniel] [32, Taylor] [32, Taylor] [44, Shakira]" do
before_each do
UserFactory.create do |user|
user.age(32)
user.name("Daniel")
end
UserFactory.create do |user|
user.age(32)
user.name("Taylor")
end
UserFactory.create do |user|
user.age(32)
user.name("Taylor")
end
UserFactory.create do |user|
user.age(44)
user.name("Shakira")
end
end

it "0 grouped columns returns { [] => 4 }" do
query = UserQuery.new

query.group_count.should eq({ [] of String => 4 })
end

it "1 grouped columns (age) returns grouping" do
query = UserQuery.new.group &.age

query.group_count.should eq({ [32] => 3, [44] => 1 })
end

it "1 grouped columns (age, name) returns grouping" do
query = UserQuery.new.group(&.age).group(&.name)

query.group_count.should eq({ [32, "Daniel"] => 1, [32, "Taylor"] => 2, [44, "Shakira"] => 1 })
end
end
end

describe "#not" do
context "with an argument" do
it "negates the given where condition as 'equal'" do
Expand Down
5 changes: 5 additions & 0 deletions src/avram/query_builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ class Avram::QueryBuilder
.map(&.split('.').last)
end

def select_direct(selection : Array(ColumnName))
@selections = selection.join(", ")
self
end

def select(selection : Array(ColumnName))
@selections = selection.join(", ") { |column| "#{@table}.#{column}" }
self
Expand Down
16 changes: 16 additions & 0 deletions src/avram/queryable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,22 @@ module Avram::Queryable(T)
end
end

# Remove when PG::PGValue matches rs.read possibilities
alias PGValue = Bool | Float32 | Float64 | Int32 | Int64 | PG::Numeric | String | Time | UUID | Nil

def group_count : Hash(Array(PGValue), Int64)
database.query_all(
query.select_direct(query.groups + ["COUNT(*)"]).statement,
args: query.args,
queryable: schema_class.name,
) do |rs|
{
query.groups.map { rs.read PGValue },
rs.read Int64,
}
end.to_h
end

def each
results.each do |result|
yield result
Expand Down

0 comments on commit 63effb0

Please sign in to comment.