LoginSignup
31
23

More than 5 years have passed since last update.

Rails で利用可能な PostgreSQL 特有の型について

Last updated at Posted at 2016-12-07

この記事は Ruby on Rails Advent Calendar の 7日目の記事です。

この記事では、Ruby on Rails がサポートしている PostgreSQL の型を紹介した上で
その中でも特にマイナーな ltree 型について詳しく紹介します。

PostgreSQL で使える型

実は、Ruby on Rails では PostgreSQL 特有の多数の型を利用可能です。

例として、下記のような型が利用可能です。

概要
daterange 日付の範囲
numrange Numeric 型の範囲
tsrange Timestamp (Ruby の DateTime相当)の範囲
tstzrange  タイムゾーンつき Timestamp の範囲
int4range integer(32bit 整数)の範囲
int8range bigint(64bit 整数)の範囲
binary 巨大なバイナリ文字列。内部的には bytea 型
xml XML 型
tsvector テキスト検索に最適化された文字列の型
hstore キー、値の組合せを意味する型
inet IPv4 アドレスまたはネットワーク
cidr IPv4 ネットワーク
macaddr Macアドレス
uuid RFC4122 で定義された Universally Unique Identifier
json JSON 型。入力されたテキストを内部的にそのまま保存する
jsonb JSON 型。入力されたテキストを解析して、バイナリ形式で保存する
ltree Label Tree を表現するデータ型
citext 大文字、小文字を区別しないテキスト型
point 平面における座標点。幾何データ型の1つ。
line 無限長の直線。幾何データ型の1つ。
lseg 線分。幾何データ型の1つ。
box 矩形。幾何データ型の1つ。
path 経路。幾何データ型の1つ。
polygon 多角形。幾何データ型の1つ。
circle 円。幾何データ型の1つ。
bit 固定長のビット列データ型。limit: 5 なら長さ5のビット列。
bit_varying 可変長のビット列データ型。 limit: 7 なら最大長 7 のビット列
money 貨幣金額を固定精度の小数点で格納するデータ型

全部を網羅的に説明すると大変ですので、上記の中で特に誰にも知られてなさそうな ltree データ型を例に紹介します。

ltree

ltree とは

ltree とは、階層的なラベルデータを表現するデータ型です。

下記の例では、Top が最上位で、 Top.Science Top.Science.Astronomy などがその子孫の
Label として存在しています。このように . 区切りで英数字の Label を並べることで、Label Path を表現します。

Ruby on Rails での ltree の利用

まず、ltree を利用可能なように準備します

$ bin/rails dbconsole
psql> CREATE EXTENSION ltree;

次にテーブルを作成します

$ bin/rails g model test path:ltree

インデックスを作るように編集します。

db/migrate/20161206150631_create_tests.rb
class CreateTests < ActiveRecord::Migration[5.0]
  def change
    create_table :tests do |t|
      t.ltree :path

      t.timestamps
    end
    add_index :tests, :path, using: :gist, name: "index_tests_path_gist"
    add_index :tests, :path, using: :btree, name: "index_tests_path_btree"
  end
end

t.ltree と指定することで、ltree 型の列 path を定義しています。
今回は GiSTインデックスと B-Tree インデックスの両方を設定しているので、インデックスの名前が重複しないうに、あえて name を指定しています。

$ bin/rake db:migrate:up VERSION=20161206150631
Running via Spring preloader in process 27159
== 20161206150631 CreateTests: migrating ======================================
-- create_table(:tests)
   -> 0.0587s
-- add_index(:tests, :path, {:using=>:gist, :name=>"index_tests_path_gist"})
   -> 0.0049s
-- add_index(:tests, :path, {:using=>:btree, :name=>"index_tests_path_btree"})
   -> 0.0405s
== 20161206150631 CreateTests: migrated (0.1044s) =============================

次にレコードを追加します。

%w(
Top
Top.Science
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
Top.Hobbies
Top.Hobbies.Amateurs_Astronomy
Top.Collections
Top.Collections.Pictures
Top.Collections.Pictures.Astronomy
Top.Collections.Pictures.Astronomy.Stars
Top.Collections.Pictures.Astronomy.Galaxies
Top.Collections.Pictures.Astronomy.Astronauts
).each do |path|
  Test.create(path: path)
end

下記は、Top.Science の子孫のみを抽出する例です。

> bin/rails console
irb> Test.where("path <@ ?", "Top.Science").map &:path
  Test Load (0.6ms)  SELECT "tests".* FROM "tests" WHERE (path <@ 'Top.Science')
=> ["Top.Science", "Top.Science.Astronomy", "Top.Science.Astronomy.Astrophysics", "Top.Science.Astronomy.Cosmology"]

下記は、 Astronomy を経由する場合のみを抽出する例です。

irb> Test.where("path ~ ?", "*.Astronomy.*").map &:path
  Test Load (1.2ms)  SELECT "tests".* FROM "tests" WHERE (path ~ '*.Astronomy.*')
=> ["Top.Science.Astronomy", "Top.Science.Astronomy.Astrophysics", "Top.Science.Astronomy.Cosmology", "Top.Collections.Pictures.Astronomy", "Top.Collections.Pictures.Astronomy.Stars", "Top.Collections.Pictures.Astronomy.Galaxies", "Top.Collections.Pictures.Astronomy.Astronauts"]

簡単に、 Astronomy を経由するレコードを一度に取得することができました。
このように ltree を使うと、階層構造を簡単に扱うことができて、便利です。

まとめ

今回、Ruby on Rails では多数の PostgreSQL 特有の型が扱えることを紹介しました。
さらに、PostgreSQL 特有の型の中でも特にマイナーな ltree 型を例に詳しく使い方を紹介しました。
ltree 型を使うことで、階層的なデータに対して、子孫や祖先、共通の経路などの条件で、簡単に該当するレコードを取得できます。

参考文献

PostgreSQL ドキュメント 付録 F. 追加で提供されるモジュール F21 ltree
Rails Guide: ActiveRecord and PostgreSQL

31
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
23