|
|
|
@ -12,38 +12,33 @@ class Quantity < ApplicationRecord
|
|
|
|
|
|
|
|
|
|
scope :defaults, ->{ where(user: nil) }
|
|
|
|
|
scope :ordered, ->{
|
|
|
|
|
cte = Arel::Table.new('cte')
|
|
|
|
|
numbered = Arel::Table.new('numbered')
|
|
|
|
|
Quantity.with(numbered: numbered(:parent_id, :name)).with_recursive(
|
|
|
|
|
cte:
|
|
|
|
|
[
|
|
|
|
|
Quantity.with(numbered: numbered(:parent_id, :name)).with_recursive(quantities: [
|
|
|
|
|
Arel::SelectManager.new.project(
|
|
|
|
|
numbered[Arel.star],
|
|
|
|
|
numbered.cast(numbered[:child_number], 'BINARY').as('path')
|
|
|
|
|
numbered.cast(numbered[:child_number], 'BINARY').as('path'),
|
|
|
|
|
Arel::Nodes.build_quoted(0).as('depth')
|
|
|
|
|
).from(numbered).where(numbered[:parent_id].eq(nil)),
|
|
|
|
|
Arel::SelectManager.new.project(
|
|
|
|
|
numbered[Arel.star],
|
|
|
|
|
cte[:path].concat(numbered[:child_number])
|
|
|
|
|
).from(numbered).join(cte).on(numbered[:parent_id].eq(cte[:id]))
|
|
|
|
|
]
|
|
|
|
|
).select(cte[Arel.star]).from(cte).order(cte[:path])
|
|
|
|
|
arel_table[:path].concat(numbered[:child_number]),
|
|
|
|
|
arel_table[:depth] + 1
|
|
|
|
|
).from(numbered).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
|
|
|
|
|
]).select(arel_table[Arel.star]).from(arel_table).order(arel_table[:path])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# TODO: extract named functions to custom Arel extension
|
|
|
|
|
# https://gist.github.com/ProGM/c6df08da14708dcc28b5ca325df37ceb#extending-arel
|
|
|
|
|
scope :numbered, ->(parent_column, order_column){
|
|
|
|
|
select(
|
|
|
|
|
arel_table[Arel.star],
|
|
|
|
|
Arel::Nodes::NamedFunction.new(
|
|
|
|
|
'LPAD',
|
|
|
|
|
[
|
|
|
|
|
Arel::Nodes::NamedFunction.new(
|
|
|
|
|
'ROW_NUMBER', []
|
|
|
|
|
).over(
|
|
|
|
|
Arel::Nodes::Window.new.partition(parent_column).order(order_column)
|
|
|
|
|
),
|
|
|
|
|
Arel::Nodes::NamedFunction.new('ROW_NUMBER', [])
|
|
|
|
|
.over(Arel::Nodes::Window.new.partition(parent_column).order(order_column)),
|
|
|
|
|
Arel::SelectManager.new.project(
|
|
|
|
|
Arel::Nodes::NamedFunction.new(
|
|
|
|
|
'LENGTH', [Arel::Nodes::NamedFunction.new('COUNT', [Arel.star])]
|
|
|
|
|
)
|
|
|
|
|
Arel::Nodes::NamedFunction.new('LENGTH', [Arel.star.count])])
|
|
|
|
|
),
|
|
|
|
|
Arel::Nodes.build_quoted('0')
|
|
|
|
|
],
|
|
|
|
@ -62,4 +57,24 @@ class Quantity < ApplicationRecord
|
|
|
|
|
def default?
|
|
|
|
|
parent_id.nil?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Return: record, its ancestors and succesive record in order of appearance,
|
|
|
|
|
# including :depth attribute. Used for table view reload.
|
|
|
|
|
def successive
|
|
|
|
|
quantities = Quantity.arel_table
|
|
|
|
|
ancestors = Arel::Table.new('ancestors')
|
|
|
|
|
Quantity.with(
|
|
|
|
|
ancestors: user.quantities.ordered.select(
|
|
|
|
|
Arel::Nodes::NamedFunction.new('LAG', [quantities[:id]]).over.as('lag_id')
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.with_recursive(quantities: [
|
|
|
|
|
Arel::SelectManager.new.project(ancestors[Arel.star]).from(ancestors)
|
|
|
|
|
.where(ancestors[:id].eq(id).or(ancestors[:lag_id].eq(id))),
|
|
|
|
|
Arel::SelectManager.new.project(ancestors[Arel.star]).from(ancestors)
|
|
|
|
|
.join(quantities).on(quantities[:parent_id].eq(ancestors[:id]))
|
|
|
|
|
.where(quantities[:lag_id].not_eq(id))
|
|
|
|
|
]).order(quantities[:path])
|
|
|
|
|
# return: .first == self ? nul, ancestors : ancestors.pop, ancestors
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|