LoginSignup
1
0

More than 3 years have passed since last update.

【Rails】ActiveRecordによるBiTemporalDataModel

Last updated at Posted at 2020-07-13

はじめに

RailsでBitemporalDataModelを扱いたかったのですがactiverecord-bitemporalという良いgemがあったので、
いろいろ触ってみた結果を書いていきたいと思います。
今回の記事はactiverecord-bitemporalの紹介です。

BitemporalDataModelとは何か

そもそもBiTemporalとはどういう意味でしょうか。
「Bi」は接頭辞で2つのという意味を表します、Bicycleとか言いますよね。
「Temporal」は時間のという意味の形容詞です。
つまりBiTemporalで二つの時間のという意味になり、
BiTemporalDataModelは二つの時間のデータモデルになります。

二つの時間は何かと言いますと、
「システム上の時間」「事実情報としての時間」です。

詳しくは説明すると長くなってしまうので、
こちらのスライドが参考になると思います。(特に33ページ目以降)

使い方

レコードの作成、更新

ではactiverecord-bitemporalを使ってレコードを作成したり更新したりをしていきたいと思います。

まずは準備です。詳しくはこちら

テーブルを用意しましょう

db/schema.rb
ActiveRecord::Schema.define(version: 1) do
  create_table :employees, force: true do |t|
    t.string :name #従業員名
    t.string :position #役職

    # ActiveRecord::BiTemporal に必要なカラムを追加する
    t.integer :bitemporal_id
    t.datetime :valid_from #適用日時
    t.datetime :valid_to #終了日時
    t.datetime :deleted_at #削除日時(論理削除)
  end
end

ActiveRecord::Bitemporalの読み込みの設定をします。

app/models/emoloyee.rb
class Employee < ActiveRecord::Base
  include ActiveRecord::Bitemporal
end
では、実際に使っていきたいと思います

employeesというテーブルを作って、従業員の情報をDBに保存していくような例を考えます。

具体的には、

2018年1月1日 田中さんが平社員として入社し、2018年1月3日にレコード作成

2020年1月1日 課長に昇進、 2020年1月10日にレコード作成

2020年1月20日 課長に更新したと思ったが、間違えて家長と入力していたので課長に修正

という例を考えます。

rails consoleで操作していきます。
※簡単のために〇〇時〇〇分〇〇秒は省略しています。(実際には秒まで入ります。)

まずは最初のレコード作成、

2018年1月1日田中さんが平社員として入社

Employee.create(name: "田中", position: "平社員", valid_from: "2018-01-01")

valid_fromには適用日時を指定できます。
何も指定しなければ入力時点現在の時刻となります。
valid_toも指定できますが、今回指定していないので9999年12月31日となっています。

この時DBは以下のようになります。

id bitemporal_id name position valid_from valid_to created_at deleted_at
1 1 田中 平社員 2018-01-01 9999-12-31 2018-01-3 NULL
次に、

2020年1月1日 課長に昇進

Employee.valid_at("2020-01-01".to_date).find_by(bitemporal_id: 1).update(position: "課長")

レコードの更新をするとき、適用日時を指定したい場合はvalid_atメソッドで適用日時を指定してからレコードを作成します。
valid_atメソッドで時間を指定しなかった場合、valid_fromは入力時点現在の時刻となります。

ちなみにEmployee.find_by(bitemporal_id: 1).update(position: "課長", valid_from: "2020-01-01")としても意味はなく、valid_fromは入力時点現在の時刻となります。
この辺りの仕組みは時間があればソースコードを確認しましょう。

このときDBは以下のようになります。

id bitemporal_id name position valid_from valid_to created_at deleted_at
1 1 田中 平社員 2018-01-01 9999-12-31 2018-01-3 2020-01-10
2 1 田中 平社員 2018-01-01 2020-01-01 2020-01-10 NULL
3 1 田中 課長 2020-01-01 9999-12-31 2020-01-10 NULL
次に、

2020年1月20日 課長に更新したと思ったが、間違えて家長と入力していたので課長に修正

前の部分では課長として更新しましたが誤字のため家長と入力し、それを修正していく場合の時を考えます。

Employee.find_by(bitemporal_id: 1, position: "家長").force_update do |employee| 
   employee.update(position: "課長") 
end

この場合は、updateではなく、force_updateを使います。
家長と入力したレコードは論理削除され、課長のレコードが新しく作られます。

このときDBは以下のようになります。

id bitemporal_id name position valid_from valid_to created_at deleted_at
1 1 田中 平社員 2018-01-01 9999-12-31 2018-01-3 2020-01-10
2 1 田中 平社員 2018-01-01 2020-01-01 2020-01-10 NULL
3 1 田中 家長 2020-01-01 9999-12-31 2020-01-20
4 1 田中 課長 2020-01-01 00:00:00 9999-12-31 2020-01-20 NULL

もしこのとき、論理削除をして新しいレコードを作成するのではなく、強制的に上書きしたい場合は

Employee.find_by(bitemporal_id: 1, position: "家長").force_update do |employee| 
  employee.update_column(position: "課長") 
end

としましょう。

レコードの検索

後日、執筆予定

1
0
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
1
0