forked from fixin.me/fixin.me
Persist Quantity :depth instead of computing it on the fly
This commit is contained in:
parent
30686dd1fc
commit
c908063212
@ -17,8 +17,46 @@ class Quantity < ApplicationRecord
|
|||||||
length: {maximum: type_for_attribute(:name).limit}
|
length: {maximum: type_for_attribute(:name).limit}
|
||||||
validates :description, length: {maximum: type_for_attribute(:description).limit}
|
validates :description, length: {maximum: type_for_attribute(:description).limit}
|
||||||
|
|
||||||
# Not persisted attribute
|
# Update `depth`s of progenies after parent change
|
||||||
attribute :depth, :integer
|
after_update if: :depth_previously_changed? do
|
||||||
|
quantities = Quantity.arel_table
|
||||||
|
selected = Arel::Table.new('selected')
|
||||||
|
|
||||||
|
self.class.connection.update(
|
||||||
|
Arel::UpdateManager.new.table(
|
||||||
|
Arel::Nodes::JoinSource.new(
|
||||||
|
quantities,
|
||||||
|
[
|
||||||
|
quantities.create_join(
|
||||||
|
# TODO: user .with(quanities: user.quantities) once the '?' problem is fixed
|
||||||
|
Quantity.with_recursive(selected: [
|
||||||
|
quantities.project(quantities[:id], quantities[:depth])
|
||||||
|
.where(quantities[:id].eq(id).and(quantities[:user_id].eq(user.id))),
|
||||||
|
quantities.project(quantities[:id], selected[:depth] + 1)
|
||||||
|
.join(selected).on(selected[:id].eq(quantities[:parent_id]))
|
||||||
|
]).select(selected[Arel.star]).from(selected).arel.as('selected'),
|
||||||
|
quantities.create_on(quantities[:id].eq(selected[:id]))
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
).set(quantities[:depth] => selected[:depth]),
|
||||||
|
"#{self.class} Update All"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent=(value)
|
||||||
|
super
|
||||||
|
self[:depth] = parent&.depth&.succ || 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent_id=(value)
|
||||||
|
super
|
||||||
|
self[:depth] = parent&.depth&.succ || 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def depth=(value)
|
||||||
|
raise ActiveRecord::ReadonlyAttributeError
|
||||||
|
end
|
||||||
|
|
||||||
scope :defaults, ->{ where(user: nil) }
|
scope :defaults, ->{ where(user: nil) }
|
||||||
|
|
||||||
@ -30,12 +68,10 @@ class Quantity < ApplicationRecord
|
|||||||
numbered.project(
|
numbered.project(
|
||||||
numbered[Arel.star],
|
numbered[Arel.star],
|
||||||
numbered.cast(numbered[:child_number], 'BINARY').as('path'),
|
numbered.cast(numbered[:child_number], 'BINARY').as('path'),
|
||||||
Arel::Nodes.build_quoted(root&.depth || 0).as('depth')
|
|
||||||
).where(root.nil? ? numbered[:parent_id].eq(nil) : numbered[:id].eq(root.id)),
|
).where(root.nil? ? numbered[:parent_id].eq(nil) : numbered[:id].eq(root.id)),
|
||||||
numbered.project(
|
numbered.project(
|
||||||
numbered[Arel.star],
|
numbered[Arel.star],
|
||||||
arel_table[:path].concat(numbered[:child_number]),
|
arel_table[:path].concat(numbered[:child_number]),
|
||||||
arel_table[:depth] + 1
|
|
||||||
).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
|
).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
|
||||||
]).order(arel_table[:path])
|
]).order(arel_table[:path])
|
||||||
}
|
}
|
||||||
@ -93,18 +129,10 @@ class Quantity < ApplicationRecord
|
|||||||
selected = Arel::Table.new('selected')
|
selected = Arel::Table.new('selected')
|
||||||
|
|
||||||
model.with(selected: self).with_recursive(arel_table.name => [
|
model.with(selected: self).with_recursive(arel_table.name => [
|
||||||
selected.project(selected[Arel.star], Arel::Nodes.build_quoted(0).as('depth'))
|
selected.project(selected[Arel.star]).where(selected[:id].eq(of)),
|
||||||
.where(selected[:id].eq(of)),
|
selected.project(selected[Arel.star])
|
||||||
# Ancestors are listed bottom up, so it's impossible to know depth at the
|
|
||||||
# start. Start with depth = 0 and count downwards, then adjust by the
|
|
||||||
# amount needed to set biggest negative depth to 0.
|
|
||||||
selected.project(selected[Arel.star], arel_table[:depth] - 1)
|
|
||||||
.join(arel_table).on(selected[:id].eq(arel_table[:parent_id]))
|
.join(arel_table).on(selected[:id].eq(arel_table[:parent_id]))
|
||||||
]).select(
|
])
|
||||||
arel_table[Arel.star],
|
|
||||||
(arel_table[:depth] + Arel::SelectManager.new.project(Arel.star.count).from(arel_table) - 1)
|
|
||||||
.as('depth')
|
|
||||||
).order(arel_table[:depth])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Return: ancestors of (possibly destroyed) self
|
# Return: ancestors of (possibly destroyed) self
|
||||||
|
@ -5,6 +5,7 @@ class CreateQuantities < ActiveRecord::Migration[7.2]
|
|||||||
t.string :name, null: false, limit: 31
|
t.string :name, null: false, limit: 31
|
||||||
t.text :description
|
t.text :description
|
||||||
t.references :parent, foreign_key: {to_table: :quantities}
|
t.references :parent, foreign_key: {to_table: :quantities}
|
||||||
|
t.integer :depth, null: false, default: 0
|
||||||
|
|
||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
@ -16,6 +16,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_04_194343) do
|
|||||||
t.string "name", limit: 31, null: false
|
t.string "name", limit: 31, null: false
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.bigint "parent_id"
|
t.bigint "parent_id"
|
||||||
|
t.integer "depth", default: 0, null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.index ["parent_id"], name: "index_quantities_on_parent_id"
|
t.index ["parent_id"], name: "index_quantities_on_parent_id"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user