全文検索
全文検索機能を使用すると、テーブル 上のコンテンツに対してキーワード検索ができます。大量のテキストデータの中から、キーワードに関連するコンテンツを効率的に検索できます。
全文検索機能の特徴は以下の通りです。
- 日本語に対応
- テーブルのデータをキーワードで検索
- 複数キーワードでの検索に対応
- Consumer向けの公開データのみを対象とした検索
- CMS向けの下書きデータを含む検索
ユースケース例
例えば、求人情報の検索に活用できます。「東京 エンジニア リモートワーク」のように地域名、職種、働き方などのキーワードで検索できます。求職者は希望条件に合った求人情報を直感的に探せます。
他にも、ブログ記事や製品カタログの検索に活用できます。
機能の有効化
スキーマで テーブル ごとに有効化できます。enableSearch プロパティを true に設定します。
import { defineConfig } from "@orizm/cli/config";
export default defineConfig({
tables: {
blog: {
columns: {},
enableSearch: true,
},
},
});検索可能なデータ型
以下のデータ型のカラムに対して全文検索が可能です。
キーワードで検索する
キーワード検索にはSDKの search() メソッドを使用します。
query プロパティに検索キーワードを指定します。複数キーワードの場合は、スペースで区切ります。
await cmsClient.tables.blog.search({ query: "キーワード1 キーワード2" });search() では filter パラメータを使用した絞り込みに制約があります。group
による絞り込みのみ対応しています。
このメソッドはCMS SDK、Consumer SDKのどちらでも利用できます。
- Consumer SDK: 公開データのみ検索可能
- CMS SDK: 下書きデータも検索可能(主にCMS上での管理用途)
検索モード(セマンティック検索)
search() の mode パラメータで検索方式を選択できます。
| mode | 説明 |
|---|---|
keyword(既定) | 従来の全文検索。mode を省略した場合もこの方式です |
semantic | 意味検索。クエリとコンテンツの意味の近さで検索します |
hybrid | keyword と semantic の両方で検索し、結果を 1 つに融合して返します |
await cmsClient.tables.blog.search({ query: "経費の精算", mode: "hybrid" });semantic と hybrid は、プロジェクトでセマンティック検索を有効にすると使えます。
セマンティック検索を利用できるかどうかは、プロジェクトの設定とシステム側の設定の両方で決まります。利用中の環境で使えるかどうかは管理者に確認してください。システム側で無効な環境では、プロジェクトの有効化は成功しても
semantic / hybrid の検索は 403 になるか、keyword と同じ結果を返します。
有効化と対象の列
orizm.config.ts に次の 3 つを書いて orizm schema push します。
- プロジェクトの
semanticSearch: { enabled: true } - 検索したいテーブルの
enableSearch: true - 意味検索の対象にする string 列の
semanticSearch: true
export default defineConfig({
semanticSearch: { enabled: true },
tables: {
blog: {
columns: {
title: { type: "string", required: true, semanticSearch: true },
body: { type: "string", required: true, semanticSearch: true },
slug: { type: "string", required: true }, // keyword 検索の対象にはなるが、意味検索には使わない
},
enableSearch: true,
},
},
});semanticSearch: true の列を持つテーブルには enableSearch: true が必要です。無いと push が失敗します。意味の近さを測る本文は、semanticSearch: true の列の値を定義順につないだテキストです。tableModules 内の string 列にも付けられます。埋め込みの費用はこの本文の量で決まるため、slug や型番の一覧のような機械的な値の列には付けないでください。
列を絞れば精度が上がるわけではありません。意味検索の精度は文脈の量で決まります。タイトル・本文・説明のように人が読んで内容を表す列はまとめて付け、識別子や機械的な値の列だけを外してください。
有効化を解除するときは enabled: false を明示して push します。宣言を省略した場合は現状維持です。無効化するとベクトルインデックスは削除され、再び有効化すると既存コンテンツ全件の埋め込みを作り直します。テーブルの最後の semanticSearch: true を外したときも同じで、--dry-run では vectorIndex:delete と表示されます。
埋め込みモデルを選ぶ(embeddingModel)
埋め込みに使うモデルはテーブルごとに embeddingModel で選べます。省略すると標準モデルです。指定できる名前は natural-language だけで、他の名前を書いた push は --dry-run でも失敗します。orizm schema validate や型チェックでは検出されません。
| 名前 | 向いている内容 |
|---|---|
| (省略) | 標準モデル。製品名・型番・固有名詞のような語で探すテーブル |
natural-language | 自然文重視。記事やブログのように人が読む長い文章を、言い換えや課題の表現で探すテーブル |
本文の種類よりも、利用者が入れる検索クエリの形で決めてください。「申請が差し戻される理由」のように文で探すなら natural-language、型番や固有名詞のように語で探すなら省略です。迷う場合は省略で始め、言い換えで当たらない例が出てから切り替えてください。natural-language は型番のような文字列の一致が標準より弱いため、型番でも探すテーブルでは hybrid を使ってください。
export default defineConfig({
semanticSearch: { enabled: true },
tables: {
blog: {
columns: {
title: { type: "string", required: true, semanticSearch: true },
body: { type: "string", required: true, semanticSearch: true },
},
enableSearch: true,
embeddingModel: "natural-language", // テーブル単位で指定する。省略 = 標準モデル
},
},
});embeddingModel を変えて push すると、そのテーブルの索引を新しいモデルで全件作り直します。--dry-run では embeddingModel:update と表示されます。作り直しの間も既存の索引で検索は続き、完了した時点で切り替わります。作り直しには既存コンテンツ全件分の埋め込みの費用が掛かり、所要時間は本文の長さの合計に比例します。enableDrafts: true のテーブルは下書きと公開の 2 つの索引を作り直すため、時間と費用は最大で 2 倍になります。
mode ごとの制約
| 項目 | keyword | semantic / hybrid |
|---|---|---|
offset + limit の上限 | 10000 | 100 |
limit: 0(件数だけ取る) | 使えます | 使えません |
query の文字数 | 上限なし | 512 文字まで |
totalCount | 正確な件数 | 目安の値。ページ数の計算に使えず、該当が無いクエリでも 0 になりません |
| フォーム投稿(submissions)の検索 | 使えます | 使えません |
| 流量制限 | 書き込みの枠 | 書き込みの枠に加えて専用の枠(制限事項) |
上限を超えた指定は 400 になります。limit と offset は 0 以上の整数だけを受け付けます。空文字や空白だけの query は、mode を問わず 0 件を返します。深いページングや正確な件数が必要な場合は keyword を使ってください。
埋め込みの処理が一時的に失敗したときは、検索を失敗させる代わりに keyword
と同じ結果を 200 で返します。このとき _semantic と _dictionary
は付かず、検索辞書による言い換えも使われません。
検索結果の _semantic
semantic / hybrid で検索すると、contents の各行に _semantic が付きます。コンテンツは一定の長さの断片(チャンク)に分けて索引されるため、長い本文のどこが当たったのかを chunks で示せます。
| フィールド | 説明 |
|---|---|
score | 行のスコア。semantic と hybrid で尺度が違うため、mode をまたいで比較できません |
chunks | 当たったチャンクの配列。各要素は text(チャンク本文)と score を持ちます |
keywordRank | キーワード検索での順位。hybrid でキーワード検索に当たった行にだけ付きます |
vectorRank | 意味検索での順位。semantic では常に付き、hybrid では意味検索に当たった行にだけ付きます |
const result = await cmsClient.tables.blog.search({
query: "申請が差し戻される理由",
mode: "hybrid",
});
for (const row of result.contents) {
console.log(row.title, row._semantic?.score, row._semantic?.chunks[0]?.text);
}_semantic は常に省略され得るものとして扱ってください。本文を更新してから索引に反映されるまでの間は、古い本文を返さずに chunks を空にします。keywordRank と vectorRank には null が入らないため、有無は undefined との比較で判定してください。
検索辞書
hybrid の検索では、検索辞書 に登録した同義語がクエリの言い換えに使われます。CMS SDK と Consumer SDK のどちらでも同じです。CMS SDK の semantic / hybrid では、説明付きのエントリが検索結果の _dictionary に返ります。詳しくは検索辞書のページを参照してください。
検索への反映タイミング
コンテンツの作成・更新・削除は、保存のあと非同期で検索インデックスに反映されます。
| 操作 | 反映先 | 目安 |
|---|---|---|
| コンテンツの作成・更新 | keyword | 数秒 |
| コンテンツの作成・更新 | semantic / hybrid | 数秒 |
| 公開 | Consumer SDK の検索 | 数秒 |
| 非公開・削除 | Consumer SDK の検索 | 即時 |
セマンティック検索の有効化、テーブルに初めて semanticSearch: true を付ける | semantic / hybrid | コンテンツ量に比例。完了まで 403 |
semanticSearch: true の列の追加・削除・順序変更(列が 1 つ以上残る場合) | semantic / hybrid | コンテンツ量に比例。完了まで既存の索引で検索を続けます |
enableDrafts の追加 | CMS SDK の semantic / hybrid | コンテンツ量に比例。下書き索引を全件作り、完了まで 403 |
enableDrafts の削除 | CMS SDK の semantic / hybrid | 即時。下書き索引は削除されます |
embeddingModel の変更 | semantic / hybrid | 本文の量に比例。完了まで既存の索引で検索を続けます |
セマンティック検索の無効化(enabled: false) | semantic / hybrid | 即時。索引は削除されます |
フラグの無い列を追加・削除しても作り直しは起きません。作り直しの間、列を変えたコンテンツの _semantic.chunks は切り替わるまで空になります。
CMS SDK の検索は下書きも対象にするため、コンテンツを非公開にしても CMS SDK の検索結果からは消えません。公開状態で結果を絞りたい場合は Consumer SDK の検索を使ってください。
索引の作り直しは、プロジェクトをまたいで 1 件ずつ順番に処理されます。作り直しの間は、他のテーブルや他のプロジェクトの semantic / hybrid への反映も完了まで待ちます。keyword への反映は影響を受けません。
進み具合は、開発者コンソールのテーブル画面にある検索インデックスのドキュメント数で確認できます。索引の名前は published_vector で、enableDrafts のテーブルには draft_vector もあります。CMS からは下の getStatus() で確認できます。
403 になる場合
semantic / hybrid の検索が 403 になるのは次の場合です。
- プロジェクトでセマンティック検索が無効
- テーブルに
semanticSearch: trueの列が無い。エラーはsearch index not found (no column has semanticSearch: true)です - 索引の初回構築中。セマンティック検索の有効化、テーブルに初めて
semanticSearch: trueを付けたとき、enableDraftsを付けたときの下書き索引(CMS SDK の検索)が該当します - システム側でセマンティック検索が無効で、索引が作られていない
- フォーム投稿(submissions)の検索
状態の取得と運用(CMS SDK)
索引の状態を取得する
semanticSearch.getStatus() で、プロジェクトのセマンティック検索の状態をまとめて取得できます。読み取り専用です。索引の再構築の要求と同期失敗の再投入は Developer API で行います。
const status = await cmsClient.semanticSearch.getStatus();
status.enabled; // プロジェクトで有効か
status.available; // semantic / hybrid を実行できるか(システム側の設定を含む)
status.paused; // 一時停止中か
status.pausedAt; // 停止に入れた時刻。停止していなければ null
for (const table of status.tables) {
for (const index of table.indexes) {
index.type; // "published_vector" | "draft_vector"
index.state; // "ready" | "building" | "failed" | "stale"
index.embeddingModel; // "standard" | "natural-language" | null(未構築)
index.rebuildPending; // 作り直し待ちか
index.documentCount; // 索引に入っているコンテンツ数。初回構築中は null
}
}
status.dictionary; // 辞書索引の状態
status.syncFailures; // 索引への反映に失敗して滞留しているジョブの件数と最新時刻state | 意味 |
|---|---|
ready | 構築済みで、semantic / hybrid を実行できます |
building | 初回構築中です。完了するまでそのテーブルの semantic / hybrid は 403 になります |
failed | 作り直しに失敗し、自動では再試行されません。定義を直して push するか、運用者に再構築を依頼してください |
stale | 一時停止中かシステム側の無効化中にコンテンツが書き込まれ、索引が古い可能性があります。再開か、システム側の再有効化後の最初の push で作り直されます |
tablesには呼び出したキーで読めるテーブルだけが載ります。semanticSearch: trueの列を持たないテーブルは載りません- 取得にはいずれかのテーブルへの読み取り権限が必要です。無いロールでは 403 になります
- 無効なプロジェクトでも 403 にはならず、
enabled: falseで返ります - 毎回計算するため、画面で定期的に取得する場合は間隔を 10 秒以上にしてください
- Consumer SDK にはありません
利用量を取得する
semanticSearch.getUsage() で、プロジェクトの月ごとの利用量を取得できます。値は埋め込みモデルに送った推定トークン数です。
const usage = await cmsClient.semanticSearch.getUsage();
for (const month of usage.months) {
month.month; // "2026-10"(UTC の年月)
for (const model of month.models) {
model.embeddingModel; // "standard" | "natural-language" | null
model.estimatedTokens; // その月にそのモデルへ送った推定トークン数
model.priceUsdPer1kTokens; // 1,000 トークンあたりの単価(USD)。不明なら null
}
}monthsには利用のあった月だけが古い順に並びます- 月の中はモデル別の内訳だけで、合計は返しません。合計と推定費用は CMS 側で計算してください
estimatedTokensはコンテンツの書き込み・索引の作り直し・辞書エントリ・検索クエリで送った本文から推定した値で、請求額とは一致しませんembeddingModelがnullの行は、今のサーバーに無いモデルで記録された分ですpriceUsdPer1kTokensは Amazon Bedrock の東京リージョンのオンデマンド単価です。検索クラスタ内で動くモデルは 0 です。円換算は CMS 側で行ってください- 取得にはいずれかのテーブルへの読み取り権限が必要です。無いロールでは 403 になります。値はプロジェクト全体で、読めるテーブルによって絞られません。無効なプロジェクトでも過去の利用量はそのまま返ります
- Consumer SDK にはありません
一時停止と再開
semanticSearch.pause() と resume() で、プロジェクトのセマンティック検索を一時停止・再開できます。利用量が想定を超えたときなど、埋め込みの費用を一時的に止めたいときに使います。
await cmsClient.semanticSearch.pause(); // 停止。戻り値は停止後の状態(getStatus() と同じ形)
await cmsClient.semanticSearch.resume(); // 再開停止中は次のようになります。
- コンテンツの作成・更新は
semantic/hybridの索引に反映されず、埋め込みの費用も掛かりません。keywordへの反映は止まりません semantic/hybridの検索はkeywordと同じ結果を 200 で返します。_semanticと_dictionaryは付かず、辞書の言い換えも使われません。索引が無い・構築中のテーブルは停止前と同じく 403 です- ベクトル索引は削除されず残ります。
getStatus()ではpaused: trueになり、各索引はstaleとrebuildPending: trueになります - スキーマを push しても索引は作り直されません。停止中に初めて
semanticSearch: trueを付けたテーブルやenableDraftsを付けたテーブルの索引も作られません。索引を削除する変更は通常どおり反映されます - 辞書エントリの登録・編集はできますが、検索結果への反映は再開後になります
resume() は停止を解き、停止中に変わった分だけを作り直します。対象は停止中に作成・更新・削除されたコンテンツ、停止中に変更した辞書エントリ、停止中の push で索引が必要になったテーブルです。停止中に push した定義の変更(embeddingModel の変更など)による作り直しは、push したときと同じ費用が掛かります。作り直しの間、既存の索引は停止前の内容で検索を受け続け、新しく作る索引は完成まで 403 です。進み具合は getStatus() で確認してください。
enabled: false での無効化との違いは次のとおりです。
| 操作 | 索引 | 再び使えるようにしたときの埋め込み | 停止中の semantic / hybrid |
|---|---|---|---|
一時停止(pause()) | 残る | 停止中に変わった分だけ | keyword と同じ結果を 200 で返す |
無効化(enabled: false) | 削除される | 既存コンテンツ全件 | 403 |
- 停止と再開には、
semanticSearch: trueの列を持つすべてのテーブルへの書き込み権限が必要です。無いロールでは 403 になります - 無効なプロジェクトの
pause()/resume()は 409 です。CMS SDK ではSemanticSearchNotEnabledErrorとして投げられ、isSemanticSearchNotEnabledError()で判別できます。停止中にenabled: falseを push すると停止も解けます - 既に停止中の
pause()と停止していないresume()はエラーにならず、現在の状態を返します - 停止中は Developer API の再構築要求(reindex)と同期失敗の再投入も 409 になります。再開してから要求してください
- Consumer SDK にはありません
セルフホスト環境での前提
Orizm をセルフホストしている場合、セマンティック検索を使うにはシステム側の準備が必要です。マネージドの Orizm(app.orizm.com)では利用者側の準備は不要です。
- OpenSearch: 2 系の公式ディストリビューションが必要です。使うプラグインは k-NN・ml-commons・neural-search の 3 つで、いずれも公式ディストリビューションに同梱されています。日本語の全文検索には kuromoji と ICU の解析プラグインも必要です
- 推論の設定: サーバーとワーカーの環境変数
ORIZM_EMBEDDING_SEARCHで埋め込みの推論方式を選びます
| 値 | 動作 |
|---|---|
off(既定) | セマンティック検索を無効にします。ベクトルインデックスは作られず、semantic / hybrid は使えません |
local | OpenSearch クラスタ内のローカルモデル(多言語 MiniLM・384 次元)で推論します。開発や小規模な検証向けです |
bedrock | Amazon Bedrock の埋め込みモデル(既定 amazon.titan-embed-text-v2:0・1024 次元)で推論します。本番向けです |
bedrockの追加設定:AWS_REGIONを設定します。標準モデルはBEDROCK_EMBEDDING_MODELとBEDROCK_EMBEDDING_DIMENSIONで変えられます。Orizm が対応していないモデルは起動時に拒否されます。Amazon OpenSearch Service では、Bedrock を呼び出す IAM ロールをBEDROCK_CONNECTOR_ROLE_ARNに指定します。未設定の場合はAWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYが使われます- 埋め込み基盤の準備: 埋め込みモデルの登録と取り込みパイプラインの作成はマイグレーションタスクが行います。サーバーを起動する前に実行してください。推論の設定を変えたときも同様です。準備ができていないとサーバーの起動ログに error が出て、
semantic/hybridは 403 になります - 索引のジョブは 1 件ずつ: ベクトル索引の同期と作り直しは、1 件ずつ順番に処理されます。処理するプロセスを複数起動しても並列には進みません
- 大量のコンテンツの初回同期:
bedrockでは Bedrock のモデルごとの処理量の上限に達することがあります。上限に達している間は同じモデルを使う検索も一時的に失敗しますが、作り直し自体は上限の回復を待って完走します offは非破壊: 一時的にoffにしても既存のベクトルインデックスは残り、埋め込みの生成だけが止まります。この間のsemantic/hybridはkeywordと同じ結果を返します。localまたはbedrockに戻して起動し直すと、古くなった索引は自動的に作り直されます
local と bedrock
では埋め込みモデルが違うため、スコアの値や順位は環境間で一致しません。品質の確認は本番と同じ推論の設定で行ってください。推論の設定や標準モデルを切り替えると、embeddingModel
を省略しているテーブルの索引はすべて作り直されます。テーブル定義の
embeddingModel は bedrock のときだけ意味を持ち、local
では名前を受け付けた上でローカルモデルで動きます。