Analytics 用のサーバー側ページネーミングの実装 implementing-server-side-page-naming-for-analytics
Adobe Analytics は、s.pageName プロパティを使用してページを一意に識別し、そのページのために収集されたデータを関連付けます。AEM から Analytics に送信されるこのプロパティに値を割り当てるには、通常は AEM 内で次のタスクを実行します。
-
Analytics クラウドサービスフレームワークを使用して、CQ 変数を Analytics の
s.pageNameプロパティにマップする(コンポーネントデータと Adobe Analytics プロパティとのマッピングを参照)。 -
ページコンポーネントを、
s.pageNameプロパティにマップする CQ 変数を含むようにデザインする(カスタムコンポーネント用の Adobe Analytics トラッキング機能の実装を参照)。
Analytics レポートデータをサイトコンソールとコンテンツインサイトに公開するには、各ページの s.pageName プロパティの値が必要です。Sites コンソールとコンテンツインサイトに s.pageName プロパティの値を指定するために実装した AnalyticsPageNameProvider インターフェイスを、AEM Analytics の Java API で定義します。AnaltyicsPageNameProvider サービスは、レポート生成のためにサーバー上の pageName プロパティを解決します。このプロパティは、追跡のためにクライアント上で JavaScript を使用して動的に設定できるからです。
デフォルトの Analytics ページ名プロバイダーサービス the-default-analytics-page-name-provider-service
これは、ページの Analytics データを取得するために使用される s.pageName プロパティの値を決定するデフォルトの DefaultPageNameProvider サービスです。このサービスは、AEM の基盤ページコンポーネント(/libs/foundation/components/page)と連携して動作します。このページコンポーネントは、s.pageName プロパティにマップされる次の CQ 変数を定義します。
pagedata.path:ページのパスに設定されます。pagedata.title:ページのタイトルに設定されます。pagedata.navTitle:ページのナビゲーションのタイトルに設定されます。
DefaultPageNameProvider サービスは、Analytics クラウドサービスフレームワークの s.pageName プロパティにマッピングされる CQ 変数を決定します。その後、Analytics レポートデータの取得に使用する適切なページプロパティを決定します。
-
pagedata.path:このサービスはpage.getPath()を使用します -
pagedata.title:このサービスはpage.getTitle()を使用します -
pagedata.navTitle:このサービスはpage.getNavigationTitle()を使用します
page オブジェクトは、そのページの com.day.cq.wcm.api.Page Java オブジェクトです。
CQ 変数をフレームワークの s.pageName プロパティにマッピングしない場合、s.pageName の値はページのパスから生成されます。例えば、/content/geometrixx/en というパスを持つページでは、s.pageName に値 content:geometrixx:en を使用します。
Analytics レポートにおける連続性の維持 maintaining-continuity-in-analytics-reporting
ページの分析データに関するすべての履歴を維持するには、一度も変更されたことがないページに使用される s.pageName プロパティの値が必要です。ただし、基盤ページコンポーネントが定義する分析プロパティは簡単に変更できます。例えば、ページを移動すると pagedata.path の値が変更され、レポート履歴の連続性が途切れて、次のようなことが起こります。
- 前のパスで収集されたデータは、このページと関連付けられなくなります。
- 以前に他のページが使用していたパスを別のページが使用する場合は、後から使用するほうのページがそのパスのデータを継承します。
レポートの連続性を保証するには、s.pageName の値に以下の性質を持たせる必要があります。
- 一意性。
- 安定性。
- 人間にとっての可読性。
例えば、カスタムページコンポーネントに、作成者がページの一意の ID を指定するために使用するページプロパティ(s.pageProperties プロパティの値として使用されるもの)を含めることができます。
- ページに含まれる Analytics 変数は、ページプロパティに保存されている一意の ID の値に設定されます。
- Analytics 変数は、Analytics フレームワークの
s.pagePropertiesプロパティにマッピングされます。 - AnalytcsPageNameProvider インターフェイスの実装は、このページプロパティの値を取得して、ページの Analytics データのクエリに使用します。
s.pageName の値について効果的な戦略を策定できるよう、Analytics のコンサルタントに相談してください。Analytics ページ名プロバイダーサービスの実装 implementing-an-analytics-page-name-provider-service
com.day.cq.analytics.sitecatalyst.AnalyticsPageNameProvider インターフェイスを OSGi サービスとして実装し、s.pageName プロパティの値を取得するロジックをカスタマイズします。サイトページ分析およびコンテンツインサイトでこのサービスを使用して、Analytics からレポートデータを取得します。
AnalyticsPageNameProvider インターフェイスで定義されている次の 2 つのメソッドを実装する必要があります。
-
getPageName:s.pageNameプロパティとして使用する値を表すString値を返します。 -
getResource:s.pageNameプロパティに関連付けられたページを表すorg.apache.sling.api.resource.Resourceオブジェクトを返します。
どちらのメソッドも、com.day.cq.analytics.sitecatalyst.AnalyticsPageNameContext オブジェクトをパラメーターとして取ります。AnalyticsPageNameContext クラスは、Analytics 呼び出しのコンテキストに関する以下の情報を提供します。
- ページリソースのベースパス。
- Analytics クラウドサービス設定の
Frameworkオブジェクト。 - ページの
Resourceオブジェクト。 - ページの
ResourceResolverオブジェクト。
このクラスは、ページ名の setter も提供します。
サンプル AnalyticsPageNameProvider 実装 example-analyticspagenameprovider-implementation
以下に示すサンプル AnalyticsPageNameProvider 実装は、以下のようなカスタムページコンポーネントをサポートしています。
- 基盤ページコンポーネントを拡張したコンポーネントです。
- ダイアログボックスには、作成者が
s.pageNameプロパティの値を指定するためのフィールドが含まれます。 - プロパティの値は、ページインスタンスの
jcr:contentノードの pageName プロパティに保存されます。 s.pageNameプロパティを保存する Analytics プロパティは、pagedata.pagenameです。このプロパティは、Analytics フレームワークのs.pageNameプロパティにマッピングされます。
以下に示す getPageName メソッドの実装は、フレームワークのマッピングが正しく設定されていれば、pageName ノードのプロパティの値を返します。
public String getPageName(AnalyticsPageNameContext context) {
String pageName = null;
Framework framework = context.getFramework();
Resource resource = context.getResource();
if (resource != null && framework != null && framework.mapsSCVariable(S_PAGE_NAME)) {
String cqVar = framework.getMapping(S_PAGE_NAME);
Page page = resource.adaptTo(Page.class);
if (cqVar.equals("pagedata.pagename")) {
pageName = page.getProperties().get("pageName",null);
}
}
return pageName;
}
以下に示す getResource メソッドの実装は、ページの Resource オブジェクトを返します。
public Resource getResource(AnalyticsPageNameContext context) {
Resource res = null;
Framework framework = context.getFramework();
ResourceResolver resolver = context.getResourceResolver();
String pageName = context.getPageName();
String basePath = context.getBasePath();
if (pageName != null && basePath != null && resolver != null
&& framework != null && framework.mapsSCVariable(S_PAGE_NAME)) {
String cqVar = framework.getMapping(S_PAGE_NAME);
if (cqVar.equals("pagedata.pagename")) {
Iterator<Resource>
hits = resolver.findResources(createQuery(pageName, basePath, "pagename"), Query.JCR_SQL2);
if (hits.hasNext()) {
res = hits.next();
res = res.getParent();
}
}
}
return res;
}
private String createQuery(String pageName, String basePath, String propName) {
return "SELECT * FROM [cq:PageContent] WHERE ISDESCENDANTNODE(["
+ basePath + "]) and [" + propName + "] = \"" + pageName + "\"";
}
以下のコードは、サービスを設定する SCR アノテーションを含む、クラス全体を表します。デフォルトのサービスをオーバーライドするサービスランキングは 200 です。
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2019 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.day.cq.analytics.sitecatalyst;
import java.util.Iterator;
import javax.jcr.query.Query;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import com.day.cq.analytics.sitecatalyst.AnalyticsPageNameContext;
import com.day.cq.analytics.sitecatalyst.AnalyticsPageNameProvider;
import com.day.cq.analytics.sitecatalyst.Framework;
import com.day.cq.wcm.api.Page;
import static com.day.cq.analytics.sitecatalyst.AnalyticsPageNameContext.S_PAGE_NAME;
/**
* Default implementation of {@link AnalyticsPageNameProvider} that resolves
* page title, path or navTitle if mapped in {@link Framework}.
*/
@Component(
service = { AnalyticsPageNameProvider.class },
property = {
Constants.SERVICE_DESCRIPTION + "=Example Page Name Resolver implementation",
Constants.SERVICE_RANKING + ":Integer=200"
}
)
public class ExamplePageNameProvider implements AnalyticsPageNameProvider {
public String getPageName(AnalyticsPageNameContext context) {
String pageName = null;
Framework framework = context.getFramework();
Resource resource = context.getResource();
if (resource != null && framework != null && framework.mapsSCVariable(S_PAGE_NAME)) {
String cqVar = framework.getMapping(S_PAGE_NAME);
Page page = resource.adaptTo(Page.class);
if (cqVar.equals("pagedata.path")) {
pageName = page.getProperties().get("pageName",null);
}
}
return pageName;
}
public Resource getResource(AnalyticsPageNameContext context) {
Resource res = null;
Framework framework = context.getFramework();
ResourceResolver resolver = context.getResourceResolver();
String pageName = context.getPageName();
String basePath = context.getBasePath();
if (pageName != null && basePath != null && resolver != null
&& framework != null && framework.mapsSCVariable(S_PAGE_NAME)) {
String cqVar = framework.getMapping(S_PAGE_NAME);
if (cqVar.equals("pagedata.pagename")) {
Iterator<Resource>
hits = resolver.findResources(createQuery(pageName, basePath, "pagename"), Query.JCR_SQL2);
if (hits.hasNext()) {
res = hits.next();
res = res.getParent();
}
}
}
return res;
}
private String createQuery(String pageName, String basePath, String propName) {
return "SELECT * FROM [cq:PageContent] WHERE ISDESCENDANTNODE(["
+ basePath + "]) and [" + propName + "] = \"" + pageName + "\"";
}
}