2018.11.18
団体用集金システムを作りたい!#13【閉包テーブルによる木構造表現】
いまひとつ筆が進んでないきらいがありますけど、開発していきまっしょい!!!
前回、閉包テーブルで階層構造データを管理することを決めたわけですが、ここでネックになるのが
深度(depth)の計算です。
この深度カラムがないと、テーブルから階層構造を読み取るのが非常にめんどくさいことになるそうですね。
(たぶん計算で出せますけどちょっとめんどくさそう・・・・・・)
ということで、ちょっと図示して書いてみたんですよ。
こんな感じの絵を描いたんですけど、例えば金管グループ(ID:6)の下にHr(ID:17)を挿入することを考えます。
で、出来上がるテーブルの中身が一番右下の数字なんですけど、これ、親となるグループのancestorをそれぞれパクってきて、depthにそれぞれ1を足し、descendantに自分自身としたものを挿入すれば問題なさそうなんですよね。
中途半端に木構造の中に入れるパターンが存在しないので、これはらくちんですね。
あともうひとつ、あると便利そうで厄介そうな機能が
移動ですね。
これを実装せずに済むと非常にラクなのですが、やはり将来性のことも考えたら実装してあげるのがベターかなと・・・・・・。というわけで、ロジックを考えてみましょう。
とりあえずググったんですけど、使えそうな実装がなかなかヒットしない・・・・・・。
僕はあんまり高級なSQL文は使えないので、とりあえず
閉包テーブルの構造をモデル化したクラスを作り、そこから操作するという手段でなんとかしようかなと思います。
例えば、新規追加の場合は、まず元となる親ノードをdescendantに持つレコードを配列として取得し、それをオブジェクトに格納し、あと一緒に新規追加する予定のノードのIDを取得します。
んで、先程説明したように、親レコードのancestor値とdepthをコピってきて、depthは+1した上でdescendantは先程取得してきたIDに書き換え、それをそのままツリーテーブルにinsertすれば完成です。
移動に関しては
「削除」→「追加」の流れで実装可能です。追加に関してはロジックの使い回しができそうです。
と、口で言うのは簡単そうですけど、ちょっと考えて管理しないとバグっちゃいそーな気がするのでこれはちゃんと設計しましょうね(笑)
その過程で、
「削除」に関してちょっと問題が吹き出してきました。
まず、基本的に
「関連するレコードをすべて削除すればいい」ということなのですが、単純に親ノードを削除すれば自動的に子ノードも削除されると思いきや、そーでもないのですね。
つまり、一旦
「関連するノードのIDをすべて取ってくる」という動作が必要になります。
これを論理的に言い換えると、
「削除するノードのID(A)」をancestorに持つノードのIDを含むレコードを削除するという操作が必要になります。
えーと、これはサブクエリで実装可能なんですかね?
ただ、この(A)を用いて、ツリーではなくその中身のテーブルも同時に削除しなきゃいけないので、結局これはサブクエリじゃなくて一回クエリとして処理して配列を受け取って処理するのがいいんですかね・・・・・・。
で、この問題に真剣に取り組んだ結果、解法が見えてきました。
つまり、移動に関しては
「親」のIDのみを書き換え、深度もそれに合わせて書き換え、元のデータを削除してから挿入するという操作をすれば実現可能です。
移動後の親フォルダを「子」に持つものの一覧を取ってきて、その深度の最大値を+1したものとの差を取ればいい感じに整合性が取れそうですね。
これいい感じにライブラリ化できたらいいんだけどなぁ・・・・・・ちょっと試せないかな(笑)
ということで方針が定まってきたので、次回また何か書くことがあれば書きます。