ドキュメントナビゲーションを開く
R2D1 / ドキュメント

はじめに

R2D1 の設定は2つのバックエンド選択から始まり、共通 API の流れへ続きます。完全なドキュメントの保存先とインデックスフィールドの保存先を選び、その2つの store でコレクション facade を設定します。

1. ドキュメントストアを選ぶ

DocumentStore は完全なシリアライズ済みドキュメント、IDによる読み取り、論理的な存在の権威ソースです。次から1つ選びます。

  • R2 DocumentStore は AWS SDK の非同期クライアント経由で Cloudflare R2 にドキュメントを保存します。
  • Filesystem DocumentStore はアトミックな置換を使って正規のローカルドキュメントを保存します。

2. インデックスストアを選ぶ

IndexStore はドキュメント ID と明示的なインデックスフィールドからなる再構築可能な projection です。次から1つ選びます。

  • D1 IndexStore は Java の非同期 HttpClient 経由で Cloudflare D1 REST API を使います。
  • JDBC IndexStore はアプリケーション所有の DataSource 経由で H2、HSQLDB、ローカル SQLite をサポートします。

この選択で R2 + D1、R2 + JDBC、Filesystem + D1、Filesystem + JDBC の4つを組み合わせられますが、コレクション API が4つに分かれるわけではありません。

3. 必要なモジュールを追加する

コア artifact は共通 API と Cloudflare R2/D1 アダプターを提供します。

dependencies {
    implementation("dev.nexcraft:r2d1:<version>")
}

アプリケーションで使う統合モジュールだけを追加します。

dependencies {
    implementation("dev.nexcraft:r2d1-filesystem:<version>")
    implementation("dev.nexcraft:r2d1-jdbc:<version>")
    implementation("dev.nexcraft:r2d1-micronaut:<version>")
}

JDBC ドライバーはアプリケーションが提供し、r2d1-jdbc には含まれません。モジュール一覧と所有権のルールは Configuration.

4. ドキュメントを定義する

空でない String member の1つに @Id を付けて ID とし、インデックスに保存するフィールドに @Index を付けます。フィルタとソートには明示的にインデックスされたフィールドだけを使えます。

import dev.nexcraft.r2d1.annotation.Document;
import dev.nexcraft.r2d1.annotation.Id;
import dev.nexcraft.r2d1.annotation.Index;

@Document("users")
public record User(
    @Id String id,
    @Index String country,
    @Index long score,
    String name) {}

R2D1 は JSON library を選びません。domain object を StoredDocument にシリアライズし、コレクションが結果を hydrate するときに戻せる DocumentCodec を提供します。

5. 共通コレクション API を設定する

次の例では2つのストレージ役割が見えるよう、アダプターの生成をアプリケーションに任せています。documentStore、indexStore、documentCodec はアプリケーション所有の値です。

import dev.nexcraft.r2d1.DocumentCodec;
import dev.nexcraft.r2d1.PersistenceCollectionFactory;
import dev.nexcraft.r2d1.R2D1;
import dev.nexcraft.r2d1.R2D1Collection;
import dev.nexcraft.r2d1.spi.DocumentStore;
import dev.nexcraft.r2d1.spi.IndexStore;

DocumentStore documentStore = applicationDocumentStore;
IndexStore indexStore = applicationIndexStore;
DocumentCodec documentCodec = applicationDocumentCodec;

R2D1 database =
    R2D1.builder()
        .collectionFactory(
            new PersistenceCollectionFactory(
                documentStore, indexStore, documentCodec, indexStore::initialize))
        .build();

R2D1Collection<User> users = database.collection(User.class);

コレクションを開くと metadata を初期化または検証してからコレクションを返します。factory は store、codec、呼び出し側が所有する実行リソースを取得しません。

6. put、get、delete

User user = new User("user-123", "NZ", 42, "Ada");
users.put(user);

Optional<User> stored = users.get("user-123");

users.delete("user-123");
Optional<User> absent = users.get("user-123");

put は @Id の値でドキュメントを作成または置換します。get は権威ドキュメントストレージを読み、存在しない ID の削除は何もしません。

7. インデックスクエリを実行する

import dev.nexcraft.r2d1.Page;
import dev.nexcraft.r2d1.SortDirection;

Page<User> first =
    users.query()
        .where("country")
        .eq("NZ")
        .sortBy("score", SortDirection.DESC)
        .limit(20)
        .fetch();

if (first.nextCursor() != null) {
  Page<User> second =
      users.query()
          .where("country")
          .eq("NZ")
          .sortBy("score", SortDirection.DESC)
          .limit(20)
          .after(first.nextCursor())
          .fetch();
}

cursor は opaque な値です。次のページを要求するときも同じ filter と sort を維持します。比較演算子と順序付けのルールは Queryingを、2つの store を本番で使う前の復旧の意味は Consistency and Recovery.