コンテンツにスキップ

Ref と Join

Ref<T> は高レベル API の参照型です。別のエンティティを安定した行 ID または主キーから解決するための情報を持ち、table binding を通して参照先を遅延読み込みできます。

参照される型には RefTraits<T> が必要です。AKKARADB_ENTITY はこれを生成します。

struct Author {
uint64_t id;
std::string name;
};
AKKARADB_ENTITY(Author, id, name);
struct Post {
uint64_t id;
akkaradb::Ref<Author> author;
std::string title;
};
AKKARADB_ENTITY(Post, id, author, title);
authors.put({1, "Alice"});
posts.put({100, akkaradb::ref<Author>(1), "Hello"});

table または schema が Author の binding を持っていれば、ref は必要になった時点で遅延解決できます。

auto post = posts.get(100);
auto name = post->author->name;

結合は走査と検索の上にある型付き読み取りビューです。

auto rows = posts
.join<&Post::author>(authors)
.where([](const Post& post, const Author& author) {
return author.name == "Alice";
})
.toVector();

plain field 同士で join する場合は、左右両方の field を指定します。

auto rows = posts.join<&PlainPost::authorId, &Author::id>(authors).toVector();

各結合行は左側の Entry と復号済みの右側エンティティを持ちます。where()first()any()count()toVector() は結合ビュー上で評価されます。

owner row が primary key 変更後も同じ logical target を追跡してほしい場合は Ref<T> を使います。見えている key を保存して比較するだけなら plain foreign-key field の方が単純です。

参照フィールドには join<&RefField>(right)、通常フィールドには join<&LeftField, &RightField>(right) を使います。参照関係が多い大規模な処理では、頻繁に使う経路を明示的なインデックスやアプリケーション層の実体化ビューとして設計する方が向いています。

状態意味
key knownid() が row-id lookup なしで返せます。
row id knownrowId() が返せるか、metadata から解決できます。
loadedoperator->operator* で値へアクセスできます。
dirtyowner を保存する前に、参照先の値も保存する必要があります。
attached遅延解決に使う table binding が利用できます。

完全なエンティティから Ref<T> を作ると未保存状態になります。PackedTable::put() は所有エンティティを書き込む前に未保存の参照を保存します。

table は pk2rowrow2pk metadata を維持するため、Ref<T> は primary key の変更を跨いでも安定します。

authors.updatePrimaryKey(1, Author{10, "Alice Cooper"});

見えている主キーが変わった後でも、既存の Ref<Author> は行 ID 経由で同じ論理エンティティを解決できます。

join 形式実行の形
join<&RefField>(right)左 table scan と右 table の row-id lookup。
join<&LeftField, &RightPk>(right)左 table scan と右 table の primary-key lookup。
join<&LeftField, &RightField>(right)左 table scan と、matching field を探すための右 table scan。

結合ヘルパーは Native の結合エンジン計画を作りません。テーブル走査と検索の上にあるビュー層のヘルパーです。

Ref<T> の遅延解決は通常の C++ 動作です。post.author->name == "Alice" のような条件式は別テーブルへ境界をまたぐため、ローカルの生の行フィールド読み込みとして表現できません。

Clang プラグインのバイトコード書き換え経路では、Ref<T> をまたぐ PackedTable::query(lambda) は所有する HostCallBool 記述子として出力されます。通常の遅延解決は保たれますが、その条件式は生の行を直接評価できません。

BytecodeQueryView::where(lambda) では、Ref<T> をまたぐ条件は復号後フィルターとして残ります。where 記述子はバイトコードとして合成できる必要があるためです。明示的な join(...).where([](const Left&, const Right&) { ... }) は既存の二つのエンティティを受け取る条件式の意味を保ち、ローカル行バイトコードには書き換えられません。