はじめに
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.
