ニュース & インサイト News & Insights

Liferay DXP (フル)セマンティック検索を使ってみた

2026.10.05

技術

はじめに

前回、DXP2026Q1から公開されたセマンティック検索を試してみたブログを書きましたが、
ただセマンティック検索を有効にしただけだとハイブリッドってことで、ただ並び順の変更をするだけでした。

なので、今回は実際にフルセマンティック検索(ほとんどの人がセマンティック検索と聞いてイメージしてるやつ)を設定して使ってみました。

セマンティック検索の仕組みを簡単に図にするとこんな感じです。

要はインデックスを作るときも検索するときにも元のテキストからベクトルを生成して、そのベクトル情報の近似値で検索を行うものです。

通常の全文検索もTokenizerでトークンを生成して、その一致性で検索するという意味では大枠の仕組みは変わらないです。
ただ、ベクトルの値で検索させるのでフルセマンティック検索の場合はトークンは使いません。

このベクトル情報を生成するのにText Embedding用のモデルを使います。
この部分が生成AIを利用する箇所になっています。

執筆時点のLiferay DXPの最新LTSバージョンの2026Q1.11だとリリース状態のText Embedding ProviderはOpenAIのみです。

β版だとHugging Faceとtxtaiも利用可能です。

本記事ではText Embedding ProviderにOpenAI、モデルにtext-embedding-3-largeを採用して使ってみました。

設定してみる

Text Embedding 設定

セマンティック検索の心臓部分でもあるText Embeddingを設定していきます。

インスタンス設定のプラットフォーム > 検索を選択します。

本記事での入力内容はこんな感じです。

  • テキスト埋め込みプロバイダーの設定: TRUE(チェック付ける)
  • テキスト埋め込みプロバイダー: OpenAI
  • API キー: (OpenAIのAPIトークン)
  • 寸法: (ブランクでOK)
  • モデル: text-embedding-3-large
  • ユーザー: (ブランクでOK)
  • 埋め込みベクトルの次元: 3072

この中の「埋め込みベクトルの次元」は利用するモデルが対応してる次元(Demention)の値を設定しましょう。

OpenAIのtext-embedding-3-largeモデルは3072次元なので3072としています。

一概には言えませんがこの次元の数が多い方が保持できるベクトル値の数が増えるので、精度が上がる傾向はあります。とはいえ、ベクトル値の生成精度はあくまでもモデルの性能に依存するので、次元が高ければいいって訳でもないです。

当然ながらインデックスのサイズも大きくなりますので、この辺も鑑みながら設定しましょう。

あと、保存する前に必ず設定のテストをして、モデルがちゃんと動作する状態になってるかを確認するようにしましょう。

ブループリントの要素作成

では、ベクトル情報で検索するようにブループリント要素を作成します。

まず、ブループリントページに移動したら要素タブに移動して新規作成ボタンをクリックします。

作成する要素の名前を入力します。ここではFull Semantic Searchとしています。

ここでセマンティック検索するように検索クエリを定義します。

{
  "description_i18n": {
    "ja_JP": "フルセマンティック検索"
  },
  "elementDefinition": {
    "category": "custom",
    "configuration": {
      "queryConfiguration": {
        "queryEntries": [
          {
            "clauses": [
              {
                "context": "query",
                "occur": "must",
                "query": {
                  "script_score": {
                    "script": {
                      "params": {
                        "queryVector": "${ml.text_embeddings.keywords_embedding}"
                      },
                      "source": "cosineSimilarity(params.queryVector, 'text_embedding_${ml.text_embeddings.vector_dimensions}_${context.language_id}') + 1.0"
                    },
                    "min_score": 1.5,
                    "query": {
                      "exists": {
                        "field": "text_embedding_${ml.text_embeddings.vector_dimensions}_${context.language_id}"
                      }
                    }
                  }
                }
              }
            ]
          }
        ]
      }
    }
  },
  "title_i18n": {
    "ja_JP": "Full Semantic Search"
  }
}

ブループリントで利用できる変数はここで定義されています。

が、残念ながらセマンティック検索用の変数(ml.text_embeddings.〇〇とか)は定義されていません。。。

Liferayのセマンティック検索のページに記載されてるJSONには登場してることから、きっと以下のようになってる模様です。

  • ${ml.text_embeddings.vector_dimensions}: Text Embeddingのベクトル次元数
  • ${ml.text_embeddings.keywords_embedding}: 検索キーワードから生成したEmbeddingベクトル

なので、「text_embedding_${ml.text_embeddings.vector_dimensions}_${context.language_id}」というフィールドは今回の例だと「text_embedding_3072_ja_JP」という風になります。

また、JSONにmin_scoreの式を入れてますが、ここでベクトルの近さのスコア(に1プラスした値)を基にヒット判定する閾値を指定しています。ここでは1.5にしてますが、適宜調整してください。

ちなみに、なんで+1してるかというとElasticSearchのscript_scoreがマイナスの値を期待していないからです。

というのも、cosineSimilarityの結果とりうる値は理論上-1.0 ~ +1.0になります。

なので+1しとくことでマイナスにならないようにしています。

ブループリントの設定

では、実際にブループリントを設定していきましょう。

ブループリントタブに戻って、新規作成ブループリントボタンをクリックします。

作成するブループリントの名前を入力します。ここではFull Semantic Searchとしています。
(命名センスが・・・)

クエリ要素の追加で右のパレットから先ほど作成したFull Semantic Search要素を追加します。
これだけだとフルセマンティック検索にならないので、

通常の全文検索をネグレクトするので検索フレームワークのインデクサー条件をOFFにします。
そして、検索フレームワークのクエリコントリビューターをすべて無効にするにします。

これでベクトル値だけで検索する設定になります。

ちなみにブループリントにはプレビュー機能があります。

これを使うことで設定を微調整しながら動作を確認できます。
要素で定義したJSONもブループリント内で編集できるので、min_scoreとかはここで動作確認しながら調整するといいです。

動作させてみる

テスト用の検索ページ作る

デフォルトの検索ページにはブループリントの設定ができないので、テスト用に新しく作ります。
(グローバルで検索ページを編集してもいいけど他のサイトにも影響出るので新規ページにしとく)

適当なサイトでナビゲーションの+アイコンからページを追加するをクリックします。
(もちろんサイトビルダーから作ってもいいです)

コンテンツページでページを作成してもいいんですが、今回はあくまでもサクッと検証するだけなのでウィジェットページを選択します。

作成するブループリントの名前を入力します。ここではSemantic Searchとしています。
(命名センスが・・・・・・・・・)

レイアウトは別に何でもいいのでデフォルトのまま作成します。

検証用ウィジェットの配置

画面右上の+アイコンから追加するウィジェットのリストを表示します。

今回検証用に配置したのは以下のウィジェットです。

  • 検索バー: 検索キーワード入力フォームです
  • ブループリントオプション: これで作ったブループリントを適用してセマンティック検索します
  • コマース検索結果: 検索結果を表示するウィジェットです

早速、ブループリントオプションを設定していきます。

設定画面のブループリントの 選択 ボタンをクリックします。

さっき作成したブループリントを選択します。

こんな感じで設定されてるブループリントが確認できます。
(管理者にしか見えないのでご安心を)

検証用のコンテンツを作成

今回検証で作成したコンテンツはこんな感じです。
全てWebコンテンツで作成しています。

  • コンテンツ1
    • タイトル:海外出張時の宿泊費について
    • 内容:
      海外への業務出張に伴う宿泊については、渡航先の地域区分に応じて会社が定める上限額まで実費を精算できます。
      北米および欧州地域では、1泊あたり25,000円を上限とします。アジア地域では1泊あたり18,000円、その他の地域では1泊あたり20,000円を上限とします。
      ホテルを予約する際は、原則として会社指定の出張手配サービスを利用してください。現地での展示会開催などにより規定額以内の宿泊施設を確保できない場合は、事前に所属部門長の承認を得ることで上限を超える宿泊施設を利用できます。
      宿泊後は領収書または宿泊を証明できる電子明細を経費精算システムに添付してください。
  • コンテンツ2
    • タイトル:退職時に返却する会社貸与品
    • 内容:
      従業員が退職する場合、最終出社日までに会社から貸与されている物品を所属部署へ返却してください。
      返却対象には、ノートPC、スマートフォン、社員証、入館カード、セキュリティトークン、モバイルWi-Fiルーターなどが含まれます。
      ノートPCおよびスマートフォンについては、個人で保存したデータを削除した上で情報システム部門へ返却してください。ただし、業務上必要なファイルについては事前に上司または後任者へ引き継いでください。
      社員証および入館カードを紛失している場合は、最終出社日までに総務部へ連絡し、所定の紛失届を提出する必要があります。
  • コンテンツ3
    • タイトル:オフィス内カフェテリアの利用案内
    • 内容:
      本社ビル3階には、従業員が利用できるカフェテリアがあります。
      営業時間は平日の11時00分から14時30分までです。土曜日、日曜日、祝日および会社指定の休日は営業していません。
      日替わりメニュー、麺類、サラダ、軽食などを提供しています。料金は社員証によるキャッシュレス決済または交通系電子マネーで支払うことができます。
      混雑緩和のため、12時00分から12時30分までは製造部門、12時30分から13時00分までは管理部門の優先利用時間としています。
      メニューとアレルギー情報は社内ポータルから確認できます。
  • コンテンツ4
    • タイトル:高額案件における承認手続き
    • 内容:
      設備購入、外部委託、システム開発などの支出を伴う案件については、契約金額に応じた社内承認が必要です。
      100万円未満の案件は課長、100万円以上500万円未満の案件は部長による承認を受けてください。
      500万円以上1,000万円未満の場合は本部長の承認が必要です。1,000万円以上の案件については担当役員による承認を受けた後、調達部門へ発注を依頼してください。
      複数の契約に分割することで承認基準を回避することは禁止されています。同一目的の発注については、原則として合計金額を基準として承認者を判断します。
  • コンテンツ5
    • タイトル:在宅勤務時の情報セキュリティ
    • 内容:
      自宅やサテライトオフィスなど、会社の事業所外で業務を行う場合は、会社が支給した端末を使用してください。
      社内システムへ接続する際は、会社指定のVPNを利用し、多要素認証を行う必要があります。
      カフェ、空港、ホテルなどで提供されている公衆無線LANへ会社端末を直接接続することは禁止しています。外出先から通信する必要がある場合は、会社支給のモバイル通信機器または会社が許可したテザリング手段を利用してください。
      機密情報を含む資料を自宅で印刷することは原則禁止です。また、オンライン会議を行う場合は、家族や第三者に業務上の会話が聞こえない環境を確保してください。

検索の検証

では、張り切ってセマンティックな検索を試してみます。

おおっ!

検索キーワードに「辞めるときパソコンはどうすればいい?」と入力して検索してみました。

作成した2番目のコンテンツがヒットしていますが、元のコンテンツには「退職」とは書かれてますけど、「辞めるとき」という記述はありまっせん。

「ノートPC]という記述はありますけど「パソコン」という記述もありません。

それぞれText Embeddingで意味合いが近いベクトルが生成されてヒットしたと考えられます。

一応他のパターンも・・・

今度は「会社で昼ご飯を食べられる場所は?」というキーワードで検索しています。

作成したコンテンツの3番目がヒットしましたが、やはりここでも全文検索だとヒットしないけど意味としては期待通りのコンテンツがヒットしています。

おまけ

ブループリント画面のプレビューはページで公開する前に自分で作ったブループリントのクエリがどう作用するかを検証できるのでとても有用です。

下図は元々のセマンティック検索用のJSONから”min_score”のフィルタ条件を除去してプレビューを実行したものです。

「会社で昼ご飯を食べられる場所は?」のプレビュー検索結果とその時のスコアは以下のようになっています。

  • オフィス内カフェテリアの利用案内: 1.56
  • 海外出張時の宿泊費について: 1.29
  • 退職時に返却する会社貸与品: 1.27
  • 高額案件における承認手続き: 1.23

このスコアを見ながら、検索結果に表示させる対象とするかどうかをmin_scoreのフィルタ条件に反映してもらうといいと思います。

まとめ

いかがでしたでしょうか。

このようにブループリントを使うことで、完全なセマンティック検索が実現できました。

自然言語で検索できるのでこれまでのように含まれてるキーワードはなんだったっけ?とか気にせず、なんとなく入力すれば内容から生成したベクトルで(意味が)近いコンテンツを検索できるようになります。

とはいえ、冒頭でも書いた通り毎回検索するときにText Embeddingで生成AIのAPIをコールする必要があります。

特に今回の検証で使ったOpenAIなど外部NWにあるLLMにアクセスするとなるとなかなか負荷が高そうに思います。

感覚的に同時接続が高い大規模なシステムでは採用は難しいように思います。

一応、いまはβ版のHugging FaceやtxtaiなどをローカルNW上にデプロイして利用することができれば解消できるかもしれません。
(一応Hugging Face使ってみましたがモデル側が期待してる形式とLiferayが送るpayloadがあってないのか精度はイマイチでした・・・)

そしてセマンティック検索は自然言語で検索できるけど結局は検索だけなので、
ヒットしたコンテンツを自分で確認しないといけないんですよね。

そこを踏まえると前回書いたArticul8との連携記事みたいに要約までしてくれる方がいいようにも思います。

OxygenDesignではArticul8の導入も支援しており、お客様の環境に合わせた生成AIとの連携についてもご相談に乗っています。

ご興味があればぜひお問い合わせください!

まつもとでした!