KiokuDB-0.32 > KiokuDB::Tutorial

名前

KiokuDB::Tutorial - Getting started with KiokuDB

KiokuDB::Tutorial - KiokuDBを始めよう

インストール

The easiest way to install KiokuDB and a number of backends is Task::KiokuDB.

KiokuDBとバックエンドを一緒にインストールするには、Task::KiokuDBをインストールするのが一番簡単です。

KiokuDB depends on Moose and a few other modules out of the box, but no specific storage module.

KiokuDBMooseと、いくつかのすぐに使えるモジュールに依存していますが、 特定のストレージモジュールには依存していません。

KiokuDB is a frontend to several backends, much like DBI uses DBDs to connect to actual databases.

KiokuDBは複数のバックエンドのフロントエンドです。 DBIが実際のデータベースへの接続にDBDを使っているのに似ています。

For development and testing you can use the KiokuDB::Backend::Hash backend, which is an in memory store, but for production KiokuDB::Backend::BDB or KiokuDB::Backend::DBI are the recommended backends.

開発用やテストとして、メモリに保存するKiokuDB::Backend::Hashバックエンドを使うことができます。 プロダクションには、KiokuDB::Backend::DBDKiokuDB::Backend::DBIをバックエンドとして推奨します。

See below for instructions on getting KiokuDB::Backend::BDB installed.

KiokuDB::Backend::DBDをインストールして、以下のインストラクションを見てください。

ディレクトリの作成

A KiokuDB directory is the object that contains all the common functionality regardless of the backend.

KiokuDBディレクトリはオブジェクトで、バックエンド以外のすべての共通の機能を含みます。

The simplest directory ready for use can be created like this:

すぐに使えるもっとも単純なディレクトリは次のように作れます:

    my $dir = KiokuDB->new(
        backend => KiokuDB::Backend::Hash->new
    );

We will revisit other more interesting backend configuration later in this document, but for now this will do.

このドキュメントの最後に、他のもっと面白いバックエンドの設定を紹介しますが、 とりあえず、やってみます。

You can also use DSN strings to connect to the various backends:

いろいろなバックエンドに接続するためのDSN文字列を使うこともできます。

    KiokuDB->connect("hash");

    KiokuDB->connect("dbi:SQLite:dbname=foo", create => 1);

    KiokuDB->connect("bdb:dir=foo", create => 1);

Or use a configuration file

または、設定ファイルでも

    KiokuDB->connect("/path/to/my_db.yml");
    KiokuDB->connect("/path/to/dir");

With a configuration file like this:

設定ファイルは次のようになります:

    backend:
      class: KiokuDB::Backend::DBI
      dsn: dbi:SQLite:dbname=/tmp/test.db
      create: 1

DBIバックエンドを使う

During this tutorial we will be using the DBI backend for two reasons. The first is DBI's ubiquity - almost everyone has used and knows how to install and use it. The second the possibility of easily looking behind the scenes, to more clearly demonstrate what KiokuDB is doing.

2つの理由で、このチュートリアルではDBIバックエンドを使います。 1つ目の理由は、DBIがどこにでもあるからです - ほとんどすべての人がインストール方法も 使い方も知っています。2つ目の理由は、簡単に裏舞台を見ることが出来るからです。 KiokuDBが何をしているかをよりわかりやすくデモンストレーションできるからです。

That said, the examples will work with all backends exactly the same.

この例ですべてのバックエンドがまったく同じように動きます。

The $dir variable used below is created like this:

以下で使う$dir変数は下記のように作られます:

    my $dir = KiokuDB->connect(
        "dbi:SQLite:dbname=kiokudb_tutorial.db",
        create => 1,
    );

Note that if you are connecting with a username and password you must specify these as named arguments:

ユーザー名とパスワードで接続する場合、名前付きの引数を指定しないといけません:

    my $dir = KiokuDB->connect(
        $dsn,
        user     => $user,
        password => $password,
    );

オブジェクトのインサート

Let's start by defining a simple class using Moose:

Mooseを使った簡単なクラスを定義してみましょう:

    package Person;
    use Moose;

    has name => (
        isa => "Str",
        is  => "rw",
    );

We can instantiate it:

それをインスタント化します:

    my $obj = Person->new( name => "Homer Simpson" );

and insert the object to the database as follows:

下記のようにオブジェクトをデータベースに入れます:

    my $scope = $dir->new_scope;

    my $homer_id = $dir->store($obj);

This is very trivial use of KiokuDB, but it illustrates a few important things.

これは、KiokuDBのとても普通の使い方です。ですが、いくつか重要なことを示しています。

First, no schema is necessary. KiokuDB can use Moose to introspect your object without needing to predefine anything like tables.

1番目に、スキーマは必要ありません。KiokuDBはテーブルのような何かを事前に定義する必要はありません。 オブジェクトの情報を取り出すために、Mooseを使うことができます。

Second, every object in the database has an ID. If you don't choose an ID for an object, KiokuDB will assign a UUID instead. The ID is like a primary key in a relational database. If you want to choose an ID for your object, you can do something like:

2番目に、データベースに入っているすべてのオブジェクトにはIDがあります。 オブジェクトにIDを選ばなけれあば、KiokuDBが代わりにUUIDを割り当てます。 IDはリレーショナルデータベースのプライマリーキーのようなものです。 自分でオブジェクトにIDを振りたければ、次のようにすることができます:

    $dir->store( homer => $obj );

and $obj's ID will be homer. If you don't provide an ID a UUID will be assigned automatically.

これで、$objのIDはhomerになります。IDを与えなければ、UUIDが自動的にふられます。

Third, all KiokuDB operations need to be performed within a scope. The scope does not apply to a simple example like the above, but becomes necessary once weak references are used. We will look into that in more detail later.

3番目に、すべてのKiokuDB操作はscope内で行う必要があります。 スコープは上のような簡単な例には適しませんが、weakリファレンスが使われるようになると、 必要になります。後でより詳細に見ていきます。

オブジェクトの読み出し

So now that Homer has been inserted into the database, we can fetch him out of there using the ID we got from store.

さて、データベースにHomerが入りました。storeから得たIDで取り出せます。

    my $homer = $dir->lookup($homer_id);

Assuming that $scope and $obj are still in scope, $homer and $obj will actually be the same reference:

$scope$objは、スコープ内にあるとします。$homer$objは実際に、同じリファレンスになります。

    refaddr($homer) == refaddr($obj)

This is because KiokuDB tracks which objects are "live" in the live object set (KiokuDB::LiveObjects).

生存しているオブジェクトセット (KiokuDB::LiveObjects)内のオブジェクトが "生存"しているかをKiokuDBが追跡しているからです。

If $obj and $scope are no longer in scope you'd need to create a new scope, and then fetch the object from the database again:

$obj$scopeが、もうスコープにいなければ、新しいスコープを作らなければいけません。 それから、再びデータベースからオブジェクトを取り出します:

    my $scope = $dir->new_scope;

    my $homer = $dir->lookup($homer_id);

In this case since the original instance of Homer is no longer live, but has been garbage collected by Perl, KiokuDB will fetch it from the backend.

このケースではHomerのオリジナルのインスタンスはもはや生きておらず、 Perlによりガベージコレクトされています。 KiokuDBはインスタンスをバックエンドから取得します。

何が保存されたか

Let's peek into the database momentarily. Launch the SQL command line tool to your database:

すぐにデータベースを覗いてみましょう。SQLコマンドラインツールを起動しましょう:

    % sqlite3 kiokudb_tutorial.db
    SQLite version 3.4.0
    Enter ".help" for instructions
    sqlite>

The database schema has two tables, entries and gin_index:

データベースのスキーマには2つのテーブルがあります。entriesgin_indexです:

    sqlite> .tables
    entries    gin_index

gin_index is used for more complex queries, and we'll get back to it at the end of the tutorial.

gin_indexはより複雑なクエリに使われます。チュートリアルの最後に扱います。

Let's have a closer look at entries:

entriesをよく見ましょう:

    sqlite> .schema entries
    CREATE TABLE entries (
      id varchar NOT NULL,
      data blob NOT NULL,
      class varchar,
      root boolean NOT NULL,
      tied char(1),
      PRIMARY KEY (id)
    );

The main columns are id and data. In KiokuDB every object has an ID which serves as a primary key and a BLOB of data associated with it.

メインのカラムはiddataです。KiokuDBにある、すべてのオブジェクトにはIDがあり、 プライマリキーとBLOBデータが関連付けられています。

Since the default serializer for the DBI backend is KiokuDB::Serializer::JSON, we can peek at the data.

DBIバックエンドのデフォルトのシリアライザーはKiokuDB::Serializer::JSONですので、 データを覗き見ることができます。

First we'll set sqlite's output mode to line. This is easier to read with large columns:

最初に、sqliteの出力モードをlineにセットしてください。大きいカラムでも見やすくなります:

    sqlite> .mode line

And select the data from the table:

テーブルからデータを取得します:

    sqlite> select id, data from entries;
       id = 201C5B55-E759-492F-8F20-A529C7C02C8B
     data = {"__CLASS__":"Person","data":{"name":"Homer Simpson"},"id":"201C5B55-E759-492F-8F20-A529C7C02C8B","root":true}

As you can see the name attribute is stored under the data key inside the blob, as is the object's class.

上記のように、name属性はblob内のdataキーにオブジェクトのクラスとして保存されています。

The data column contains all of the data necessary to recreate the object.

dataカラムはオブジェクトを再作成するのに必要なすべてのデータを含んでいます。

All the other columns are used solely for lookups. Later on we'll show how to create more search columns.

他のすべてのカラムは単に検索のために使われます。後で、どのように検索用のカラムを作るのかを見せます。

When using KiokuDB::Backend::BDB the on-disk format is actually a hash of id to data.

KiokuDB::Backend::DBDを使った場合は、ディスク上のフォーマットは、実際には、idからdataのハッシュになります。

オブジェクトのリレーションシップ

Let's extend the Person class to hold some more interesting data than just a name:

Personクラスにnameよりも、もっと面白いデータを追加してみましょう:

    package Person;

    has spouse => (
        isa => "Person",
        is  => "rw",
        weak_ref => 1,
    );

This new spouse attribute will hold a reference to another person object.

spouse属性は他のPersonオブジェクトのリファレンスを持ちます。

Let's first create and insert another object:

まずは、他のオブジェクトを作りましょう:

    my $marge_id = $dir->store(
        Person->new( name => "Marge Simpson" ),
    );

Now that we have both objects in the database, let's link them together:

データベースに両方のオブジェクトを持たせます。2つを一緒にリンクしましょう:

    {
        my $scope = $dir->new_scope;

        my ( $marge, $homer ) = $dir->lookup( $marge_id, $homer_id );

        $marge->spouse($homer);
        $homer->spouse($marge);

        $dir->store( $marge, $homer );
    }

Now we have created a persistent object graph, that is several objects which point to each other.

今、永続的なオブジェクトグラフを作りました。これは、複数のオブジェクトが お互いに参照しています。

The reason spouse had the weak_ref option was so that this circular structure will not leak.

spouseにはweak_refオプションがありましたので、この循環構造はリークしません。

When then objects are updated in the database, KiokuDB sees that their spouse attribute contains references, and this relationship will be encoded using their unique ID in storage.

データベースでオブジェクトが更新されたら、LinkDBspouse属性を含むリファレンスを見て、 この関係はストレージ内でユニークなIDを使ってエンコードされます。

To load the graph, we can do something like this:

このグラフをロードするために、次のようにできます:

    {
        my $scope = $dir->new_scope;

        my $homer = $dir->lookup($homer_id);

        print $homer->spouse->name; # Marge Simpson
    }

    {
        my $scope = $dir->new_scope;

        my $marge = $dir->lookup($marge_id);

        print $marge->spouse->name; # Homer Simpson

        refaddr($marge) == refaddr($marge->spouse->spouse); # true
    }

When KiokuDB is loading the initial object, all the objects the object depends on will also be loaded. The spouse attribute contains a reference to another object (by ID), and this link is resolved at inflation time.

KiokuDBが最初のオブジェクトをロードしたら、そのオブジェクトが依存している すべてのオブジェクトがロードされます。spouse属性は他のオブジェクトを(IDで) 持っているので、インフレーション時にそのリンクを解決します。

The purpose of new_scope

(new_scopeの目的)

This is where new_scope becomes important. As objects are inflated from the database, they are pushed onto the live object scope, in order to increase their reference count.

new_scopeが重要になるところです。オブジェクトはデータベースからインフレートされ、 リファレンスカウントを増やすために、生存しているオブジェクトスコープに追加されます。

If this was not done, by the time $homer was returned from lookup his spouse attribute would have been cleared because there is no other reference to Marge.

これがされていなければ、lookupから$homerが戻ってくる時までに、 spouse属性がクリアされます。マージする他のリファレンスがないからです。

If, on the other hand the circular structure was not weak, it would have to be broken manually, which is very error prone.

もし、一方で、循環構造がweakでなければ、手で壊さなければいけません。 これは、とてもエラーになりやすいです。

By using this idiom:

次のイディオムを使って:

    {
        my $scope = $dir->new_scope;

        # do all KiokuDB work in here
    }

You are ensuring that the objects live at least as long as is necessary.

少なくとも必要である時間はオブジェクトが生きていることを確保できます。

In a web application context usually you create one new scope per request.

Webアプリケーションのコンテキストでは、普通リクエストごとに新しいスコープを作ります。

While scopes can nest, this is not a requirement.

スコープがネストできるなら、必須ではありません。

You are free to create as many or as few scopes as you like, as long as there is at least one, but note that child scopes refer to their parents to ensure that all objects that were already live at the time that a scope is created are still alive.

少なくとも一つのスコープがあれば、好きなだけ多くの、または、少ないスコープを作ることができます。 その時に作られたスコープにすでにあるすべてのオブジェクトを確実にする親を参照している子供のスコープもまだ生きています。

データベース内のリファレンス

Now that we have an object graph in the database let's have another look at what's inside.

さて、データベースにオブジェクトグラフがあります。内部がどうなっているか見てみましょう。

    sqlite> select id, data from entries;
       id = 201C5B55-E759-492F-8F20-A529C7C02C8B
     data = {"__CLASS__":"Person","data":{"name":"Homer Simpson","spouse":{"$ref":"05A8D61C-6139-4F51-A748-101010CC8B02.data"}},"id":"201C5B55-E759-492F-8F20-A529C7C02C8B","root":true}

       id = 05A8D61C-6139-4F51-A748-101010CC8B02
     data = {"__CLASS__":"Person","data":{"name":"Marge Simpson","spouse":{"$ref":"201C5B55-E759-492F-8F20-A529C7C02C8B.data"}},"id":"05A8D61C-6139-4F51-A748-101010CC8B02","root":true}

You'll notice the spouse field has a JSON object with a $ref field inside it holding the UUID of the target object.

spouseフィールドがJSONオブジェクトということに気づくでしょう。 そして、その内部の$refフィールドには、対象のオブジェクトのUUIDがあります。

When data is loaded KiokuDB queues up references to unloaded objects and then loads them in order to materialize the memory resident object graph.

データがロードされると、KiokuDBはロードさえていないオブジェクトへのリファレンスを キューに入れて、オブジェクトグラフをメモリに常駐させるために、それらをロードします。

If you're curious about why the data is represented this way, this format is called JSPON, or JavaScript Persistent Object Notation (http://www.jspon.org/). When using KiokuDB::Backend::Storable the KiokuDB::Entry and KiokuDB::Reference objects are serialized with their storable hooks instead.

データがこのような方法で表現されている理由について知りたければ、 このフォーマットは、JPSONか JavaScript Persistent Object notation(http://www.jpson.org)と呼ばれています。 KiokuDB::Backend::Storableを使うと、KiokuDB::EntryKiokuDB::Referenceオブジェクトは、 代わりに、storableフックでシリアライズされます。

オブジェクトセット

More complex relationships (not necessarily 1 to 1) are fairly easy to model with Set::Object.

より複雑なリレーションシップ(1対1に限らない)は、Set::Objectでかなり簡単にモデル化できます。

Let's extend the Person class to add such a relationship:

Personクラスを拡張してそのようなリレーションシップを足してみましょう:

    package Person;

    has children => (
        does => "KiokuDB::Set",
        is   => "rw",
    );

KiokuDB::Set objects are KiokuDB specific wrappers for Set::Object.

KiokuDB::Setオブジェクトは、Set::ObjectKiokuDB用のラッパーです。

    my @kids = map { Person->new( name => $_ ) } qw(maggie lisa bart);

    use KiokuDB::Util qw(set);

    my $set = set(@kids);

    $homer->children($set);

    $dir->store($homer);

The set convenience function creates a new KiokuDB::Set::Transient object. A transient set is one which started its life in memory space.

setという便利な関数は新しいKiokuDB::Set::Transientオブジェクトを作ります。 一時的なセットはメモリスペースに存在するものです。

The weak_set convenience function also exists, creating a transient set with Set::Object::Weak used internally to help avoid circular structures (for instance if setting a parent attribute in our example).

weak_setという便利な関数もあります。 循環構造(例えば、今の例にparent属性を追加する)を避けるために内部で使われている、 Set::Object::Weakで一時的なセットを作ります。

The set object behaves pretty much like a normal Set::Object:

このオブジェクトは普通のSet::Objectとほとんど同じように振る舞います。

    my @kids = $dir->lookup($homer_id)->children->members;

The main difference is that sets coming from the database are deferred by default, that is the objects in @kids are not loaded until they are actually needed.

主な違いは、セットがデータベースから来るのがデフォルトで遅延されていることです。 @kidsにあるオブジェクトは、実際に必要になるときまでロードされません。

This allows large object graphs to exist in the database, while only being partially loaded, without breaking the encapsulation of user objects. This behavior is implemented in KiokuDB::Set::Deferred and KiokuDB::Set::Loaded.

このことにより、ユーザーのオブジェクトのカプセル化を壊すこと無しに、 部分的にロードされるので、データベースに巨大なオブジェクトグラフがあっても問題になりません。 この振る舞いはKiokuDB::Set::DefferedKiokuDB::Set::Loadedで実装されています。

This set object is optimized to make most operations defer loading. For instance, if you intersect two deferred sets, only the members of the intersection set will need to be loaded.

このセットオブジェクトは、遅延ロードの操作に最適化されています。 例えば、2つの遅延セットを横断するなら、横断するセットのみがロードされる必要があります。

THE TYPEMAP

Storing an object with KiokuDB involves passing it to KiokuDB::Collapser, the object that "flattens" objects into KiokuDB::Entry before the entries are inserted into the backend.

KiokuDBにオブジェクトが保存される際に、KiokuDB::Collapserを通過します。 エントリーがバックエンドにインサートされる前に、KiokuDB::Entryに、 "平たく"されたオブジェクトを入れます。

The collapser uses a KiokuDB::TypeMap object that tells it how objects of each type should be collapsed.

collapserには、KiokuDB::TypeMapオブジェクトを使います。このオブジェクトは、 それぞれのタイプのオブジェクトがどのように破壊するかを教えます。

During retrieval of objects the same typemap is used to reinflate objects back into working objects.

オブジェクトを取ってくる間、オブジェクトを再インフレートして、 ワーキングオブジェクトにするのに、同じtypemapが使われます。

Trying to store an object that is not in the typemap is an error. The reason behind this is that many objects depend on runtime states (for instance DBI handles need a socket, objects based on XS modules have an internal pointer as an integer), and even though the majority of objects are safe to serialize, even a small bit of unreported fragility is usually enough to create large, hard to debug problems.

typemapにないオブジェクトを保存しようとするとエラーになります。 ランタイムの状態に依存する多くのオブジェクトがあるためです(例えば、DBIはソケット、オブジェクト。 XSベースのモジュールは数値のような内部的なポインタを持ちます)。 大半のオブジェクトは安全にシリアライズできるにもかかわらず、 わずかな報告されないもろさが、大きなデバッグの難しい問題を作るのはありがちなことです。

An exception to this rule is Moose based objects, because they have sufficient meta information available through Moose's powerful reflection support in order to be safely serialized.

このルールの例外は、Mooseベースのオブジェクトです。Mooseの強大な リフレクションサポートを通して、十分なメタ情報が利用できるので、 安全にシリアライズ出来ます。

Additionally, the standard backends provide a default typemap for common objects (DateTime, Path::Class, etc), which by default is merged with any custom typemap you pass to KiokuDB.

加えて、標準のバックエンドは共通のオブジェクト(DateTime, Path::Classなど>)用に デフォルトのtypemapを提供しています。KiokuDBにどんなカスタムのtypemapが渡されても、 デフォルトとマージされます。

So, in order to actually get KiokuDB to store things like Class::Accessor based objects, you can do something like this:

それで、実際にKiokuDBClass::Accessorベースのオブジェクトのようなものを保存させるには、 次のようにします:

    my $dir = KiokuDB->new(
        backend => $backend,
        typemap => KiokuDB::TypeMap->new(
            entries => {
                "My::Object" => KiokuDB::TypeMap::Entry::Naive->new,
            },
        ),
    );

KiokuDB::TypeMap::Entry::Naive is a type map entry that performs naive collapsing of the object, by simply walking it recursively.

KiokuDB::TypeMap::Entry::Naiveは単純に再帰的にたどることで、 オブジェクトのナイーブな破壊を行います。

When the collapser encounters an object it will ask KiokuDB::TypeMap::Resolver for a collapsing routine based on the class of the object.

collapser は、オブジェクトを見つけると、KiokuDB::TypeMap::Resolverに、 オブジェクトのクラスに応じた、破壊ルーチンを尋ねます。

This lookup is typically performed by ref $object, not using inheritance, because a typemap entry that is safe to use with a superclass isn't necessarily safe to use with a subclass. If you do want inherited entries, specify isa_entries:

この検索は、典型的には、ref $objectで行われ、継承を使いません。 スーパークラスで安全に使われているtypemapエントリーは、 必ずしもサブクラスで安全に使えるとは限らないからです。 継承されたエントリーにしたいなら、isa_entriesを指定してください。

    KiokuDB::TypeMap->new(
        isa_entries => {
            "My::Object" => KiokuDB::TypeMap::Entry::Naive->new,
        },
    );

If no normal (ref keyed) entry is found for an object, the isa entries are searched for a superclass of that object. Subclass entries are tried before superclass entries. The result of this lookup is cached, so it only happens once per class.

オブジェクトに通常の(ref keyed)エントリーが見つからなければ、 isaエントリーがオブジェクトスーパークラスのために探されます。 サブクラスエントリーはスーパークラスエントリーより前に試されます。 この検索の結果はキャッシュされるので、クラスごとに一回しか起こりません。

Typemap Entries

If you want to do custom serialization hooks, you can specify hooks to collapse your object:

カスタムのシリアライズのフックが欲しければ、自分のオブジェクトを破壊するための フックを指定できます。

    KiokuDB::TypeMap::Entry::Callback->new(
        collapse => sub {
            my $object = shift;

            ...

            return @some_args;
        },
        expand => sub {
            my ( $class, @some_args ) = @_;

            ...

            return $object;
        },
    );

These hooks are called as methods on the object to be collapsed.

これらのフックはオブジェクトを破壊するときに、メソッドとして呼ばれます。

For instance the Path::Class related typemap ISA entry is:

例えば、typemapのISAに関連するPath::Classは:

    'Path::Class::Entity' => KiokuDB::TypeMap::Entry::Callback->new(
        intrinsic => 1,
        collapse  => "stringify",
        expand    => "new",
    );

The intrinsic flag is discussed in the next section.

intrinsicフラグは次のセクションで述べます。

Another option for typemap entries is KiokuDB::TypeMap::Entry::Passthrough, which is appropriate when you know the backend's serialization can handle that data type natively.

typemapエントリのもう一つの選択はKiokuDB::Typemap::Entry::Passthroughです。 バックエンドのシリアライズがネイティブにデータタイプを扱うことができると分かっていれば、 これは適切です。

For example, if your object has a Storable hook which you know is appropriate (e.g. contains no sub objects that need to be collapsible) and your backend uses KiokuDB::Backend::Serialize::Storable. DateTime is an example of a class with such storable hopes:

例えば、オブジェクトに適切なStorableフックがあり(破壊する必要のあるサブオブジェクトを含まない)、 バックエンドには、KiokuDB::Backend::Serialize::Storableを使う場合です。 DateTimeはそのようにstorableが望むクラスの例です:

    'DateTime' => KiokuDB::Backend::Entry::Passthrough->new( intrinsic => 1 )

Intrinsic vs. First Class

In KiokuDB every object is normally assigned an ID, and if the object is shared by several objects this relationship will be preserved.

KiokuDBでは、すべてのオブジェクトに、通常、IDが割り当てられます。 オブジェクトが複数のオブジェクトに共有されている場合、このリレーションは維持されます。

However, for some objects this is not the desired behavior. These are objects that represent values, like DateTime, Path::Class entries, URI objects, etc.

しかし、いくつかのオブジェクトは望ましい振る舞いをしません。 それらは、DateTimeや、Path::Classエントリ、URIオブジェクトのようなもので、 値を表現します。

KiokuDB can be asked to collapse such objects intrinsicly, that is instead of creating a new KiokuDB::Entry with its own ID for the object, the object gets collapsed directly into its parent's structures.

KiokuDBintrinsiclyに、そのようなオブジェクトを、 そのオブジェクトにそれ自身のIDと新しいKiokuDB::Entryを作る代わりに、 破壊するよう要求できます。オブジェクトが直接破壊できれば、親の構造の中に入ります。

This means that shared references that are collapsed intrinsically will be loaded back from the database as two distinct copies, so updates to one will not affect the other.

破壊され、共有されたリファレンスは、もともと2つの区別されたコピーとして データーベースからロードされます。ですので、一つをアップデートしても、 もう一方には影響がありません。

For instance, when we run the following code:

例えば、下記のようなコードを動かしたとして:

    use Path::Class;

    my $path = file(qw(path to foo));

    $obj_1->file($path);

    $obj_2->file($path);

    $dir->store( $obj_1, $obj_2 );

While the following is true when the data is being inserted, it will no longer be true when $obj_1 and $obj_2 are loaded from the database:

データがインサートされるときには、下記は真ですが、 $obj_1$obj_2がデーターベースからロードされると、もはや真ではありません:

    refaddr($obj_1->file) == refaddr($obj_2->file)

This is because both $obj_1 and $obj_2 each got its own copy of $path.

$obj_1$obj_2の両方が$pathのコピーだからです。

This behavior is usually more appropriate for objects that aren't mutated, but are instead cloned and replaced, and for which creating a first class entry in the backend with its own ID is undesired.

この現象は、通常、変異されず、複製されたり置き換えられたりするオブジェクトに適しています。 そのようなオブジェクトのためには、最初のクラスエントリが独自のIDでバックエンドに作られるのは、 望まれていないからです。

The Default Typemap

Each backend comes with a default typemap, with some built in entries for common CPAN modules' objects. KiokuDB::TypeMap::Default contains more details.

それぞれのバックエンドには、デフォルトのtypemapがついています。 それには、共通のCPANモジュールオブジェクトのために、いくつか共通のビルトインのエントリもあります。 KiokuDB::TypeMap::Defaultにより詳細があります。

単純な検索

Most backends support an inefficient but convenient simple search, which scans the entries and matches fields.

ほとんどのバックエンドが効率的ではないものの、便利な単純な検索があります。 ろえは、エントリをスキャンして、フィールドにマッチさせます。

If you want to make use of this API we suggest using KiokuDB::Backend::DBI since simple searching is implemented using an SQL where clause, which is much more efficient (you do have to set up the column manually though).

このAPIを使いたいなら、KiokuDB::Backend::DBIを使うことをおすすめします。 単純亜検索はSQLのwhere節を使って実装でき、より効率的だからです。 (ただし、手でカラムをセットアップしないといけませんが)

Calling the search method with a hash reference as the only argument invokes the simple search functionality, returning a Data::Stream::Bulk with the results:

searchメソッドに引数としてハッシュリファレンスのみを渡して呼びます。 単純な検索機能が呼び出され、Data::Stream::Bulkが結果と一緒に戻ってきます:

    my $stream = $dir->search({ name => "Homer Simpson" });

    while ( my $block = $stream->next ) {
        foreach my $object ( @$block ) {
            # $object->name eq "Homer Simpson"
       }
    }

This exact API is intentionally still underdefined. In the future it will be compatible with DBIx::Class 0.09's syntax.

正確なAPIはまだ決められていません。将来的に、DBIx::Class 0.09のシンタックスと 互換にするつもりです。

DBI SEARCH COLUMNS

In order to make use of the simple search API we need to configure columns for our DBI backend.

この簡単な検索APIを使うには、DBIバックエンドにカラムを設定しなければいけません。

Let's create a 'name' column to search by:

検索するために、'name'カラムを作りましょう:

    my $dir = KiokuDB->connect(
        "dbi:SQLite:dbname=foo",
        columns => [
            # specify extra columns for the 'entries' table
            # in the same format you pass to DBIC's add_columns

            name => {
                data_type => "varchar",
                is_nullable => 1, # probably important
            },
        ],
    );

You can either alter the schema manually, or use kioku dump to back up your data, delete the database, connect with create => 1 and then use kioku load.

スキーマを手で変更することもできますし、また、データをバックアップするのに、kioku dumpを使い、 データベースを削除し、create => 1で接続し、kioku loadを使うことも出来ます。

To populate this column we'll need to load Homer and update him:

このカラムを埋め込むために、Homerをロードして、更新する必要があります:

    {
        my $s = $dir->new_scope;
        $dir->update( $dir->lookup( $homer_id ) );
    }

And this is what it looks in the database:

データベースでは次のようになります:

       id = 201C5B55-E759-492F-8F20-A529C7C02C8B
     name = Homer Simpson

BDBを始めよう

The most mature backend for KiokuDB is KiokuDB::Backend::BDB. It performs very well, and supports many features, like Search::GIN integration to provide customized indexing of your objects and transactions.

KiokuDBでもっとも成熟したバックエンドは、KiokuDB::Backend::DBDです(訳注:DBIのほうが安定しているとYAPC::Asia 2009で聞きました)。 十分に動きますし、多くの機能をサポートします。 オブジェクトのインデックスのカスタマイズやトランザクションを提供する Search::GINのようなインテグレーションもあります。

KiokuDB::Backend::DBI is newer and not as tested, but also supports transactions and Search::GIN based queries. It performs quite well too, but isn't as fast as KiokuDB::Backend::BDB.

KiokuDB::Backend::DBIはより新しいですが、そこまでテストされていません。 ですが、トランザクションもサポートしますし、クエリベースのSearch::GINもあります。 これも、なかなかよく動きます。ですが、KiokuDB::Backend::BDBと同じくらい速くはありません (訳注:YAPC::Asia 2009では、ほぼ変わらないと聞きました)

Installing KiokuDB::Backend::BDB

KiokuDB::Backend::BDB needs the BerkeleyDB module, and a recent version of Berkeley DB itself, which can be found here: http://www.oracle.com/technology/software/products/berkeley-db/db/index.html.

KiokuDB::Backend::BDBは、BerkeleyDBモジュールが必要です。 また、最近のバージョンのBerkeley DB自身も必要です。Berkeley DBは、以下のURLにあります。 http://www.oracle.com/technology/software/products/berkeley-db/db/index.html.

BerkeleyDB (the library) normally installs into /usr/local/BerkeleyDB.4.7, while BerkeleyDB (the module) looks for it in /usr/local/BerkeleyDB, so adding a symbolic link should make installation easy.

BerkeleyDB(ライブラリ)は通常、/usr/local/BerkeleyDB.4.7にインストールされます。 ですが、BerkeleyDB(モジュール)は、/usr/local/BerkeleyDBを見ようとします。 ですので、シンボリックリンクを作っておけば、インストールが簡単になります。

Once you have BerkeleyDB installed, KiokuDB::Backend::BDB should install without problem and you can use it with KiokuDB.

BerkeleyDBがインストールできれば、KiokuDB::Backend::BDBは問題なくインストールできるはずです。 KiokuDBと一緒に使うことができます。

Using KiokuDB::Backend::BDB

To use the BDB backend we must first create the storage. To do this the create flag must be passed:

BDBバックエンドを使うために、ストレージを作らなければいけません。 このために、createフラグを渡さなければいけません。

    my $backend = KiokuDB::Backend::BDB->new(
        manager => {
            home   => Path::Class::Dir->new(qw(path to storage)),
            create => 1,
        },
    );

The BDB backend uses BerkeleyDB::Manager to do a lot of the BerkeleyDB gruntwork. The BerkeleyDB::Manager object will be instantiated using the arguments provided in the manager attribute.

BDBバックエンドは、BerkeleyDB::Managerを使って、たくさんのBerkeleyDBの下働きを行います。 BerkeleyDB::Managerオブジェクトはmanager属性で提供される引数を使って、インスタンス化されます。

Now that the storage is created we can make use of this backend, much like before:

これで、ストレージがつくられました。このバックエンドを、以前と同様に使います。

    my $dir = KiokuDB->new( backend => $backend );

Subsequent opens will not require the create argument to be true, but it doesn't hurt.

その後のオープンには、create属性が真である必要はありませんが、真であっても特に害はありません。

This connect call is equivalent to the above:

このconnectは上記のものと同じです:

    my $dir = KiokuDB->connect( "bdb:dir=path/to/storage", create => 1 );

トランザクション

Some backends (ones which do the KiokuDB::Backend::Role::TXN role) can be used with transactions.

いくつかのバックエンド(KiokuDB::Backend::Role::TXNロールをするもの)は、トランザクションが使えるものがあります。

If you are familiar with DBIx::Class this should be very familiar:

DBIx::Classに慣れているなら、すぐわかるでしょう:

    $dir->txn_do(sub {
        $dir->store($obj);
    });

This will create a BerkeleyDB level transaction, and all changes to the database are committed if the block was executed cleanly.

BerkeleyDBレベルのトランザクションを作ります。データベースへのすべての変更は ブロックが綺麗に実行されたら、コミットされます。

If any error occurred the transaction will be rolled back, and the changes will not be visible to subsequent reads.

何らかのエラーが起きれば、トランザクションはロールバックされます。 変更は次の読み込みでは、見えません。

Note that KiokuDB does not touch live instances, so if you do something like

KiokuDB生きているインスタンスには触れません。ですので、次のようにすると

    $dir->txn_do(sub {
        my $scope = $dir->new_scope;

        $obj->name("Dancing Hippy");
        $dir->store($obj);

        die "an error";
    });

the name attribute is not rolled back, it is simply the store operation that gets reverted.

name属性はロールバックされませんstoreオペレーションだけが、元に戻ります。

Transactions will nest properly, and with most backends they generally increase write performance as well.

トランザクションは適切にネストできます。また、ほとんどのバックエンドで、一般的に 書き込みのパフォーマンスが良くなります。

クエリ

KiokuDB::Backend::BDB::GIN is a subclass of KiokuDB::Backend::BDB that provides Search::GIN integration.

KiokuDB::Backend::BDB::GINKiokuDB::Backend::BDBのサブクラスで、 Serach::GINインテグレーションを提供しています。

Search::GIN is a framework to index and query objects, inspired by Postgres' internal GIN api. GIN stands for Generalized Inverted Indexes.

Search::GINはインデックスとクエリーオブジェクトのフレームワークです。 Postgresの内部GIN apiにインスパイアされました。 GINは、Generalized Inverted Indexes(訳注:汎用転置索引)の略です。

Using Search::GIN arbitrary search keys can be indexed for your objects, and these objects can then be looked up using queries.

Search::GINを使うと、任意の検索キーをオブジェクトにタイしてインデックスできます。 そして、それらのオブジェクトをクエリで検索できます。

For instance, one of the pre canned searches Search::GIN supports out of the box is class indexing. Let's use Search::GIN::Extract::Callback to do custom indexing of our objects:

例えば、Search::GINがサポートする、すぐに使える、予めある検索の一つに、クラスインデックスがあります。 Search::GIN::Extract::Callback を使って、オブジェクトにカスタムのインデックスを作りましょう:

    my $dir = KiokuDB->new(
        backend => KiokuDB::Backend::BDB::GIN->new(
            extract => Search::GIN::Extract::Callback->new(
                extract => sub {
                    my ( $obj, $extractor, @args ) = @_;

                    if ( $obj->isa("Person") ) {
                        return {
                            type => "user",
                            name => $obj->name,
                        };
                    }

                    return;
                },
            ),
        ),
    );

    $dir->store( @random_objects );

To look up the objects, we use the a manual key lookup query:

オブジェクトを検索するために、マニュアルキー検索クエリを使います:

    my $query = Search::GIN::Query::Manual->new(
        values => {
            type => "person",
        },
    );

    my $stream = $dir->search($query);

The result is Data::Stream::Bulk object that represents the search results. It can be iterated as follows:

結果として、検索結果を表すData::Stream::Bulkオブジェクトが返ります。 次のようにイテレートできます。

    while ( my $block = $stream->next ) {
        foreach my $person ( @$block ) {
            print "found a person: ", $person->name;
        }
    }

Or even more simply, if you don't mind loading the whole resultset into memory:

また、より単純に、メモリに全結果をロードしてもかまわないなら:

    my @people = $stream->all;

Search::GIN is very much in its infancy, and is very under documented. However it does work for simple searches such as this and contains pre canned solutions like Search::GIN::Extract::Class.

Search::GINはまだ未成熟です。ドキュメントも書いているところです。 ですが、このような単純な検索は動きますし、Search::GIN::Extract::Classのような 予めある解決を含んでいます。

In short, it works today, but watch this space for new developments.

つまり、現在は動きますが、新しく開発をするときには、これに注意してください。

翻訳について

翻訳者:加藤敦 (ktat@cpan.org)

Perlドキュメント日本語訳 Project にて、 Perlモジュール、ドキュメントの翻訳を行っております。

 http://perldocjp.sourceforge.jp/
 http://sourceforge.jp/projects/perldocjp/
 http://www.freeml.com/ctrl/html/MLInfoForm/perldocjp@freeml.com
 http://www.perldoc.jp/