hitode909の日記

趣味はマリンスポーツですの日記です

スキーマレスなオブジェクトたちからスキーマを推測するやつ

MongoDBなどはスキーマレスなデータベースであり,先にスキーマ決めなくても,何でもつっこめることになってる.
データベースから見ればスキーマレスということでいいけど,アプリケーション的には,何が入ってるかちゃんと管理したい.

下の例では,AliceとBobでは持ってるフィールドがちがって,Bobはhobbyを持ってるけど,Aliceは持ってない.

{
    name => 'Alice',
    age => 20,
}
{
    name => 'Bob',
    age => 21,
    hobbies => ['tennis', 'soccer'],
}


これくらいなら見れば分かるけど,長期間運用してて,結局何が入ってるのか分からない,みたいなことがあって,難しかった.
オブジェクトをどんどん渡していくと構造を教えてくれるのを作った.


ObjectClassifierのインスタンスを作って,調べたいオブジェクトを渡していく.

my $oc = ObjectClassifier->new;

$oc->add({
    name => 'Alice',
    age => 20,
});
$oc->add({
    name => 'Bob',
    age => 21,
    hobbies => ['tennis', 'soccer'],
});

print Dumper $oc->classify->dump;

dumpとかやると,こういう構造でした,というのを教えてくれる.

{
  'name(100%)' => 'String(100%)',
  'hobbies(50%)' => [
                      'String(100%)'
                    ],
  'age(100%)' => 'Number(100%)'
};

nameとageは必ずあるけど,hobbiesはない場合もあるとか,nameは必ずStringであるとか,そういう感じのことが分かる.

これくらいなら見ればわかるけど,長年雑に扱ってたデータベースなどに対して使うと便利.
雑なデータベースから全件selectしてみて順番にaddしてdumpすればこれまでスキーマを管理していなかったオブジェクトたちの様子を見れる,といったかんじ.
MongoDBなどだけでなく,MySQLになぜかJSONとかYAMLとかで値をつっこんでる事例があるとしたら,そういうのを調べるのにも使える.
出力のフォーマットこんなのでいいかとか,こういうのが出ると便利とか,議論の余地がある.
MongoDBならMongoidのクラスが生成されるとか.
せっかくならバリデータが生成されるとかになると便利かもしれない.


一般的な話っぽく書いたけど,Perlから値入れたのを調べたかったので,1か0か''かundefならBoolとか判定してる.そのへんはユースケースに応じて変えるとよさそう.たとえばURL としてパースできたらURLが入ってるとか,そういうルールを追加するとよいと思う.

仕組み

ObjectClassifier::Class::Bool とか ObjectClassifier::Class::Number みたいな,あるオブジェクトがそのクラスに属するか分類してくれるオブジェクトがいて,それらに,addされたオブジェクトを渡していく.
$number->accept(1)は受理されるけど,$number->accept('a')は受理されない.
最終的に,一番高確率で受理したクラスを採用する.
ArrayのときはArrayのメンバも見る,Hashのときは各フィールドごとに再帰的に調べていく,みたいな感じ.
全体で300行くらいな割に便利だと思う.