Arel: Add support for FILTER clause (SQL:2003)
Currently supported by PostgreSQL 9.4+ and SQLite 3.30+ See: - http://modern-sql.com/feature/filter - https://www.postgresql.org/docs/9.4/static/sql-expressions.html#SYNTAX-AGGREGATES - https://sqlite.org/lang_aggfunc.html#aggfilter Example: Model.all.pluck( Arel.star.count.as('records_total').to_sql, Arel.star.count.filter(Model.arel_table[:some_column].not_eq(nil)).as('records_filtered').to_sql, )
This commit is contained in:
parent
c0911e9a36
commit
a738295805
@ -1,3 +1,23 @@
|
|||||||
|
* Arel: Add support for FILTER clause (SQL:2003)
|
||||||
|
|
||||||
|
Currently supported by PostgreSQL 9.4+ and SQLite 3.30+
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
Model.all.pluck(
|
||||||
|
Arel.star.count.as('records_total').to_sql,
|
||||||
|
Arel.star.count.filter(Model.arel_table[:some_column].not_eq(nil)).as('records_filtered').to_sql,
|
||||||
|
)
|
||||||
|
|
||||||
|
Example result:
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS records_total,
|
||||||
|
COUNT(*) FILTER (WHERE "some_column" IS NOT NULL) AS records_filtered
|
||||||
|
FROM models
|
||||||
|
|
||||||
|
*Andrey Novikov*
|
||||||
|
|
||||||
* Two change tracking methods are added for `belongs_to` associations.
|
* Two change tracking methods are added for `belongs_to` associations.
|
||||||
|
|
||||||
The `association_changed?` method (assuming an association named `:association`) returns true
|
The `association_changed?` method (assuming an association named `:association`) returns true
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
require "arel/expressions"
|
require "arel/expressions"
|
||||||
require "arel/predications"
|
require "arel/predications"
|
||||||
|
require "arel/filter_predications"
|
||||||
require "arel/window_predications"
|
require "arel/window_predications"
|
||||||
require "arel/math"
|
require "arel/math"
|
||||||
require "arel/alias_predication"
|
require "arel/alias_predication"
|
||||||
|
9
activerecord/lib/arel/filter_predications.rb
Normal file
9
activerecord/lib/arel/filter_predications.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
module FilterPredications
|
||||||
|
def filter(expr)
|
||||||
|
Nodes::Filter.new(self, expr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -28,6 +28,7 @@
|
|||||||
# binary
|
# binary
|
||||||
require "arel/nodes/binary"
|
require "arel/nodes/binary"
|
||||||
require "arel/nodes/equality"
|
require "arel/nodes/equality"
|
||||||
|
require "arel/nodes/filter"
|
||||||
require "arel/nodes/in"
|
require "arel/nodes/in"
|
||||||
require "arel/nodes/join_source"
|
require "arel/nodes/join_source"
|
||||||
require "arel/nodes/delete_statement"
|
require "arel/nodes/delete_statement"
|
||||||
|
10
activerecord/lib/arel/nodes/filter.rb
Normal file
10
activerecord/lib/arel/nodes/filter.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
module Nodes
|
||||||
|
class Filter < Binary
|
||||||
|
include Arel::WindowPredications
|
||||||
|
include Arel::AliasPredication
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -4,6 +4,7 @@ module Arel # :nodoc: all
|
|||||||
module Nodes
|
module Nodes
|
||||||
class Function < Arel::Nodes::NodeExpression
|
class Function < Arel::Nodes::NodeExpression
|
||||||
include Arel::WindowPredications
|
include Arel::WindowPredications
|
||||||
|
include Arel::FilterPredications
|
||||||
attr_accessor :expressions, :alias, :distinct
|
attr_accessor :expressions, :alias, :distinct
|
||||||
|
|
||||||
def initialize(expr, aliaz = nil)
|
def initialize(expr, aliaz = nil)
|
||||||
|
@ -245,6 +245,13 @@ def visit_Arel_Nodes_Window(o, collector)
|
|||||||
collector << ")"
|
collector << ")"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def visit_Arel_Nodes_Filter(o, collector)
|
||||||
|
visit o.left, collector
|
||||||
|
collector << " FILTER (WHERE "
|
||||||
|
visit o.right, collector
|
||||||
|
collector << ")"
|
||||||
|
end
|
||||||
|
|
||||||
def visit_Arel_Nodes_Rows(o, collector)
|
def visit_Arel_Nodes_Rows(o, collector)
|
||||||
if o.expr
|
if o.expr
|
||||||
collector << "ROWS "
|
collector << "ROWS "
|
||||||
|
37
activerecord/test/cases/arel/nodes/filter_test.rb
Normal file
37
activerecord/test/cases/arel/nodes/filter_test.rb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative "../helper"
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
module Nodes
|
||||||
|
class ::FilterTest < Arel::Spec
|
||||||
|
describe "Filter" do
|
||||||
|
it "should add filter to expression" do
|
||||||
|
table = Arel::Table.new :users
|
||||||
|
_(table[:id].count.filter(table[:income].gteq(40_000)).to_sql).must_be_like %{
|
||||||
|
COUNT("users"."id") FILTER (WHERE "users"."income" >= 40000)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "as" do
|
||||||
|
it "should alias the expression" do
|
||||||
|
table = Arel::Table.new :users
|
||||||
|
_(table[:id].count.filter(table[:income].gteq(40_000)).as("rich_users_count").to_sql).must_be_like %{
|
||||||
|
COUNT("users"."id") FILTER (WHERE "users"."income" >= 40000) AS rich_users_count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "over" do
|
||||||
|
it "should reference the window definition by name" do
|
||||||
|
table = Arel::Table.new :users
|
||||||
|
window = Arel::Nodes::Window.new.partition(table[:year])
|
||||||
|
_(table[:id].count.filter(table[:income].gteq(40_000)).over(window).to_sql).must_be_like %{
|
||||||
|
COUNT("users"."id") FILTER (WHERE "users"."income" >= 40000) OVER (PARTITION BY "users"."year")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user