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 検索エンジンをラップします。

class Index {
  static create(
    path?: string | null,
    schema?: Schema,
    walSyncPolicy?: WalSyncPolicy,
    commitPolicy?: CommitPolicy,
  ): Promise<Index>;
}

ファクトリメソッド

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

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

メソッド

メソッド説明
putDocument(id, doc)ドキュメントを上書き保存。
addDocument(id, doc)既存バージョンを残してチャンクを追記。
putDocuments(docs)バッチ upsert。docs は Array<[id, doc]> で、バッチごとに WAL fsync 1 回で順に適用(重複 ID はデデュープ、最後が勝ち)。最初の不正エントリで fail-fast し、適用済みの prefix はロールバックされません。
addDocuments(docs)バッチチャンク追記。putDocuments と同様ですが、繰り返した ID は別バージョンとして蓄積されます。
getDocuments(id)指定 ID の全バージョンを取得。
deleteDocuments(id)指定 ID の全バージョンを削除。
commit()書き込みをフラッシュし変更を検索可能にする。
flushWal()WAL の永続性バリアを強制する。WAL 同期ポリシー / 永続性を参照。
search(query, limit?, offset?, highlight?, rescore?)DSL 文字列で検索。rescore は上位の結果を late interaction で並べ替えます。Late interaction による再採点(Rescore)を参照。
searchTerm(field, term, limit?, offset?, highlight?)完全一致 Term 検索。
searchVector(field, vector, limit?, offset?)事前計算ベクトルで検索。
searchVectorText(field, text, limit?, offset?)テキストを自動埋め込みして検索。
searchWithRequest(request)SearchRequest で検索。
searchBatch(queries, limit?, offset?, highlight?)複数の DSL 文字列クエリを並列実行します。results[i] は queries[i] に対応。戻り値は Promise<Array<Array<JsSearchResult>>>。空入力の場合は [] を返します。highlight はすべてのクエリに同一に適用されます。
stats()インデックス統計(documentCount、vectorFields)を返す。

ドキュメント操作と検索メソッドはすべて非同期で Promise を返します。 stats() は同期メソッドです。

stats() は次の形のオブジェクトを返します:

interface IndexStats {
  documentCount: number;
  vectorFields: Record<string, { count: number; dimension: number }>;
}

WAL 同期ポリシー / 永続性

永続インデックスでは、すべての書き込みが先行書き込みログ(WAL)に追記されます。 デフォルトでは WAL はすべてのレコードごとに fsync されるため、Promise が 解決した時点で各書き込みは完全に永続化されます。Index.create はオプションの walSyncPolicy を受け付け、永続性を一部犠牲にして書き込みスループットを 向上させることができます。また flushWal() で必要なときに永続性バリアを 強制できます。

class WalSyncPolicy {
  static perRecord(): WalSyncPolicy;
  static group(
    maxRecords?: number,
    maxBytes?: number,
    maxIntervalMs?: number,
  ): WalSyncPolicy;
}
コンストラクタ説明
WalSyncPolicy.perRecord()デフォルト。WAL レコードごとに fsync し、書き込みごとに完全に永続化します。
WalSyncPolicy.group(...)グループコミット。複数の書き込みにまたがって fsync をまとめます。

group(...) のパラメータ(引数を省略するとそのデフォルトを維持):

パラメータデフォルト説明
maxRecords1024この件数のレコードが蓄積されたらフラッシュします。
maxBytes1048576(1 MiB)この量の未同期バイトが蓄積されたらフラッシュします。
maxIntervalMsなし任意の定期フラッシュタイマー(ミリ秒)。省略するとタイマー無効。

グループコミットでは、maxRecords または maxBytes のいずれかに達した 時点で WAL がフラッシュされ、commit() 時にも必ずフラッシュされます。 クラッシュ時には最後の未同期バッチまでを失う可能性があります — これは SQLite の synchronous = NORMAL と同じトレードオフです。完全な commit() を 行わずにこれまで書き込んだ内容をディスクへ強制するには flushWal() を 呼び出します。

メソッド説明
flushWal()今すぐ WAL の永続性バリアを強制します。Promise<void> を返します。
import { Index, WalSyncPolicy } from "laurus-nodejs";

// 1 秒の定期フラッシュタイマー付きでグループコミットを有効化します。
const policy = WalSyncPolicy.group(4096, undefined, 1000);
const index = await Index.create("./myindex", schema, policy);

for (let i = 0; i < 10000; i++) {
  await index.putDocument(`doc${i}`, { title: `Document ${i}` });
}

// まだコミットせずに永続性バリアを強制します。
await index.flushWal();

await index.commit(); // WAL もフラッシュされます

walSyncPolicy を省略する(または WalSyncPolicy.perRecord() を渡す)と、 デフォルトの完全に永続的な動作が維持されます。

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

デフォルトでは呼び出し側がすべての commit() を駆動するため、保留中の変更は 明示的に呼び出したときにのみ検索可能になります。Index.create はオプションの commitPolicy を受け付け、代わりに取り込み駆動のタイミングでエンジンに自動 コミットさせることができます。これにより明示的な commit() なしで書き込みが 実体化されます。

class CommitPolicy {
  static manual(): CommitPolicy;
  static everyDocs(n: number): CommitPolicy;
  static intervalMs(ms: number): CommitPolicy;
}
コンストラクタ説明
CommitPolicy.manual()デフォルト。自動コミットなし。呼び出し側がすべての commit() を駆動します。
CommitPolicy.everyDocs(n)適用されたドキュメント n 件ごとに自動コミットします。
CommitPolicy.intervalMs(ms)バックグラウンドタイマーにより少なくとも ms ミリ秒ごとに自動コミットします(デフォルト: なし)。ネイティブ専用。wasm では no-op(バックグラウンドスレッドがないため)。

everyDocs(n) では、エンジンは適用されたドキュメント n 件ごとにコミットし、 その件数は単発・バッチ両方の取り込みにまたがって数えられます — 1 つのバッチ 内部でもドキュメント n 件ごとにコミットされます。everyDocs(0) は有効で 自動コミットを無効化し、CommitPolicy.manual() と等価です。

intervalMs(ms) は everyDocs(n) の時間ベース版です。バックグラウンドタイマー が少なくとも ms ミリ秒ごとに自動コミットするため、取り込みがアイドル状態でも 末尾の部分的なバッチが実体化されます。これはネイティブ専用です — wasm には バックグラウンドスレッドがないため、エンジンは intervalMs を no-op として扱い ます(ファクトリは値を構築しますが、WebAssembly ではタイマーによるコミットは 発生しません)。

commitPolicy は walSyncPolicy と直交します。walSyncPolicy は WAL の fsync 永続性を制御するのに対し、commitPolicy は保留中の変更を検索可能な コミットへいつ実体化するかを制御します。両者は自由に組み合わせられます。

import { Index, CommitPolicy } from "laurus-nodejs";

// 適用されたドキュメント 1000 件ごとに自動コミットします。
const index = await Index.create(
  null,
  schema,
  undefined,
  CommitPolicy.everyDocs(1000),
);

for (let i = 0; i < 10000; i++) {
  await index.putDocument(`doc${i}`, { title: `Document ${i}` });
}
// エンジンはすでに 10 回コミット済みです。明示的な commit() は不要です。

commitPolicy を省略する(または CommitPolicy.manual() を渡す)と、 デフォルトの呼び出し側駆動のコミット動作が維持されます。


Schema

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

class Schema {
  constructor();
}

フィールドメソッド

メソッド説明
addTextField(name, stored?, indexed?, termVectors?, docValues?, analyzer?, multiValued?, positionIncrementGap?)全文検索フィールド(転置インデックス、BM25)。docValues は値を DocValues(ソート・ファセット・集計が読み取る列指向ストア)にもコピーするかどうかを制御します(Issue #1047、デフォルト true)。stored も true の場合のみ有効です。multiValued: true で文字列の配列を受け付けます(Issue #1175): term クエリはいずれかの要素がタームを含めばマッチし、フレーズクエリは slop が positionIncrementGap(デフォルト 100。0 にすると要素を連結したものとして付番)に達しない限り 2 つの要素をまたぎません。値は文字列の配列として読み戻されます。analyzer にはパラメータ不要の組込名("standard" / "english" / "keyword" / "simple" / "noop"、または addAnalyzer で登録したカスタム名)を指定します。Lindera 辞書パスが必要な Japanese プリセットを使う場合は、lindera tokenizer を含むカスタム analyzer を登録して、その名前を参照してください。
addIntegerField(name, stored?, indexed?, multiValued?, docValues?)64 ビット整数フィールド。multiValued: true で整数配列を受け付け(範囲クエリは “any match”)。docValues は上記を参照。
addFloatField(name, stored?, indexed?, multiValued?, docValues?)64 ビット浮動小数点フィールド。multiValued: true で浮動小数点配列を受け付け(範囲クエリは “any match”)。docValues は上記を参照。
addBooleanField(name, stored?, indexed?, multiValued?, docValues?)真偽値フィールド。multiValued: true で真偽値の配列を受け付け(flags:true のような term クエリはいずれかの要素が値と等しければマッチ。値は真偽値の配列として読み戻されます)。docValues は上記を参照。
addBytesField(name, stored?, multiValued?)バイナリデータフィールド。docValues オプションはありません —— Bytes の値は設定にかかわらず DocValues に一切書き込まれないためです。multiValued: true を渡すと base64 文字列の配列を受け付け(Issue #1176)、単一の base64 文字列と同じ方法でスキーマ対応の変換が要素ごとにデコードします。Bytes はそもそもインデックスされないため、他の multiValued オプションと異なりクエリ一致の意味論はなく、保存時の形と取り込み時の許容個数を変えるだけです。値は MIME を落としたバイト整数配列の配列として読み戻され、スカラーフィールドと同じ入出力の非対称性を持ちます。
addGeoField(name, stored?, indexed?, multiValued?, docValues?)地理座標フィールド。multiValued: true で { lat, lon } オブジェクトの配列を受け付け(距離 / バウンディングボックスクエリはいずれかのポイントが条件を満たせばマッチ)。docValues は上記を参照。
addGeo3dField(name, stored?, indexed?, multiValued?, docValues?)3D ECEF カルテシアン座標フィールド(x, y, z はメートル)。multiValued: true で { x, y, z } オブジェクトの配列を受け付け(距離 / バウンディングボックス / nearest クエリはいずれかのポイントが条件を満たせばマッチ)。詳細は Geo3d の概念。docValues は上記を参照。
addDatetimeField(name, stored?, indexed?, multiValued?, docValues?)UTC 日時フィールド。multiValued: true で RFC 3339 文字列の配列を受け付け(範囲クエリはいずれかの時刻が条件を満たせばマッチ。値は UTC に正規化した RFC 3339 文字列の配列として読み戻されます)。docValues は上記を参照。
addHnswField(name, dimension, distance?, m?, efConstruction?, defaultEfSearch?, embedder?, quantizer?, subvectorCount?, rerankStorage?, pqCodebookPath?, baseWeight?)HNSW ベクトルフィールド。baseWeight は他の vector フィールドと同時に検索されたときの相対的なスコアリング優先度(Issue #1084)。ウェイトを参照。
addFlatField(name, dimension, distance?, embedder?, baseWeight?)Flat(全探索)ベクトルフィールド。
addIvfField(name, dimension, distance?, nClusters?, nProbe?, embedder?, baseWeight?)IVF ベクトルフィールド。
addMultiVectorField(name, dimension, distance?, embedder?, storage?)文書ごとに可変個のトークンベクトルを保持する MultiVector フィールド。late interaction による再採点に使います(Issue #1351)。MultiVector フィールドを参照。dimension は各トークンベクトルの次元で、0 より大きくなければなりません。distance は "cosine"(デフォルト)または "dot_product" です。どちらもフィールド追加時に検証され、不正なら code InvalidArg の例外を投げます。storage は各トークンベクトルのディスク上の要素種別を指定します(Issue #1346)— "f32"(デフォルト、正確)、"f16"(2倍小さい)、"int8"(約4倍小さい)のいずれかで、認識できない値も InvalidArg の例外を投げます。embedder にはトークン単位の Embedder("candle_colbert")の名前を指定し、テキスト値と再採点のクエリテキストを埋め込みます。このフィールド単体では検索できず、トークンベクトルは保存されないため、getDocuments や検索結果には含まれません。
addEmbedder(name, config)名前付き Embedder を登録。Embedderを参照。
addAnalyzer(name, tokenizer, charFilters?, tokenFilters?)カスタムアナライザ定義を登録します。tokenizer は必須、charFilters/tokenFilters は省略可能なオブジェクトの配列です。各オブジェクトはスキーマ TOML/JSON 形式と同じ { type: "...", ... } 形式で、キーは snake_case のままです(下記参照)。組み込みアナライザ用に予約された名前(standard、keyword、english、simple、noop)は例外になり、その名前を定義したスキーマ(fromToml で読み込んだものなど)で新しいインデックスを Index.create する場合も同じです。正規表現の妥当性など意味的な検証は、このメソッド呼び出し時点ではなく Index 構築時に行われます。
analyzerNames()addAnalyzer で登録された、または TOML から読み込まれたカスタムアナライザの名前一覧を返します。
Schema.fromToml(tomlStr) (静的メソッド)laurus-cli create index --schema と同じ形式の TOML 文字列からスキーマを読み込みます。
Schema.fromTomlFile(path) (静的メソッド)TOML ファイルからスキーマを読み込みます。
toToml()このスキーマを laurus-cli と同じ形式の TOML 文字列にシリアライズします。
toTomlFile(path)このスキーマを TOML ファイルに書き込みます。
setDefaultFields(fields)デフォルト検索フィールドを設定。
setDynamicFieldPolicy(policy)未宣言フィールドの扱いを設定。policy は "strict" / "dynamic"(デフォルト)/ "ignore"。詳細は下記を参照。
dynamicFieldPolicy()現在のポリシーを小文字の文字列で返す。
fieldNames()全フィールド名を返す。
toString()スキーマの文字列表現("Schema(fields=[...])" 形式)を返す。

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

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

上記のどの add*Field メソッドも、name が _(_id を除く)で始まる場合は例外を投げ、フィールドを追加しない。fromToml / fromTomlFile で読み込んだスキーマはそのようなフィールドを引き続き受け付けるため、永続化済みのスキーマも読み込めるが、そこから新しい Index を作成すると例外を投げる。詳細はフィールド命名規則を参照。

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

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

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

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

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

addAnalyzer(name, tokenizer, charFilters?, tokenFilters?) と [analyzers.<name>] TOML セクションで使用します。tokenizer は単一のオブジェクト、 charFilters/tokenFilters はオブジェクトの配列で、配列の順序どおりに適用されます。

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

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

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

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

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

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

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

Embedder

addEmbedder(name, config) と [embedders.<name>] TOML セクションで使用します。 config は type キーでバックエンドを選ぶオブジェクトです。アナライザ コンポーネントと同じく、キーはスキーマ TOML/JSON 形式に合わせて snake_case の ままです。

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

type必須キー省略可能キーフィーチャーフラグ
"precomputed"––(常に利用可能)
"candle_bert"model–embeddings-candle
"candle_clip"model–embeddings-multimodal
"openai"model–embeddings-openai
"candle_colbert"modelrevision、query_maxlen、doc_maxlenembeddings-candle

"candle_colbert" は BERT ベースの ColBERT チェックポイント("colbert-ir/colbertv2.0" など)を実行してトークンごとに 1 本のベクトルを出力するため、MultiVector フィールド(addMultiVectorField)でしか使えません。逆に MultiVector フィールドが 受け付けるのは "candle_colbert" か "precomputed" だけで、それ以外の組み合わせでは Index.create が例外を投げます。revision はモデルリポジトリのブランチ・タグ・ コミットを固定します(再埋め込みで同じベクトルを再現できるよう、コミットを 固定してください)。query_maxlen と doc_maxlen はチェックポイントのクエリ長と 文書長(トークン数)を上書きします。

addEmbedder は、config がオブジェクトでない場合は embedder config must be an object、 type が無いか未知の値である場合や必須キーが無い場合は invalid embedder config: ... の例外を投げます。バインディングのビルドで有効にしていないフィーチャーフラグの タイプも登録はできますが、Index.create が例外を投げます。

const schema = new Schema();
schema.addEmbedder("colbert", {
  type: "candle_colbert",
  model: "answerdotai/answerai-colbert-small-v1",
  revision: "934fa8bb4ce2284f4c2baa232d81aca4d076fa5e",
});
schema.addTextField("body");
schema.addMultiVectorField("body_colbert", 96, "cosine", "colbert");

距離指標

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

クエリクラス

TermQuery

new TermQuery(field: string, term: string)

指定フィールドで完全一致する Term を含むドキュメントにマッチ。

PhraseQuery

new PhraseQuery(field: string, terms: string[])

指定順序で Term を含むドキュメントにマッチ。

FuzzyQuery

new FuzzyQuery(field: string, term: string, maxEdits?: number)

最大 maxEdits 編集距離までの近似マッチ(デフォルト 2)。

WildcardQuery

new WildcardQuery(field: string, pattern: string)

パターンマッチ。* は任意の文字列、? は任意の1文字。

NumericRangeQuery

new NumericRangeQuery(
  field: string,
  min?: number | null,
  max?: number | null,
  numericType?: "integer" | "float",
)

[min, max] 範囲の数値にマッチします。null(または省略)で開放端。 numericType は内部の範囲型を選択します("integer"(デフォルト)または "float")。それ以外の値は例外をスローします。

DateTimeRangeQuery

new DateTimeRangeQuery(
  field: string,
  min?: string | null,
  max?: string | null,
)

[min, max] 範囲(両端を含む)の DateTime 値にマッチします。null(または省略)で 開放端。境界は Query DSL が受け付ける任意の形式の文字列リテラルです: RFC 3339 ("2024-01-01T09:00:00+09:00"、UTC に正規化)、オフセットなしの "YYYY-MM-DDTHH:MM:SS[.fff]"(UTC)、または "YYYY-MM-DD"(その日の 0 時 UTC)。 Date は date.toISOString() で渡します。不正な境界は構築時に Error を スローします。BooleanQuery.mustDateTimeRange / shouldDateTimeRange / mustNotDateTimeRange および SearchRequest.setLexicalDateTimeRange / setFilterDateTimeRange で句として設定します。

GeoDistanceQuery

GeoDistanceQuery.withinRadius(
  field: string, lat: number, lon: number, distanceM: number,
): GeoDistanceQuery

地理的距離検索(半径指定)。

GeoBoundingBoxQuery

GeoBoundingBoxQuery.withinBoundingBox(
  field: string,
  minLat: number, minLon: number,
  maxLat: number, maxLon: number,
): GeoBoundingBoxQuery

地理的バウンディングボックス検索。

Geo3dDistanceQuery

Geo3dDistanceQuery.withinSphere(
  field: string,
  x: number, y: number, z: number,
  distanceM: number,
): Geo3dDistanceQuery

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

Geo3dBoundingBoxQuery

Geo3dBoundingBoxQuery.withinBox(
  field: string,
  minX: number, minY: number, minZ: number,
  maxX: number, maxY: number, maxZ: number,
): Geo3dBoundingBoxQuery

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

Geo3dNearestQuery

Geo3dNearestQuery.kNearest(
  field: string,
  x: number, y: number, z: number,
  k: number,
  initialRadiusM?: number,
  maxRadiusM?: number,
): Geo3dNearestQuery

3D ECEF 座標フィールドへの k 最近傍検索。initialRadiusM / maxRadiusM は 反復拡張サーチの探索コーンを調整します。

BooleanQuery

class BooleanQuery {
  constructor();
  // 各クエリタイプ X について(X は次のいずれか):
  //   { Term, Phrase, Fuzzy, Wildcard, NumericRange, DateTimeRange,
  //     GeoDistance, GeoBoundingBox,
  //     Geo3dDistance, Geo3dBoundingBox, Geo3dNearest,
  //     Boolean, Span }
  mustX(query: X): void;
  shouldX(query: X): void;
  mustNotX(query: X): void;
}

MUST / SHOULD / MUST_NOT 句による複合ブーリアンクエリ。各句は対応するクエリ クラスのインスタンスを引数に取ります。例: mustTerm(new TermQuery("body", "rust")) や shouldGeo3dNearest(Geo3dNearestQuery.kNearest(...))。

Node.js バインディングは多態 must(query) ではなく 39 個の per-type メソッド (13 クエリタイプ × 3 極性)を公開しています。これは js_name を上書きした クラスに対する napi-derive の Either<&T, ...> 引数バリデーションの制限を 回避するためです。

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

const bq = new BooleanQuery();
bq.mustTerm(new TermQuery("body", "programming"));
bq.mustNotTerm(new TermQuery("title", "python"));
bq.shouldFuzzy(new FuzzyQuery("body", "data", 1));

SpanQuery

SpanQuery.term(field: string, term: string): SpanQuery
SpanQuery.near(
  field: string, terms: string[],
  slop?: number, ordered?: boolean,
): SpanQuery
SpanQuery.nearSpans(
  field: string, clauses: SpanQuery[],
  slop?: number, ordered?: boolean,
): SpanQuery
SpanQuery.containing(
  field: string, big: SpanQuery, little: SpanQuery,
): SpanQuery
SpanQuery.within(
  field: string,
  include: SpanQuery, exclude: SpanQuery, distance: number,
): SpanQuery

位置・近接ベースのスパンクエリ。

VectorQuery

new VectorQuery(field: string, vector: number[])

事前計算済み埋め込みベクトルによる最近傍検索。

VectorTextQuery

new VectorTextQuery(field: string, text: string)

クエリ時にテキストを埋め込みに変換して検索。 インデックスに Embedder の設定が必要。


SearchRequest

高度な制御のための全機能検索リクエスト。

interface SearchRequestOptions {
  queryDsl?: string;
  limit?: number;   // デフォルト 10
  offset?: number;  // デフォルト 0
  highlight?: HighlightOptions;
  rescore?: RescoreOptions;
}

class SearchRequest {
  constructor(options?: SearchRequestOptions);
}

コンストラクタにはプリミティブな options を渡し、多態クエリ句は下記の per-type セッターで設定します。BooleanQuery 同様、napi-derive の Either<&T, ...> バリデーション制限を回避するため per-type 化されています。 highlight と rescore はプレーンなデータ(クラスインスタンスのユニオンではない) なので、セッターを介さず SearchRequestOptions に直接持たせています。

DSL とフュージョンセッター

メソッド説明
setQueryDsl(dsl: string)DSL 文字列クエリを設定。
setRrfFusion(rrf: RRF)RRF フュージョンを使用。
setWeightedSumFusion(ws: WeightedSum)加重和フュージョンを使用。

ベクトルセッター

メソッド説明
setVectorQuery(query: VectorQuery)事前計算ベクトルクエリを設定。
setVectorTextQuery(query: VectorTextQuery)テキストベースのベクトルクエリを設定(登録 Embedder で自動埋め込み)。

Lexical セッター(per-type)

X を { Term, Phrase, Fuzzy, Wildcard, NumericRange, DateTimeRange, GeoDistance, GeoBoundingBox, Geo3dDistance, Geo3dBoundingBox, Geo3dNearest, Boolean, Span } の各クエリタイプとして、以下のメソッドが公開されています:

メソッド説明
setLexicalX(query: X)明示的なハイブリッドリクエストの Lexical コンポーネントを設定。
setFilterX(query: X)スコアリング後のフィルタコンポーネントを設定。

合計 26 個の per-type セッター(13 lexical + 13 filter)に加え、上記の DSL / ベクトル / フュージョンセッターが利用可能です。例: setLexicalNumericRange(q) / setFilterNumericRange(q)、 setLexicalDateTimeRange(q) / setFilterDateTimeRange(q)。

const req = new SearchRequest({ limit: 5 });
req.setLexicalTerm(new TermQuery("title", "rust"));
req.setVectorQuery(new VectorQuery("embedding", [0.1, 0.2, 0.3, 0.4]));
req.setRrfFusion(new RRF(60.0));
const results = await index.searchWithRequest(req);

ハイライト

search、searchTerm、searchBatch、SearchRequestOptions は省略可能な highlight オブジェクトを受け付けます(Issue #1134)。

interface HighlightOptions {
  fields: string[];
  fragmentSize?: number;    // デフォルト 150
  maxFragments?: number;    // デフォルト 5
  tag?: string;             // デフォルト "mark"
  cssClass?: string;
  requireFieldMatch?: boolean; // デフォルト true
}

必須なのは fields のみです。ハイライトは同じ呼び出しに渡したクエリに従い、stored: true のテキストフィールドのみハイライト可能です — 保存されていない、テキスト型でない、またはマッチしなかったフィールドは結果の highlights オブジェクトに現れません。

const results = await index.search("body:rust", 10, 0, { fields: ["body"], tag: "em" });
// results[0].highlights => { body: ["<em>Rust</em> is a systems programming language."] }

Late interaction による再採点(Rescore)

search(末尾の rescore 引数)と SearchRequestOptions は省略可能な rescore オブジェクトを受け付けます(Issue #1351)。lexical・vector・ハイブリッドのどの 第 1 段階の検索でも、上位の結果を MultiVector フィールド に対する ColBERT 型の late interaction(MaxSim)で並べ替えます。仕組みは ベクトル検索 → Late Interaction による再採点(Rescore) を参照してください。

// index.d.ts では JsRescoreOptions としてエクスポートされます。
interface RescoreOptions {
  field: string;          // MultiVector フィールド
  vectors?: number[][];   // クエリのトークンベクトル
  text?: string;          // フィールドの Embedder で埋め込むクエリテキスト
  windowSize?: number;    // デフォルト 100、最大 10,000
}
フィールド型デフォルト説明
fieldstring–採点に使う MultiVector フィールド。
vectorsnumber[][]–クエリのトークンベクトル。1〜1,024 本で、どれもフィールドの次元を持ち、値は有限でなければなりません。文書のトークンベクトルを作ったのと同じモデルで計算してください。
textstring–クエリテキスト。フィールドのトークン単位の Embedder("candle_colbert")がクエリとして埋め込みます。空白のみは不可。
windowSizenumber100再採点する第 1 段階の上位件数。1〜10,000。

vectors と text はちょうど一方だけを指定します。そうでない場合、検索は code InvalidArg、メッセージ rescore needs exactly one of vectors or text で reject されます。それ以外の値はエンジンが検索時に検証し、field が未知または MultiVector フィールドでない、text が空白のみまたはフィールドにトークン単位の Embedder が無い、クエリのベクトル数が範囲外または次元が異なる、windowSize が 範囲外、のいずれかの場合に code InvalidArg、rescore: ... を含むメッセージで reject されます。

並び順とスコア:

  • 第 1 段階の上位 windowSize 件を MaxSim の高い順に並べ替え、再採点された結果の score はその MaxSim になります。
  • ウィンドウ内でフィールドにトークンベクトルを持たない結果が第 1 段階の順で続き、 最後にウィンドウ外の結果がそのまま続きます。どちらも第 1 段階のスコアを保ち、 そのスコアは MaxSim と比較できません。

searchTerm、searchVector、searchVectorText、searchBatch は rescore 引数を取りません。代わりに SearchRequest を組み立ててください。SearchRequest はハイブリッドを含むどの第 1 段階でも再採点できます。

// DSL 検索を、クエリのトークンベクトルで再採点します。
const results = await index.search("title:rust", 10, 0, undefined, {
  field: "tokens",
  vectors: [[1, 0], [0, 1]],
});

// ハイブリッド検索を、クエリテキストで再採点します
// (フィールドに "candle_colbert" の Embedder が必要)。
const req = new SearchRequest({
  limit: 10,
  rescore: { field: "body_colbert", text: "how do lifetimes work", windowSize: 50 },
});
req.setLexicalTerm(new TermQuery("body", "lifetimes"));
req.setVectorQuery(new VectorQuery("body_vec", queryEmbedding));
req.setRrfFusion(new RRF(60.0));
const reranked = await index.searchWithRequest(req);

SearchResult

検索メソッドが配列として返す結果。

interface SearchResult {
  id: string;        // 外部ドキュメント識別子
  score: number;     // 関連度スコア
  document: object | null; // 取得フィールド、stored=false の場合は null
  highlights: Record<string, string[]>; // 要求したフィールドごとのハイライト済みフラグメント
}

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


融合アルゴリズム

RRF

new RRF(k?: number)  // デフォルト 60.0

Reciprocal Rank Fusion。ランク位置で Lexical と Vector の 結果リストを統合。

WeightedSum

new WeightedSum(
  lexicalWeight?: number,  // デフォルト 0.5
  vectorWeight?: number,   // デフォルト 0.5
)

両スコアリストを個別に正規化し、加重和で結合。


テキスト解析

SynonymDictionary

class SynonymDictionary {
  constructor();
  addSynonymGroup(terms: string[]): void;
}

WhitespaceTokenizer

class WhitespaceTokenizer {
  constructor();
  tokenize(text: string): Token[];
}

SynonymGraphFilter

class SynonymGraphFilter {
  constructor(
    dictionary: SynonymDictionary,
    keepOriginal?: boolean,  // デフォルト true
    boost?: number,          // デフォルト 1.0
  );
  apply(tokens: Token[]): Token[];
}

Token

interface Token {
  text: string;
  position: number;
  startOffset: number;
  endOffset: number;
  boost: number;
  stopped: boolean;
  positionIncrement: number;
  positionLength: number;
  tokenType?: string;
}

startOffset と endOffset は、元テキストの UTF-8 バイトオフセットです。非 ASCII のテキストでは JavaScript の文字列(UTF-16)の添字と一致しないため、エンコードしたバイト列を切り出します: new TextDecoder().decode(new TextEncoder().encode(text).slice(tok.startOffset, tok.endOffset))。

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

手で組み立てたトークンでは tokenType を省略できます。その場合、複数語の同義語は語のオフセットが連続しているときだけ一致するため、空白で区切られた語には tokenType: "alphanum" を付けてください。


フィールド値の型

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

JavaScript 型Laurus 型備考
nullNull
booleanBool
number(整数)Int64
number(浮動小数点)Float64
stringTextISO 8601 文字列は DateTime になる
number[](すべて整数)Int64Array多値整数フィールド。ベクトルフィールドでは配列を f32 にキャスト。空配列は空の Int64Array
number[]Float64Array多値浮動小数点フィールド(整数は拡張)。ベクトルフィールドでは配列を f32 にキャスト
{ lat, lon }Geo2 つの number 値
{ x, y, z }GeoEcef3 つの number 値(メートル単位、3D ECEF 直交座標)
{ lat, lon }[]GeoArray{ lat, lon } オブジェクトの配列。フィールドに multiValued: true が必要
{ x, y, z }[]GeoEcefArray{ x, y, z } オブジェクトの配列。フィールドに multiValued: true が必要
number[][]VectorArrayMultiVector フィールド(addMultiVectorField)のトークンベクトル。フィールドの次元を持つ配列 1〜8,192 個。内側の配列の長さがそろっていないと、取り込みは token vectors must share one dimension で拒否される。トークン単位の Embedder を持つフィールドは string も受け付け、トークンベクトルに埋め込む。保存されないため、getDocuments や検索結果には含まれない
string[](すべて RFC 3339)DateTimeArrayRFC 3339 日時文字列の配列(HTTP ゲートウェイと同じ infer_from_json の規則)。フィールドに multiValued: true が必要
string[](すべてが RFC 3339 ではない)TextArray文字列の配列(Issue #1175)。フィールドに multiValued: true が必要。string[] として読み戻される。宣言済みの多値 Bytes フィールドでは、同じ base64 文字列の配列がスキーマ対応の変換によって要素ごとに BytesArray にデコードされる(Issue #1176)
boolean[]BoolArray真偽値の配列(HTTP ゲートウェイと同じ infer_from_json の規則)。フィールドに multiValued: true が必要。[true, 1] のような混在配列は拒否される