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

API リファレンス

Index

Laurus 検索エンジンをラップするメインクラスです。

Laurus::Index.new(path: nil, schema: nil, wal_sync_policy: nil, commit_policy: nil)

コンストラクタ

パラメータ型デフォルト説明
path:String | nilnil永続ストレージのディレクトリパス。nil の場合はインメモリインデックスを作成します。指定した場合、そのディレクトリは laurus-cli create index/--index-dir と同じ <path>/schema.toml + <path>/store/ というレイアウトに従うため、ここで作成したインデックスは CLI からも開けます(逆も同様)。詳細は下記を参照。
schema:Schema | nilnilスキーマ定義。新規にファイルベース(またはインメモリ)インデックスを作成する場合のみ意味を持ちます。既存のファイルベースインデックスを再オープンする場合は省略する必要があり、永続化済みのスキーマが代わりに読み込まれます。新規インデックスで省略した場合は空のスキーマが使用されます。
wal_sync_policy:WalSyncPolicy | nilnil先行書き込みログ(WAL)の耐久性ポリシー。nil の場合はデフォルトのレコードごと fsync を維持します。WAL 同期ポリシーと耐久性 を参照。
commit_policy:CommitPolicy | nilnil自動コミットポリシー。nil の場合はデフォルトの manual モード(呼び出し側がすべての commit を駆動)を維持します。コミットポリシーと自動コミット を参照。

ファイルベースインデックスの作成 vs 再オープン(path: を指定した場合): <path>/schema.toml がまだ存在しない場合、この呼び出しは新規インデックスを作成し、schema:(省略時は空のスキーマ)をそこに永続化します。<path>/schema.toml が既に存在する場合、この呼び出しは既存インデックスを再オープンします – schema: は省略しなければならず、指定すると ArgumentError が発生します(どちらのスキーマを優先すべきか曖昧になるため)。path: がこの規約導入以前のレイアウト(schema.toml が無く、セグメントファイルが path: 直下にある)のインデックスを含んでいる場合も ArgumentError になります。

メソッド

メソッド説明
put_document(id, doc)ドキュメントをアップサート(upsert)します。同じ ID の既存バージョンをすべて置換します。
add_document(id, doc)既存バージョンを削除せずにドキュメントチャンクを追記します。
put_documents(docs)バッチ upsert。docs は [id, hash] ペアの Array で、バッチごとに WAL fsync 1 回で順に適用します(重複 ID はデデュープ、最後が勝ち)。最初の不正エントリで fail-fast し、適用済みの prefix はロールバックされません。
add_documents(docs)バッチチャンク追記。put_documents と同様ですが、繰り返した ID は別バージョンとして蓄積されます。
get_documents(id) -> Array<Hash>指定 ID の全保存バージョンを返します。
delete_documents(id)指定 ID の全バージョンを削除します。
commitバッファリングされた書き込みをフラッシュし、すべての保留中の変更を検索可能にします。
flush_walWAL の耐久バリアをオンデマンドで強制します。未同期の WAL レコードを同期的に fsync し、nil を返します。group-commit ポリシー下で実行する場合に有用です(下記参照)。
search(query, limit: 10, offset: 0, highlight: nil, rescore: nil) -> Array<SearchResult>検索クエリを実行します。rescore: には上位の結果を並べ替える LateInteractionRescore を渡します(Issue #1351)。それ以外の値は TypeError になります。LateInteractionRescore を参照。
search_batch(queries, limit: 10, offset: 0, highlight: nil) -> Array<Array<SearchResult>>独立した複数の検索を 1 回の呼び出しで実行します。各クエリは内部の tokio ランタイム上で並列に dispatch されます。results[i] は queries[i] に対応し、入力が空の配列の場合は [] を返します。highlight: はバッチ内のすべてのクエリに同一に適用されます。rescore: キーワードはありません。要素の SearchRequest は、その rescore: で再採点されます。
stats -> Hashインデックス統計("document_count"、"vector_fields")を返します。

search の query 引数

query パラメータは以下のいずれかを受け付けます:

  • DSL 文字列(例: "title:hello"、"content:\"memory safety\"")
  • Lexical クエリオブジェクト(TermQuery、PhraseQuery、BooleanQuery など)
  • Vector クエリオブジェクト(VectorQuery、VectorTextQuery)
  • SearchRequest(完全な制御が必要な場合)

query が SearchRequest の場合は、その limit:・offset:・highlight:・rescore: が使われ、search のキーワード引数は無視されます。

search_batch の queries 配列の各要素も同じ種類の値を受け付けます。DSL 文字列・クエリオブジェクト・SearchRequest を 1 つのバッチ内で混在させることもできます。

ハイライト

search/search_batch の highlight: キーワード(Issue #1134)は、各ヒットの SearchResult#highlights にフィールドごとのハイライト済みフラグメントを要求します。以下のいずれかを受け付けます。

  • フィールド名の配列: highlight: ["body"]
  • Hash: 必須の fields キー(String または Symbol)に加えて、HighlightConfig の任意の設定(max_fragments、fragment_size、tag、css_class、require_field_match、max_analyzed_chars、return_entire_field_if_no_highlight。キーは String・Symbol どちらも可)を指定 — 例: highlight: {fields: ["body"], tag: "em", max_fragments: 2}
results = index.search("body:rust", highlight: ["body"])
results[0].highlights # => {"body" => ["<mark>Rust</mark> is a systems programming language."]}

ハイライトは search/search_batch に渡したクエリ(または SearchRequest の query/lexical_query、下記参照)に従い、stored: true のテキストフィールドのみハイライト可能です。存在しない、保存されていない、テキスト型でないフィールドは黙ってスキップされます。highlight: を省略すると、すべての結果の highlights は空のままになります。同じ highlight: キーワードは SearchRequest.new でも使用できます。

WAL 同期ポリシーと耐久性

先行書き込みログ(WAL: Write-Ahead Log)は、コミット済みデータをクラッシュ から保護します。デフォルトでは WAL は完全に耐久的で、すべてのレコードは 書き込みが返る前に fsync されます。group commit(グループコミット) を有効にすると、fsync 呼び出しをまとめることで、耐久性をいくらか引き換えに 書き込みスループットを向上させられます。

WalSyncPolicy

Laurus::WalSyncPolicy は WAL のフラッシュ方法を記述するイミュータブルな 値オブジェクトです。Index.new(wal_sync_policy:) に渡します。

# デフォルト: 書き込みごとに耐久(各レコードを個別に fsync)。
Laurus::WalSyncPolicy.per_record

# Group commit: fsync をまとめてコストを償却。
Laurus::WalSyncPolicy.group(
  max_records: nil,      # このレコード数でフラッシュ(デフォルト 1024)
  max_bytes: nil,        # このバイト数でフラッシュ(デフォルト 1 MiB)
  max_interval_ms: nil,  # このミリ秒ごとに定期的にもフラッシュ
)
コンストラクタ説明
WalSyncPolicy.per_recordデフォルト。すべてのレコードは書き込みが返る前に fsync されます。書き込みごとに完全に耐久的です。
WalSyncPolicy.group(max_records:, max_bytes:, max_interval_ms:)fsync をまとめます。引数なしの場合はデフォルト(max_records: 1024、max_bytes: 1 MiB、タイマーなし)を使用します。WAL は max_records または max_bytes のいずれかが蓄積したとき、および毎回の commit 時にフラッシュされます。max_interval_ms: を指定すると、定期タイマーでもフラッシュします。

Group commit は SQLite の synchronous = NORMAL に相当します。クラッシュ時に 失われるのは最後の未同期バッチのレコードまでで、インデックスが破損する ことはありません。レコードは常に commit 時に耐久化されるため、成功した commit はポリシーに関わらず耐久バリアとなります。

フラッシュの強制

コミットの合間に耐久バリアを強制するには flush_wal を呼び出します。 例えば、バッチが安全に永続化されたことを通知する前などです。未同期の レコードを同期的に fsync し、nil を返します。デフォルトのレコードごと ポリシーでは実質的に no-op です。

# group commit を有効にし、必要に応じて耐久性を強制する。
policy = Laurus::WalSyncPolicy.group(max_records: 4096, max_bytes: 4 * 1024 * 1024)
index = Laurus::Index.new(path: "./myindex", wal_sync_policy: policy)

index.put_document("doc1", { "title" => "Hello" })
index.flush_wal  # group バッチが満杯でなくてもレコードが永続化される

コミットポリシーと自動コミット

デフォルトでは、すべてのコミットは呼び出し側が駆動します。バッファリング された書き込みは、明示的に commit を呼び出したときにのみ検索可能になり ます。自動コミットポリシー(auto-commit policy) を使うと、その責務を エンジンに委ねられ、一定数のドキュメントを適用するたび、または定期タイマー で自動的にコミットされます。

CommitPolicy

Laurus::CommitPolicy は、エンジンがバッファリングされた書き込みをいつ ストアへ実体化するかを記述するイミュータブルな値オブジェクトです。 Index.new(commit_policy:) に渡します。

# デフォルト: manual — すべてのコミットは呼び出し側が駆動。
Laurus::CommitPolicy.manual

# 自動コミット: N ドキュメント適用ごとにコミット。
Laurus::CommitPolicy.every_docs(1000)

# 自動コミット: 少なくとも N ミリ秒ごとにコミット(ネイティブのみ)。
Laurus::CommitPolicy.interval_ms(5000)
コンストラクタ説明
CommitPolicy.manualデフォルト。自動コミットなし。すべての commit は呼び出し側が駆動します。
CommitPolicy.every_docs(n)n ドキュメント適用ごとに自動コミットします。単発・バッチ両方の取り込みを通してカウントされ、単一バッチ 内 でも n ドキュメントごとにコミットされます。
CommitPolicy.interval_ms(ms)バックグラウンドタイマーで少なくとも ms ミリ秒ごとに自動コミットします。取り込みがアイドル状態でも、末尾の部分バッチがコミットされます。every_docs の時間ベース版です。デフォルト: なし。ネイティブのみ — WebAssembly(wasm32)ではバックグラウンドスレッドが存在しないため、エンジンはこれを no-op として扱います(値は構築されますが、タイマーによるコミットは発生しません)。

every_docs(0) は有効で、自動コミットを無効化します(manual と等価)。

コミットポリシーは WalSyncPolicy と直交して います。WalSyncPolicy が WAL の fsync 耐久性を制御するのに対し、 CommitPolicy はストアがバッファリングされた書き込みをいつ実体化するかを 制御します。両者は独立して設定します。

# 1000 ドキュメント適用ごとに自動コミット。
policy = Laurus::CommitPolicy.every_docs(1000)
index = Laurus::Index.new(path: "./myindex", commit_policy: policy)

Schema

Index のフィールドとインデックスタイプを定義します。

Laurus::Schema.new

フィールドメソッド

メソッド説明
add_text_field(name, stored: true, indexed: true, term_vectors: true, doc_values: true, analyzer: nil, multi_valued: false, position_increment_gap: 100)全文フィールド(転置インデックス、BM25)。term_vectors: はタームの位置を保存するかどうかを制御し、フレーズクエリ・スパンクエリが読み取ります。doc_values: は値を DocValues(ソート・ファセット・集計が読み取る列指向ストア)にもコピーするかどうかを制御します(Issue #1047)。stored: true の場合のみ有効です。multi_valued: true で String の Array を受け付けます(Issue #1175): term クエリはいずれかの要素がタームを含めばマッチし、フレーズクエリは slop が position_increment_gap:(デフォルト 100。0 にすると要素を連結したものとして付番)に達しない限り 2 つの要素をまたぎません。値は String の Array として読み戻されます。analyzer: にはパラメータ不要の組込名("standard" / "english" / "keyword" / "simple" / "noop"、または add_analyzer で登録したカスタム名)を指定します。Lindera 辞書パスが必要な Japanese プリセットは、lindera tokenizer を含むカスタム analyzer として登録し、名前で参照してください。
add_integer_field(name, stored: true, indexed: true, multi_valued: false, doc_values: true)64 ビット整数フィールド。multi_valued: true で整数配列を受け付け(範囲クエリは “any match”)。doc_values: は上記を参照。
add_float_field(name, stored: true, indexed: true, multi_valued: false, doc_values: true)64 ビット浮動小数点フィールド。multi_valued: true で浮動小数点配列を受け付け(範囲クエリは “any match”)。doc_values: は上記を参照。
add_boolean_field(name, stored: true, indexed: true, multi_valued: false, doc_values: true)ブールフィールド。multi_valued: true で true / false の Array を受け付け(flags:true のような term クエリはいずれかの要素が値と等しければマッチ。値は true / false の Array として読み戻されます)。doc_values: は上記を参照。
add_bytes_field(name, stored: true, multi_valued: false)生バイトフィールド。doc_values: オプションはありません —— Bytes の値は設定にかかわらず DocValues に一切書き込まれないためです。multi_valued: true を渡すと base64 String の Array を受け付け(Issue #1176)、単一の base64 String と同じ方法で要素ごとにデコードされます。Bytes はそもそもインデックスされないため、他の multi_valued: オプションと異なりクエリ一致の意味論はなく、保存時の形と取り込み時の許容個数を変えるだけです。値はスカラーフィールドと同様、(バイナリ)String の Array として読み戻されます。
add_geo_field(name, stored: true, indexed: true, multi_valued: false, doc_values: true)地理座標フィールド(緯度/経度)。multi_valued: true で { "lat" => .., "lon" => .. } Hash の Array を受け付け(距離 / バウンディングボックスクエリはいずれかのポイントが条件を満たせばマッチ)。doc_values: は上記を参照。
add_geo3d_field(name, stored: true, indexed: true, multi_valued: false, doc_values: true)3D ECEF カルテシアン座標フィールド(x, y, z はメートル)。multi_valued: true で { "x" => .., "y" => .., "z" => .. } Hash の Array を受け付け(距離 / バウンディングボックス / nearest クエリはいずれかのポイントが条件を満たせばマッチ)。詳細は Geo3d の概念。doc_values: は上記を参照。
add_datetime_field(name, stored: true, indexed: true, multi_valued: false, doc_values: true)UTC 日時フィールド。multi_valued: true で Time / DateTime / RFC 3339 String の Array を受け付け(範囲クエリはいずれかの時刻が条件を満たせばマッチ。値は UTC の RFC 3339 String の Array として読み戻されます)。doc_values: は上記を参照。
add_hnsw_field(name, dimension, distance: "cosine", m: 16, ef_construction: 200, quantizer: nil, subvector_count: nil, rerank_storage: nil, embedder: nil, pq_codebook_path: nil, base_weight: 1.0)HNSW 近似最近傍ベクトルフィールド。base_weight は他の vector フィールドと同時に検索されたときの相対的なスコアリング優先度(Issue #1084)。ウェイトを参照。
add_flat_field(name, dimension, distance: "cosine", embedder: nil, base_weight: 1.0)Flat(総当たり)ベクトルフィールド。
add_ivf_field(name, dimension, distance: "cosine", n_clusters: 100, n_probe: 1, embedder: nil, base_weight: 1.0)IVF 近似最近傍ベクトルフィールド。
add_multi_vector_field(name, dimension, distance: "cosine", storage: "f32", embedder: nil)文書ごとに可変個のトークンベクトル(ColBERT のトークンごとの埋め込みなど)を保持する MultiVector フィールド(Issue #1351)。値は数値の Array の Array で、トークンごとに要素数 dimension の Array を 1 つ持ちます。ANN 索引は持たず、late interaction による再採点だけが読み取ります(LateInteractionRescore を参照)。トークンベクトルは保存されないため、get_documents や検索結果にこのフィールドは含まれません。distance: は "cosine" か "dot_product"、dimension は 0 より大きい値でなければならず、どちらもフィールド追加時に検査されます(ArgumentError)。storage: は各トークンベクトルのディスク上の要素種別を指定します — "f32"(デフォルト、正確)、"f16"(2倍小さい)、"int8"(約4倍小さいが非可逆圧縮)のいずれかで、こちらもフィールド追加時に検査されます(ArgumentError)。embedder: にはトークン単位のエンベダー("candle_colbert" のもの。エンベダータイプを参照)の名前を指定し、テキストの値と再採点のクエリテキストを埋め込みます。MultiVector フィールドを参照。

ベクトル量子化とリランクストレージ(HNSW フィールド):

  • quantizer — "scalar_8bit"(デフォルト、4 倍圧縮)または高圧縮率の "product_quantization"。Product quantization では subvector_count(dimension を割り切れる値)が必須です。
  • rerank_storage — "f32" を指定すると完全精度の *.hnsw.f32 サイドカーを書き出し、厳密な Stage-2 リランクを有効化します。省略すると int8 のみのセグメントを維持します。
  • pq_codebook_path — 共有 PQ codebook のストレージ相対ファイル名(Issue #631)。laurus train pq-codebook CLI コマンドで一度だけ学習します。quantizer: "product_quantization" との組み合わせでのみ意味を持ち、以後の commit は segment ごとの k-means 再学習の代わりに学習済み codebook で encode します。省略すると segment ごとの学習を維持します。

上記のどの add_*_field メソッドも、name が _(_id を除く)で始まる場合は ArgumentError を発生させ、何も追加しない。from_toml / from_toml_file で読み込むスキーマはそのようなフィールドを引き続き受け付けるため、永続化済みのスキーマはそのまま読み込める。ただし、そのスキーマから新しい Index を作成すると ArgumentError になる。詳細はフィールド命名規則を参照。

その他のメソッド

メソッド説明
add_embedder(name, config)名前付きエンベダー定義を登録します。config は "type" キーを持つ Hash で、キーは String / Symbol どちらでも構いません(下記参照)。type が無い・未知である、または必須キーが無い場合は ArgumentError(invalid embedder config: ...)になります。
add_analyzer(name, tokenizer, char_filters: nil, token_filters: nil)カスタムアナライザ定義を登録します。tokenizer は必須、char_filters:/token_filters: は省略可能な Hash の配列です。各 Hash はスキーマ TOML/JSON 形式と同じ {type: "..."} 形式で、キーは String / Symbol どちらでも構いません(下記参照)。組み込みアナライザ用に予約された名前(standard、keyword、english、simple、noop)は ArgumentError になり、その名前を定義したスキーマ(from_toml で読み込んだものなど)から新しい Index を作る場合も同じです。正規表現の妥当性など意味的な検証は、このメソッド呼び出し時点ではなく Index 構築時に行われます。
analyzer_names -> Array<String>add_analyzer で登録された、または TOML から読み込まれたカスタムアナライザの名前一覧を返します。
Laurus::Schema.from_toml(toml_str) -> Schema (クラスメソッド)laurus-cli create index --schema と同じ形式の TOML 文字列からスキーマを読み込みます。
Laurus::Schema.from_toml_file(path) -> Schema (クラスメソッド)TOML ファイルからスキーマを読み込みます。
to_toml -> Stringこのスキーマを laurus-cli と同じ形式の TOML 文字列にシリアライズします。
to_toml_file(path)このスキーマを TOML ファイルに書き込みます。
set_default_fields(fields)クエリでフィールドが指定されていない場合に使用するデフォルトフィールドを設定します。fields は文字列の配列です。
set_dynamic_field_policy(policy)未宣言フィールドの扱いを設定します。policy は "strict" / "dynamic"(デフォルト)/ "ignore"。詳細は下記を参照。
dynamic_field_policy -> String現在のポリシーを小文字の文字列で返します。
field_names -> Array<String>このスキーマに定義されたフィールド名のリストを返します。

Dynamic field policy(動的フィールドポリシー)

ドキュメントに含まれるがスキーマに宣言されていないフィールドの扱いを制御します:

  • "strict" — ドキュメントを拒否
  • "dynamic"(デフォルト)— 各未宣言フィールドの型を推論してスキーマに追加。警告: integer フィールドに入ってきた float 値は静かに切り捨てられます(3.14 → 3)。厳密さが必要なら "strict" を使用してください
  • "ignore" — 未宣言フィールドを静かに破棄

詳細な挙動マトリクスは スキーマとフィールド を参照してください。

エンベダータイプ

各型の説明を含む正規のリファレンスは スキーマフォーマットリファレンス → エンベダー を参照してください。

"type"必須キーFeature Flag
"precomputed"–(常に利用可能)
"candle_bert""model"embeddings-candle
"candle_clip""model"embeddings-multimodal
"openai""model"embeddings-openai
"candle_colbert""model"embeddings-candle

"candle_colbert" は、省略可能なキー "revision"、"query_maxlen"、"doc_maxlen" も受け付けます。トークンベクトルを生成するため、使えるのは MultiVector フィールド(add_multi_vector_field)だけです。キーは String / Symbol どちらでも構いません。

schema = Laurus::Schema.new
schema.add_embedder(
  "colbert",
  { type: "candle_colbert", model: "colbert-ir/colbertv2.0", query_maxlen: 32 },
)
schema.add_multi_vector_field("body_colbert", 128, embedder: "colbert")

アナライザコンポーネント

add_analyzer(name, tokenizer, char_filters: nil, token_filters: nil) と [analyzers.<name>] TOML セクションで使用します。tokenizer は単一の Hash、 char_filters:/token_filters: は Hash の配列で、配列の順序どおりに適用されます。

各コンポーネントの説明を含む正規のリファレンスは スキーマフォーマットリファレンス → アナライザ を参照してください。

トークナイザ(tokenizer、必ず1つ):

type必須キー省略可能キー
"whitespace"––
"unicode_word"––
"regex"–pattern(デフォルト \w+)、gaps(デフォルト false)
"ngram"min_gram、max_gram–
"lindera"mode、dictuser_dict
"whole"––

文字フィルタ(char_filters、トークン化前の生テキストに適用):

type必須キー省略可能キー
"unicode_normalization"form("nfc"/"nfd"/"nfkc"/"nfkd")–
"pattern_replace"pattern、replacement–
"mapping"mapping(置換用の Hash)–
"japanese_iteration_mark"–kanji(デフォルト true)、kana(デフォルト true)

トークンフィルタ(token_filters、トークン化後のトークン列に適用):

type必須キー省略可能キー
"lowercase"––
"stop"–words(デフォルト: 英語のストップワード)
"stem"–stem_type("porter"/"simple"/"identity")
"boost"boost–
"limit"limit–
"strip"––
"remove_empty"––
"flatten_graph"––
schema = Laurus::Schema.new
schema.add_analyzer(
  "ja_ipadic",
  { type: "lindera", mode: "normal", dict: "/var/lib/lindera/ipadic" },
  char_filters: [
    { type: "unicode_normalization", form: "nfkc" },
    { type: "japanese_iteration_mark" },
  ],
  token_filters: [{ type: "lowercase" }],
)
schema.add_text_field("title", analyzer: "ja_ipadic")

距離メトリクス

値説明
"cosine"コサイン類似度(デフォルト)
"euclidean"ユークリッド距離
"dot_product"内積
"manhattan"マンハッタン距離
"angular"角度距離

クエリクラス

TermQuery

Laurus::TermQuery.new(field, term)

指定フィールドに完全一致する語句を含むドキュメントを検索します。

PhraseQuery

Laurus::PhraseQuery.new(field, terms)

指定した語句が順序どおりに含まれるドキュメントを検索します。terms は文字列の配列です。

FuzzyQuery

Laurus::FuzzyQuery.new(field, term, max_edits: 2)

編集距離が max_edits 以内の近似一致を検索します。

WildcardQuery

Laurus::WildcardQuery.new(field, pattern)

ワイルドカードパターン検索。* は任意の文字列、? は任意の1文字に一致します。

NumericRangeQuery

Laurus::NumericRangeQuery.new(field, min: nil, max: nil)

[min, max] の範囲内の数値を検索します。開いた境界には nil を指定します。型(整数または浮動小数点)は min/max の Ruby 型から推論されます。

DateTimeRangeQuery

Laurus::DateTimeRangeQuery.new(field, min: nil, max: nil)

[min, max] の範囲内(両端を含む)の DateTime 値を検索します。開いた境界には nil を指定する(またはキーワードを省略する)と開放されます。境界は Query DSL が受け付ける任意の形式の String リテラル(RFC 3339 の "2024-01-01T09:00:00+09:00"(UTC に正規化)、オフセットなしの "YYYY-MM-DDTHH:MM:SS[.fff]"(UTC)、"YYYY-MM-DD"(その日の 0 時 UTC))、または iso8601 に応答する任意のオブジェクト(Time、DateTime)です。不正な境界は構築時に ArgumentError を発生させます。クエリオブジェクトを受け付ける場所(Index#search、BooleanQuery、SearchRequest)ならどこでも使用できます。

GeoDistanceQuery

Laurus::GeoDistanceQuery.within_radius(field, lat, lon, distance_m)

地理的距離検索(半径指定)。指定した地点から distance_m メートル以内の (lat, lon) 座標を持つドキュメントを返します。

GeoBoundingBoxQuery

Laurus::GeoBoundingBoxQuery.within_bounding_box(
  field, min_lat, min_lon, max_lat, max_lon,
)

地理的範囲(バウンディングボックス)検索。軸並行 [min_lat, max_lat] × [min_lon, max_lon] 内の (lat, lon) 座標を持つドキュメントを返します。

Geo3dDistanceQuery

Laurus::Geo3dDistanceQuery.within_sphere(field, x, y, z, distance_m)

3D ECEF 座標フィールドへの球距離検索。中心 (x, y, z) から distance_m メートル以内 の座標を持つドキュメントを返します。ECEF の理論については Geo3d の概念 を参照。

Geo3dBoundingBoxQuery

Laurus::Geo3dBoundingBoxQuery.within_box(
  field,
  min_x, min_y, min_z,
  max_x, max_y, max_z,
)

軸並行 3D 範囲(AABB)検索。

Geo3dNearestQuery

Laurus::Geo3dNearestQuery.k_nearest(
  field, x, y, z, k,
  initial_radius_m: nil,
  max_radius_m: nil,
)

3D ECEF 座標フィールドへの k 最近傍検索。initial_radius_m: / max_radius_m: キーワード引数(オプション)で反復拡張サーチの探索コーンを調整できます。

BooleanQuery

bq = Laurus::BooleanQuery.new
bq.must(query)
bq.should(query)
bq.must_not(query)

複合ブールクエリ。must 節はすべて一致する必要があり、must_not 節は一致してはなりません。should 節はスコアリングに寄与し、must 節が無い場合は少なくとも1つが一致する必要があります。

SpanQuery

# 単一語句
Laurus::SpanQuery.term(field, term)

# Near: slop 位置以内の語句
Laurus::SpanQuery.near(field, terms, slop: 0, ordered: true)

# ネストされた SpanQuery 句を使った Near
Laurus::SpanQuery.near_spans(field, clauses, slop: 0, ordered: true)

# Containing: big スパンが little スパンを含む
Laurus::SpanQuery.containing(field, big, little)

# Within: 最大距離での include スパンと exclude スパン
Laurus::SpanQuery.within(field, include_span, exclude_span, distance)

位置・近接スパンクエリ。near は語句文字列の配列を受け取り、near_spans はネスト式のために SpanQuery オブジェクトの配列を受け取ります。

VectorQuery

Laurus::VectorQuery.new(field, vector)

事前計算済みエンベディングベクトルを使った近似最近傍検索を行います。vector は Float の配列です。

VectorTextQuery

Laurus::VectorTextQuery.new(field, text)

クエリ時に text をエンベディングに変換してベクトル検索を行います。インデックスにエンベダーの設定が必要です。


SearchRequest

高度な制御が必要な場合の完全なリクエストクラスです。

Laurus::SearchRequest.new(
  query: nil,
  lexical_query: nil,
  vector_query: nil,
  filter_query: nil,
  fusion: nil,
  limit: 10,
  offset: 0,
  highlight: nil,
  rescore: nil,
)
パラメータ説明
query:DSL 文字列または単一クエリオブジェクト。lexical_query: / vector_query: と排他的。
lexical_query:明示的なハイブリッド検索の Lexical コンポーネント。
vector_query:明示的なハイブリッド検索の Vector コンポーネント。
filter_query:スコアリング後に適用する Lexical フィルター。
fusion:フュージョンアルゴリズム(RRF または WeightedSum)。両コンポーネント指定時のデフォルトは RRF(k: 60)。
limit:最大結果件数(デフォルト 10)。
offset:ページネーションオフセット(デフォルト 0)。
highlight:Index#search の highlight: と同じ配列または Hash の形式(Issue #1134)。ハイライトを参照。
rescore:リクエスト(lexical・vector・ハイブリッドのいずれも可)の上位の結果を並べ替える LateInteractionRescore(Issue #1351)。それ以外の値は TypeError になります。LateInteractionRescore を参照。

LateInteractionRescore

検索結果の上位を late interaction(ColBERT の MaxSim)で再採点します(Issue #1351)。Index#search または SearchRequest.new の rescore: に渡します。

Laurus::LateInteractionRescore.new(field, query, window_size: nil)
パラメータ型デフォルト説明
fieldString–MultiVector フィールド(add_multi_vector_field)。
queryString | Array<Array<Numeric>>–クエリテキスト(フィールドのトークン単位のエンベダー、つまり "candle_colbert" のものが埋め込みます)、またはクエリのトークンベクトル(文書のトークンベクトルと同じモデルで計算したもの)。要素には Integer も使えます。
window_size:Integer | nilnil(100)再採点する 1 段目の上位結果の件数。最大 10,000。nil のときは既定値の 100 になります。
メソッド説明
window_size -> Integer再採点する上位結果の件数を返します。
inspect -> StringLateInteractionRescore(field="tokens", window_size=100) のような要約を返します。

1 段目の上位 window_size 件の結果を、フィールドに対する MaxSim で並べ替えます。再採点された結果の score はその MaxSim です。ウィンドウ外の結果は 1 段目の順序とスコアのまま、再採点された結果の後に続きます。順序の規則と類似度の詳細は ベクトル検索 → late interaction による再採点 を参照してください。

エラー: query が String でも数値の Array の Array でもない場合、コンストラクタが TypeError を発生させます。それ以外の値は検索時にエンジンが検査し、フィールドが存在しない・MultiVector フィールドでない、テキストクエリが空・フィールドにトークン単位のエンベダーが無い、クエリがフィールドの次元のトークンベクトルを 1〜1,024 個持たない、window_size が 1〜10,000 の範囲外、のいずれかの場合に、メッセージに rescore: ... を含む ArgumentError を発生させます。

schema = Laurus::Schema.new
schema.add_text_field("title")
schema.add_multi_vector_field("tokens", 2, distance: "dot_product")

index = Laurus::Index.new(schema: schema)
index.put_document("a", { "title" => "rust", "tokens" => [[0.1, 0.0]] })
index.put_document("b", { "title" => "rust language", "tokens" => [[0.9, 0.2]] })
index.commit

rescore = Laurus::LateInteractionRescore.new("tokens", [[1.0, 0.0], [0.0, 1.0]], window_size: 50)
results = index.search("title:rust", rescore: rescore)
results.map(&:id) # => ["b", "a"] -- "b" の MaxSim は 1.1、"a" は 0.1

# フィールドに "candle_colbert" のエンベダーがあれば、クエリをテキストで渡せる。
rescore = Laurus::LateInteractionRescore.new("body_colbert", "how do lifetimes work")

SearchResult

Index#search が返すクラスです。

result.id         # => String   -- 外部ドキュメント識別子
result.score      # => Float    -- 関連性スコア
result.document   # => Hash|nil -- 取得されたフィールド値。削除済みの場合は nil
result.highlights # => Hash     -- 要求したフィールドごとのハイライト済みフラグメント

highlights は highlight: で指定した各フィールドをハイライト済みフラグメント(最も良いものが先頭)にマッピングします。ハイライトされなかったフィールドは Hash に現れず、highlight: を要求しなかった場合 highlights は {} になります。詳細はハイライトを参照してください。


フュージョンアルゴリズム

RRF

Laurus::RRF.new(k: 60.0)

逆順位フュージョン(Reciprocal Rank Fusion)。Lexical と Vector の結果リストを順位位置によってマージします。k は平滑化定数で、値が大きいほど上位ランクの影響が小さくなります。

WeightedSum

Laurus::WeightedSum.new(lexical_weight: 0.5, vector_weight: 0.5)

両スコアリストをそれぞれ正規化した後、lexical_weight * lexical_score + vector_weight * vector_score として結合します。


テキスト解析

SynonymDictionary

dict = Laurus::SynonymDictionary.new
dict.add_synonym_group(["fast", "quick", "rapid"])

同義語グループの辞書です。グループ内のすべての語句は互いの同義語として扱われます。

WhitespaceTokenizer

tokenizer = Laurus::WhitespaceTokenizer.new
tokens = tokenizer.tokenize("hello world")

空白で分割してテキストをトークン化し、Token オブジェクトの配列を返します。

SynonymGraphFilter

filter = Laurus::SynonymGraphFilter.new(dictionary, keep_original: true, boost: 1.0)
expanded = filter.apply(tokens)

SynonymDictionary の同義語でトークンを展開するトークンフィルターです。

Token

token.text                # => String  -- トークンテキスト
token.position            # => Integer -- トークンストリーム内の位置
token.start_offset        # => Integer -- 元テキスト内の UTF-8 バイト開始オフセット
token.end_offset          # => Integer -- 元テキスト内の UTF-8 バイト終了オフセット
token.boost               # => Float   -- スコアブースト係数(1.0 = 調整なし)
token.stopped             # => Boolean -- ストップフィルターによって除去されたかどうか
token.position_increment  # => Integer -- 前のトークンの位置との差分
token.position_length     # => Integer -- このトークンがカバーする位置数
token.token_type          # => String または nil -- トークン種別(例: "alphanum")

オフセットは文字数ではなくバイト数です。非 ASCII のテキストではバイト単位で切り出します: text.byteslice(token.start_offset...token.end_offset)。

token_type は "alphanum"、"num"、"cjk"、"katakana"、"hiragana"、"hangul"、"punctuation"、"whitespace"、"synonym"、"email"、"url"、"other" のいずれか、または nil です。SynonymGraphFilter#apply は各トークンのオフセットと種別を保ち、挿入する同義語には種別 "synonym" と、置き換える語のオフセットを付けます。


フィールド値の型マッピング

Ruby の値は自動的に Laurus の DataValue 型に変換されます:

Ruby 型Laurus 型備考
nilNull
true / falseBool
IntegerInt64
FloatFloat64
StringText
Array(Integer)Int64Array多値整数フィールド。ベクトルフィールドでは配列を f32 にキャスト。空の Array は空の Int64Array
Array(数値)Float64Array多値浮動小数点フィールド(整数は拡張)。ベクトルフィールドでは配列を f32 にキャスト
Array(数値の Array の配列)VectorArrayMultiVector フィールド(add_multi_vector_field)のトークンベクトル。トークンごとにフィールドの次元の Array を 1 つ持つ(Issue #1351)。Integer の要素も可。true や String などそれ以外の要素は TypeError。get_documents や検索結果には返されない
Hash("lat", "lon")Geo2 つの Float 値
Hash("x", "y", "z")GeoEcef3 つの Float 値(メートル単位、3D ECEF 直交座標)
Array("lat", "lon" を持つ Hash の配列)GeoArrayフィールドに multi_valued: true が必要
Array("x", "y", "z" を持つ Hash の配列)GeoEcefArrayフィールドに multi_valued: true が必要
Time / String(iso8601 に応答)DateTimeiso8601 経由で変換
Array(Time、または iso8601 に応答する他のオブジェクトを含む)DateTimeArray各要素を RFC 3339 としてパース(iso8601 に応答するオブジェクトは先に変換)。日時でない要素は ArgumentError。フィールドに multi_valued: true が必要
Array(String)DateTimeArray または TextArray全要素が RFC 3339 としてパースできれば DateTimeArray、そうでなければ TextArray(Issue #1175。String の Array として読み戻される)。フィールドに multi_valued: true が必要。宣言済みの多値 Bytes フィールドでは、同じ base64 String の Array が要素ごとにデコードされる(Issue #1176)
Array(true / false)BoolArray全要素が true または false であること。[true, 1] のような混在 Array は TypeError。フィールドに multi_valued: true が必要