類似結果ウィジェットにカスタムコンテンツを追加する
購読者 サポート対象外
このチュートリアルで取り上げている SimilarResultsContributor SPI は Liferay DXP 2024.Q2 で内部化され、 release.dxp.api からは利用できなくなりました。
この機能は、Liferay DXP 7.3以降に同梱されているサービスプロバイダーインターフェース(SPI)に依存しています。 Liferay DXP 7.2 の Fix Pack 5 以降では、 Liferay Marketplace から Similar Results ウィジェットをインストールすることで利用できます。
類似結果ウィジェット にアプリケーションのカスタムコンテンツを表示するには、 類似結果コントリビューターを実装します。 なお、コントリビューターが正しく機能するためには、類似結果ウィジェットがページ上のメインアセットとしてあなたのコンテンツを検出できる必要があります。 つまり、サポートされているLiferay DXPアセット(ブログ記事やWikiページなど)と同様に、「表示ウィジェット」内でURL経由で表示できる必要があるということです。 類似結果ウィジェットは、カスタムコントリビューターを必要とせずに、Lifery DXPのアセットパブリッシャーに表示されるあらゆるコンテンツで使用できることに注意してください。

ナレッジベースアプリケーションは、KB記事用の SimilarResultsContributor を標準で実装していないため、この例ではそれを実装します。 簡略化のため、ここではアプリケーションのルートフォルダにあるナレッジベース記事のみを扱います。
ナレッジベース記事用のSimilarResultsContributorをデプロイします。
Liferay DXP の新しいインスタンスを起動するには、以下を実行します。
docker run -it -m 8g -p 8080:8080 liferay/dxp:2026.q1.9-lts
http://localhost:8080 に、メールアドレス test@liferay.com とパスワード test を使用して Liferay にサインインします。 プロンプトが表示されたら、パスワードを learn に変更してください。
次に、以下の手順に従って、Liferay DXP インスタンスでサンプル SimilarResultsContributor を起動して実行します。
-
Acme Similar Results Contributor をダウンロードして解凍します。
curl https://resources.learn.liferay.com/examples/liferay-r1s1.zip -Ounzip liferay-r1s1.zip -
モジュールのルートディレクトリから、ビルドとデプロイを実行します。
./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)注記このコマンドは、デプロイされたjarファイルをDockerコンテナの/opt/liferay/osgi/modulesにコピーするのと同じです。
-
Liferay Dockerコンテナコンソールでデプロイメントを確認してください。
com.acme.r1s1.impl_1.0.0 [1009] を開始しました -
サンプル貢献者が正しく動作していることを確認してください。 まず、ブラウザで
https://localhost:8080を開いてください。 -
サイトメニュー → コンテンツ → ナレッジベース にナレッジベース記事を追加してください。
「タイトル」と「内容」の各フィールドが類似していることを確認してください。 これらの文字列を使用して、3つの記事を作成できます(タイトルと本文には同じ文字列を使用してください)。
テストKB記事1
テストKB記事2
テストKB記事3
-
ページにナレッジベース表示ウィジェットを追加し、続いて類似検索結果ウィジェットを追加します。
-
類似結果ウィジェットのウィジェット設定を開き、以下の設定に 1 の値を必ず設定してください。
最小用語頻度: 1 最小文書頻度: 1
-
表示するメインアセットとして選択するには、ナレッジベース記事のいずれかをクリックしてください。
「類似検索結果」ウィジェットに、関連する他のナレッジベース記事が表示されるようになりました。

例が正しく動作することを確認したので、その仕組みを学びましょう。
SimilarResultsContributor を調べてください
デプロイ済みのサンプルを確認してください。 これには、類似検索結果ウィジェットのカスタムコンテンツを有効にするコントリビューターというクラスが1つだけ含まれています。
OSGi登録のためのコントリビュータークラスに注釈を付ける
R1S1SimilarResultsContributor は SimilarResultsContributor インターフェースを実装します。
@Component(service = SimilarResultsContributor.class)
public class R1S1SimilarResultsContributor implements SimilarResultsContributor {
サービス コンポーネント プロパティは、実装を SimilarResultsContributor サービスとして登録します。
SimilarResultsContributor インターフェースを確認してください。
インターフェースの3つのメソッドを実装してください。
public void detectRoute(RouteBuilder、routeBuilder、RouteHelper、routeHelper);
detectRoute を実装して、エンティティの URL パターンの固有の部分を提供し、類似結果ウィジェットがコントリビューターを呼び出す必要があるかどうかを検出できるようにします。 URLパターンは、 RouteBuilder オブジェクトの属性として追加されます。 RouteHelper は、解析のために URL 文字列全体を取得するのに便利です。
各表示ウィジェットに対してサポートされている SimilarResultsContributor は 1 つのみです。
public void resolveCriteria(
CriteriaBuilder criteriaBuilder, CriteriaHelper criteriaHelper);
resolveCriteria を実装して、ページ上のメインエンティティを使用して対応する検索エンジンドキュメントを検索します。 検出されたルートから、あなたの貢献者が適切な人物であると判断された場合に、この処理が実行されます。
public void writeDestination(
DestinationBuilder destinationBuilder,
DestinationHelper destinationHelper);
類似結果ウィジェット内のリンクをユーザーがクリックしたときにメインアセットを更新するには、 writeDestination を実装します。
類似結果貢献者を完了する
detectRoute メソッドを実装します。
@Override
public void detectRoute(
RouteBuilder routeBuilder, RouteHelper routeHelper) {
String[] pathParts = StringUtil.split(
_http.getPath(routeHelper.getURLString()),
Portal.FRIENDLY_URL_SEPARATOR);
String[] parameters = StringUtil.split(
pathParts[pathParts.length - 1], CharPool.FORWARD_SLASH);
if (!parameters[0].matches("knowledge_base")) {
throw new RuntimeException(
"ナレッジベース記事が検出されませんでした");
}
RouteBuilder.addAttribute("urlTitle", パラメータ[1]);
}
detectRoute を実装して、エンティティの URL パターンの特定の部分をチェックするロジックを挿入します。 類似結果ウィジェットは、このチェックを使用して正しい SimilarResultsContributor を見つけます。 エンティティの表示URLが検出された場合は、後で使用するために、URLルートに少なくとも1つの属性を追加してください。 ここでは、フレンドリー URL に "knowledge_base" があるかどうかを確認し、検出された場合は、メソッド シグネチャで渡された RouteBuilder に "urlTitle" を属性として追加します。
routeHelper.getUrlString の呼び出しは、仮想インスタンス内で検出されたアセットの相対 URL を取得するために使用できるため重要です。 例えば、
/web/guest/page-title/-/knowledge_base/kb-article-url-title
RouteBuilder に属性として追加された ID は、 resolveCriteria メソッドでエンティティと対応する検索エンジンのドキュメントを取得するために使用されます。
resolveCriteria メソッドを実装します。
@Override
public void resolveCriteria(
CriteriaBuilder criteriaBuilder, CriteriaHelper criteriaHelper) {
String urlTitle = (String)criteriaHelper.getRouteParameter("urlTitle");
KBArticle kbArticle = _kbArticleLocalService.fetchKBArticleByUrlTitle(
criteriaHelper.getGroupId(),
KBFolderConstants.DEFAULT_PARENT_FOLDER_ID, urlTitle);
if (kbArticle == null) {
return;
}
AssetEntry assetEntry = _assetEntryLocalService.fetchEntry(
criteriaHelper.getGroupId(), kbArticle.getUuid());
if (assetEntry == null) {
return;
}
String uidField = String.valueOf(kbArticle.getPrimaryKeyObj());
if (ReleaseInfo.getBuildNumber() ==
ReleaseInfo.RELEASE_7_2_10_BUILD_NUMBER) {
uidField = String.valueOf(kbArticle.getResourcePrimKey());
}
criteriaBuilder.uid(Field.getUID(assetEntry.getClassName(), uidField));
}
ページに表示されているエンティティに対応する検索エンジンのドキュメントを検索してください。 criteriaBuilder.uid メソッドには、適切な検索エンジンのドキュメントの uid フィールドの値を指定する必要があります (これは通常、ドキュメント内の Elasticsearch 指定の _id フィールドと同じです)。 Liferay DXPインデックスでは、このフィールドはエントリクラス名とクラスの主キーの組み合わせです。 両方を文字列として Field.getUID に渡して値を取得します。 この例では、 detectRoute メソッド ( urlTitle) で属性に追加した ID を使用してモデル エンティティを取得し、それを使用してアセット エントリを取得します。
Liferay DXP 7.2とLiferay DXP 7.3には違いがあるため、それぞれのバージョンに対応したロジックを含む、バージョンチェックの条件をここに示します。 Liferay DXP 7.3 では、 getPrimaryKeyObj がクラス名と組み合わせて使用されますが、Liferay DXP 7.2 では、 getResourcePrimKey が必要です。
一致する文書が見つかったら、類似の結果が更新されるように宛先URLを記述します。
writeDestination メソッドを実装します。
@Override
public void writeDestination(
DestinationBuilder destinationBuilder,
DestinationHelper destinationHelper) {
String urlTitle = (String)destinationHelper.getRouteParameter(
"urlTitle");
AssetRenderer<?> assetRenderer = destinationHelper.getAssetRenderer();
KBArticle kbArticle = (KBArticle)assetRenderer.getAssetObject();
destinationBuilder.replace(urlTitle, kbArticle.getUrlTitle());
}
類似結果ウィジェットのリンクをユーザーがクリックしたときにメインアセットを更新するには、 writeDestination を実装します。 「類似商品」クエリが検索エンジンに再送信され、類似検索結果リストが新しいメインアセットに合わせて再レンダリングされます。 KB記事の場合、作業全体は、元のURL(メインアセット用)の urlTitle を、一致したエンティティの urlTitle に置き換えることです。
destinationHelper.getRouteParameter の呼び出しは重要です。 DestinationHelper のメソッドの中で唯一事前検索演算子であるため、メインアセットまたは類似結果リンクを再レンダリングする前に、常に現在選択されているメインアセットからデータを返します。 DestinationHelper メソッドの残りの部分(ここに示されているもう 1 つのメソッド、 getAssetRendererを含む)は、一致したアセットのデータを返します。 このメソッドは、一致した結果ごとに繰り返し実行されます。
サービスの依存関係を宣言する
このコードは、OSGi コンテナにデプロイされたサービス AssetEntryLocalService、 KBArticleLocalService、および Http に依存しています。 宣言サービス @Reference アノテーションを使用して、それらが必要であることを宣言します。このアノテーションは org.osgi.service.component.annotations.Reference によって提供されます。 それらを私有地に植えなさい。
@Reference
private AssetEntryLocalService _assetEntryLocalService;
@Reference
private Http _http;
@Reference
private KBArticleLocalService _kbArticleLocalService;
追加情報
エンティティの URL の実装はそれぞれ大きく異なる可能性があるため、独自のアプリケーションのコントリビューターを作成する際にさらにインスピレーションが必要な場合は、GitHub の SimilarResultsContributor インターフェース とバンドルされている 実装 を参照してください。
アプリケーションのカスタムコンテンツを類似検索結果ウィジェットに追加する際に必要な作業の大部分は、表示URLの操作です。 Liferay のアセットがどのように表示 URL を作成するかを知るには、エンティティの *AssetRenderer クラスの getURLView メソッドを調べます。
JournalArticleAssetRenderer#getURLView、Liferay DXP 7.3.2 GA3WikiPageAssetRenderer#getURLView、Liferay DXP 7.3.2 GA3BlogsEntryAssetRenderer#getURLView、Liferay DXP 7.3.2 GA3DLFileEntryAssetRenderer#getURLView、Liferay DXP 7.3.2 GA3
前述のとおり、この例では、アプリケーションのルートフォルダにある KB 記事と連携する SimilarResultsModelDocumentContributor を作成する方法を示します。 KBフォルダのサポートを追加することは可能であり、意欲的な読者にとっては興味深い課題となるでしょう。 インスピレーションを得るために、 DocumentLibrarySimilarResultsContributor のソースコードを参照してください。
トラブルシューティング:アセットUIDアーキテクチャ
uid は、Liferay DXP 7.3 以降、標準的な方法で構築されます。 com.liferay.portal.search.internal.model.uid.UIDFactoryImpl クラスは、Liferay のインデックス アーキテクチャによって制御されるすべてのドキュメントの uid を設定する役割を担っています。 現在は標準化されているため、推測する必要はありません。
同様に、バージョン 7.2 および 7.1 では、エンティティが Composite Indexer API でインデックス付けされている場合 (つまり、 ModelDocumentContributor クラスを持っている場合)、 uid は Liferay の実装によって設定され、標準化されます。
ただし、従来のIndexer APIでインデックス登録されているエンティティ(つまり、LiferayのBaseIndexerを継承した*Indexerクラスを持ち)については、uidを設定するロジックがオーバーライドされている可能性があるため、そのエンティティのインデックス登録の実装を確認してみる価値があります。