AncestryはActiveRecordと一緒に使うと階層データを簡単に扱うことができますが、そのやり方がちょっとおもしろいのです。
次のような階層的データを作ってみました。

+---+----+---------+
|id |name|ancestry |
+---+----+---------+
| 1 | A  |         |
| 2 | B  | 1       |
| 3 | C  | 1/2     |
| 4 | D  | 1/2/3   |
| 5 | E  | 1/2/3   |
| 6 | F  | 1/2/3/4 |
| 7 | G  | 1       |
| 8 | H  | 1/7     |
+---+----+---------+

階層を記録しているancestryはString型になっています。なぜString型にしているのか不思議だったのですが、

require "active_record"
require "ancestry"

ActiveRecord::Base.establish_connection(
    adapter: "sqlite3",
    database: "test_ancestry/db/development.sqlite3"
    )

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

class Tree < ApplicationRecord
    has_ancestry
end

ActiveRecord::Base.logger = Logger.new(STDOUT)
p tc=Tree.find_by(name: "C")
p tc.ancestors
p tc.descendants

実行してみますと
p tc=Tree.find_by(name: "C")

DEBUG -- :   Tree Load (0.6ms)  SELECT  "trees".* FROM "trees" WHERE "trees"."name" = ? LIMIT ?  [["name", "C"], ["LIMIT", 1]]
#<Tree id: 3, name: "C", ancestry: "1/2">

上の階層を取り出すと
p tc.ancestors

DEBUG -- :   Tree Load (0.5ms)  SELECT  "trees".* FROM "trees" WHERE "trees"."id" IN (1, 2) ORDER BY coalesce("trees"."ancestry", '') LIMIT ?  [["LIMIT", 11]]
#<ActiveRecord::Relation [#<Tree id: 1, name: "A", ancestry: nil>, #<Tree id: 2, name: "B", ancestry: "1">]>

ancestryが "1/2"であるからidが1と2を取り出しています。

下の階層を取り出すのはどうするのだろうか?
複数の階層があるのですが、
p tc.descendants

 DEBUG -- :   Tree Load (0.2ms)  SELECT  "trees".* FROM "trees" WHERE ("trees"."ancestry" LIKE '1/2/3/%' OR "trees"."ancestry" = '1/2/3') LIMIT ?  [["LIMIT", 11]]
#<ActiveRecord::Relation [#<Tree id: 4, name: "D", ancestry: "1/2/3">, #<Tree id: 5, name: "E", ancestry: "1/2/3">, #<Tree id: 6, name: "F", ancestry: "1/2/3/4">]>

なるほど、LIKE '1/2/3/%'と'1/2/3'とすることで、一度に取り出しています。
ancestryがString型なので簡単にできるのか。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.