Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

スキーマとフィールド

Schema はドキュメントの構造を定義します。どのフィールドが存在し、各フィールドがどのようにインデクシングされるかを指定します。Schema は Engine にとって唯一の情報源です。

CLI で使用される TOML ファイル形式については、スキーマフォーマットリファレンスを参照してください。

Schema

Schema は名前付きフィールドのコレクションです。各フィールドはLexical フィールド(キーワード検索用)または Vector フィールド(類似度検索用)のいずれかです。

#![allow(unused)]
fn main() {
use laurus::Schema;
use laurus::lexical::TextOption;
use laurus::lexical::core::field::IntegerOption;
use laurus::vector::HnswOption;

let schema = Schema::builder()
    .add_text_field("title", TextOption::default())
    .add_text_field("body", TextOption::default())
    .add_integer_field("year", IntegerOption::default())
    .add_hnsw_field("embedding", HnswOption::default())
    .add_default_field("body")
    .build();
}

デフォルトフィールド

add_default_field() は、クエリがフィールド名を明示的に指定しない場合に検索対象となるフィールドを指定します。これは Query DSL パーサーで使用されます。

フィールドタイプ

graph TB
    FO["FieldOption"]

    FO --> T["Text"]
    FO --> I["Integer"]
    FO --> FL["Float"]
    FO --> B["Boolean"]
    FO --> DT["DateTime"]
    FO --> G["Geo"]
    FO --> BY["Bytes"]

    FO --> FLAT["Flat"]
    FO --> HNSW["HNSW"]
    FO --> IVF["IVF"]

Lexical フィールド

Lexical フィールドは転置インデックス(Inverted Index)を使用してインデクシングされ、キーワードベースのクエリをサポートします。

タイプRust 型SchemaBuilder メソッド説明
TextTextOptionadd_text_field()全文検索可能。Analyzer によりトークン化される
IntegerIntegerOptionadd_integer_field()64 ビット符号付き整数。範囲クエリをサポート
FloatFloatOptionadd_float_field()64 ビット浮動小数点数。範囲クエリをサポート
BooleanBooleanOptionadd_boolean_field()true / false
DateTimeDateTimeOptionadd_datetime_field()UTC タイムスタンプ。範囲クエリをサポート
GeoGeoOptionadd_geo_field()緯度/経度のペア。半径検索とバウンディングボックスクエリをサポート
BytesBytesOptionadd_bytes_field()バイナリデータ

Text フィールドオプション

TextOption はテキストのインデクシング方法を制御します。

#![allow(unused)]
fn main() {
use laurus::lexical::TextOption;

// Default: indexed + stored + term vectors (all true)
let opt = TextOption::default();

// Customize
let opt = TextOption::default()
    .indexed(true)
    .stored(true)
    .term_vectors(true);
}
オプションデフォルト説明
indexedtrueフィールドが検索可能かどうか
storedtrue元の値が取得用に保存されるかどうか
term_vectorstrueターム位置が保存されるかどうか(フレーズクエリやハイライトに必要)

Vector フィールド

Vector フィールドは近似最近傍(ANN: Approximate Nearest Neighbor)検索のためのベクトルインデックスを使用してインデクシングされます。

タイプRust 型SchemaBuilder メソッド説明
FlatFlatOptionadd_flat_field()ブルートフォース線形スキャン。正確な結果
HNSWHnswOptionadd_hnsw_field()Hierarchical Navigable Small World グラフ。高速な近似検索
IVFIvfOptionadd_ivf_field()Inverted File Index。クラスタベースの近似検索

HNSW フィールドオプション(最も一般的)

#![allow(unused)]
fn main() {
use laurus::vector::HnswOption;
use laurus::vector::core::distance::DistanceMetric;

let opt = HnswOption {
    dimension: 384,                          // vector dimensions
    distance: DistanceMetric::Cosine,        // distance metric
    m: 16,                                   // max connections per layer
    ef_construction: 200,                    // construction search width
    base_weight: 1.0,                        // default scoring weight
    quantizer: None,                         // optional quantization
};
}

パラメータの詳細なガイダンスについては、Vector インデクシングを参照してください。

Document

Document は名前付きフィールド値のコレクションです。DocumentBuilder を使用してドキュメントを構築します。

#![allow(unused)]
fn main() {
use laurus::Document;

let doc = Document::builder()
    .add_text("title", "Introduction to Rust")
    .add_text("body", "Rust is a systems programming language.")
    .add_integer("year", 2024)
    .add_float("rating", 4.8)
    .add_boolean("published", true)
    .build();
}

ドキュメントのインデクシング

Engine はドキュメントを追加するための 2 つのメソッドを提供しており、それぞれ異なるセマンティクスを持ちます。

メソッド動作ユースケース
put_document(id, doc)Upsert — 同じ ID のドキュメントが存在する場合、置き換えられる標準的なドキュメントインデクシング
add_document(id, doc)Append — 新しいチャンクとしてドキュメントを追加。同じ ID で複数のチャンクを持てるチャンク分割されたドキュメント(例: 段落に分割された長い記事)
#![allow(unused)]
fn main() {
// Upsert: replaces any existing document with id "doc1"
engine.put_document("doc1", doc).await?;

// Append: adds another chunk under the same id "doc1"
engine.add_document("doc1", chunk2).await?;

// Always commit after indexing
engine.commit().await?;
}

ドキュメントの取得

get_documents を使用して、外部 ID でドキュメント(チャンクを含む)を取得します。

#![allow(unused)]
fn main() {
let docs = engine.get_documents("doc1").await?;
for doc in &docs {
    if let Some(title) = doc.get("title") {
        println!("Title: {:?}", title);
    }
}
}

ドキュメントの削除

外部 ID を共有するすべてのドキュメントとチャンクを削除します。

#![allow(unused)]
fn main() {
engine.delete_documents("doc1").await?;
engine.commit().await?;
}

ドキュメントのライフサイクル

graph LR
    A["Build Document"] --> B["put/add_document()"]
    B --> C["WAL"]
    C --> D["commit()"]
    D --> E["Searchable"]
    E --> F["get_documents()"]
    E --> G["delete_documents()"]

重要: ドキュメントは commit() が呼び出されるまで検索可能になりません。

DocumentBuilder メソッド

メソッド値の型説明
add_text(name, value)Stringテキストフィールドを追加
add_integer(name, value)i64整数フィールドを追加
add_float(name, value)f64浮動小数点数フィールドを追加
add_boolean(name, value)boolブールフィールドを追加
add_datetime(name, value)DateTime<Utc>日時フィールドを追加
add_vector(name, value)Vec<f32>事前計算済みベクトルフィールドを追加
add_geo(name, lat, lon)(f64, f64)地理座標フィールドを追加
add_bytes(name, data)Vec<u8>バイナリデータを追加
add_field(name, value)DataValue任意の値型を追加

DataValue

DataValue は Laurus におけるフィールド値を表す統合列挙型です。

#![allow(unused)]
fn main() {
pub enum DataValue {
    Null,
    Bool(bool),
    Int64(i64),
    Float64(f64),
    Text(String),
    Bytes(Vec<u8>, Option<String>),  // (data, optional MIME type)
    Vector(Vec<f32>),
    DateTime(DateTime<Utc>),
    Geo(f64, f64),          // (latitude, longitude)
}
}

DataValue は一般的な型に対して From<T> を実装しているため、.into() 変換が使用できます。

#![allow(unused)]
fn main() {
use laurus::DataValue;

let v: DataValue = "hello".into();       // Text
let v: DataValue = 42i64.into();         // Int64
let v: DataValue = 3.14f64.into();       // Float64
let v: DataValue = true.into();          // Bool
let v: DataValue = vec![0.1f32, 0.2].into(); // Vector
}

予約フィールド

_id フィールドは Laurus の内部使用のために予約されています。外部ドキュメント ID を格納し、常に KeywordAnalyzer(完全一致)でインデクシングされます。スキーマに追加する必要はありません。自動的に管理されます。

動的フィールド管理

稼働中のエンジンに対して、フィールドの追加および削除を動的に行えます。 フィールドの型変更はサポートされていません。

フィールドの追加

Engine::add_field() を使用すると、稼働中のエンジンにフィールドを動的に追加できます。

Lexical フィールドの追加

let updated_schema = engine.add_field(
    "category",
    FieldOption::Text(TextOption::default()),
).await?;

Vector フィールドの追加

let updated_schema = engine.add_field(
    "embedding",
    FieldOption::Flat(FlatOption::default().dimension(384)),
).await?;

既存のドキュメントには影響がありません(新しいフィールドの値が存在しないだけです)。

フィールドの削除

Engine::delete_field() を使用すると、稼働中のエンジンからフィールドを動的に削除できます。

let updated_schema = engine.delete_field("category").await?;

フィールド削除時の動作は以下の通りです。

  • スキーマからフィールド定義が削除されます。
  • default_fields に含まれている場合、そこからも削除されます。
  • フィールドに紐づくアナライザーおよびエンベッダーの登録が解除されます。
  • 既にインデックスされたデータは物理的に残りますが、スキーマから削除されたフィールドにはアクセスできなくなります。

共通の注意事項

返却された Schema は呼び出し側で永続化する必要があります(例: schema.toml への書き出し)。

スキーマ設計のヒント

  1. Lexical フィールドと Vector フィールドを分離する — フィールドは Lexical か Vector のいずれかであり、両方にはなりません。ハイブリッド検索には、別々のフィールドを作成してください(例: テキスト用に body、ベクトル用に body_vec)。

  2. 完全一致フィールドには KeywordAnalyzer を使用する — カテゴリ、ステータス、タグフィールドは PerFieldAnalyzer 経由で KeywordAnalyzer を使用し、トークン化を避けてください。

  3. 適切なベクトルインデックスを選択する — ほとんどの場合は HNSW、小規模データセットには Flat、非常に大規模なデータセットには IVF を使用してください。詳細は Vector インデクシングを参照。

  4. デフォルトフィールドを設定する — Query DSL を使用する場合、デフォルトフィールドを設定することで、ユーザーは body:hello の代わりに hello と記述できます。

  5. スキーマジェネレータを使用するlaurus create schema を実行して、手書きの代わりにインタラクティブにスキーマ TOML ファイルを構築できます。詳細は CLI コマンドを参照。