web-vitals.js、Google アナリティクス、BigQuery を使用してパフォーマンスを測定する

1. 始める前に

演習内容

この Codelab で行う内容は次のとおりです。

  • Google アナリティクス 4 プロパティを BigQuery にリンクします。
  • web-vitals ライブラリをウェブページに追加します。
  • web-vitals データを準備して Google アナリティクスに送信します。
  • BigQuery でウェブに関する主な指標のデータをクエリします。
  • Google データポータルでダッシュボードを作成し、ウェブに関する主な指標のデータを可視化します。

必要なもの

  • GA4 プロパティを持つ Google アナリティクス アカウント。
  • Google Cloud アカウント
  • Chromium ベースのウェブブラウザ(Google Chrome、Microsoft Edge など)(Chromium ベースのウェブブラウザが必要な理由について詳しくは、Browser Support をご覧ください)。
  • 任意のテキスト エディタ(Sublime Text、Visual Studio Code など)。
  • web-vitals ライブラリの動作を確認するためにテストページをホストする場所(ローカル サーバーを使用して静的なウェブページを配信するか、GitHub でテストページをホストします)。
  • 分析コードをデプロイできる公開サイト(コードを本番環境に移すと、この Codelab の最後にある BigQuery とデータポータルの例を理解しやすくなります)。
  • HTML、CSS、JavaScript、Chrome DevTools に関する知識。

準備

まず、コードを公開したらすぐにパフォーマンスを分析できるように、Google アナリティクス 4 を BigQuery にリンクします。

Google アナリティクス ヘルプセンターの手順に沿って、GA4 プロパティを BigQuery にリンクします。

Google アナリティクス プロパティでイベントデータを BigQuery にエクスポートする準備が整ったので、web-vitals ライブラリをサイトに統合します。

2. web-vitals ライブラリと gtag をウェブページに追加する

まず、web-vitals ライブラリをウェブページに追加します。

  1. web-vitals ライブラリを追加するページ テンプレートを開きます。この例ではシンプルなページを使用します。

basic.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Web Vitals Test</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <p><img style="max-width: 360px" src="https://placekitten.com/g/3840/2160" alt="Kitten" /></p>
  <p>Text below image</p>
</body>
</html>
  1. テキスト エディタで、空のファイルにソースコードを貼り付けます。
  2. ファイルを basic.html としてローカルに保存します。
  3. このモジュール スクリプトをコピーし、終了タグ </body> の直前に貼り付けます。このスクリプトは、コンテンツ配信ネットワークから web-vitals ライブラリを読み込みます。

basic.html

<script type="module">
  import {getCLS, getFID, getLCP} from 'https://unpkg.com/web-vitals?module';

  getCLS(console.log);
  getFID(console.log);
  getLCP(console.log);
</script>

変更後のコードは次のようになります。

basic.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Web Vitals Test</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <p><img style="max-width: 360px" src="https://placekitten.com/g/3840/2160" alt="Kitten" /></p>
  <p>Text below image</p>

<script type="module">
  import {getCLS, getFID, getLCP} from 'https://unpkg.com/web-vitals?module';

  getCLS(console.log);
  getFID(console.log);
  getLCP(console.log);
</script>
</body>
</html>
  1. ファイルを保存します。

web-vitals ライブラリをウェブページに追加しました。

3. ウェブページのウェブに関する主な指標を測定する

ウェブに関する主な指標は、Chrome で、または Chromium ブラウザの web-vitals ライブラリで取得される、実際のユーザー エクスペリエンスの測定値です。web-vitals を本番環境にリリースすると、ユーザーの接続速度、デバイスの電力、サイトの操作方法に基づいて、さまざまな結果が表示されます。web-vitals ライブラリの機能を示すために、低速接続でのユーザー エクスペリエンスをシミュレートします。

  1. 保存したファイルをウェブブラウザで開きます。
  2. ウェブページを右クリックします。
  3. [検証] をクリックして Google Chrome デベロッパー ツールを開きます。

1d60156133044215.png

  1. [Console] タブ > 「Console settingsb5c716ebfacfd86.png をクリックします。

a310e2b6e03891a1.png

  1. [Preserve log] チェックボックスをオンにすると、ウェブページを更新したときにログが保持されます。

cdfbcd3315aa45cd.png

  1. [Network] タブ > [Online] > [Slow 3G] をクリックして、低速のネットワーク接続をシミュレートします。

b1fab3d167d032f0.png

  1. [Console] タブをクリックします。
  2. ウェブページの任意の場所をクリックして、Largest Contentful Paint(LCP)と First Input Delay(FID)の指標を出力させます。
  3. Reload this pageacaaa8c0fdd33b1.png をクリックして、Cumulative Layout Shift(CLS)の指標を出力させます。

e18b530e48108a4.png

  1. [Network] タブ > [Online] > [Fast 3G] をクリックして、高速のネットワーク接続をシミュレートします。
  2. [Console] タブをクリックします。
  3. ウェブページの任意の場所をクリックして、再度 LCP と FID の指標を出力させます。

e5d5ca555ded9f7a.png

  1. 「Reload this page」acaaa8c0fdd33b1.png をクリックして、再度 CLS の指標を出力させます。

e8bde4594a01021b.png

これで、ウェブページのウェブに関する主な指標を測定できました。

4. web-vitals データを詳しく調べる

測定するウェブに関する主な指標のイベントごとに、パフォーマンスのボトルネックのデバッグに利用できる多くの情報が返されます。各 web-vitals イベントには、現在の指標値に寄与しているイベントに関する情報とともに、entries 配列が含まれています。

CLS entries

getCLS() によってログに記録されたオブジェクトの entries プロパティを開くと、LayoutShift エントリのリストが表示されます。各 LayoutShift には、レイアウト シフト スコアを反映する value プロパティと、シフトされた要素の確認に使用できる sources 配列が含まれています。

355f0ff58e735079.png

この例では 2 つのレイアウト シフトが発生し、どちらもページの h1 要素を移動させています。currentRect プロパティは要素の現在の位置を示し、previousRect プロパティは要素の以前の位置を示しています。

LCP entries

getLCP() によってログに記録されたオブジェクトの entries プロパティを開くと、最終的な値がレポートされる前に Largest Contentful Paint の候補だった要素がわかります。

737ebf826005dbe7.png

この例では、entries 配列にすべての LCP 候補のリストが時系列順で含まれています。h1 要素が最初にレンダリングされ、その後に img 要素がレンダリングされます。img は Largest Contentful Paint でした。レポートされた LCP 要素は、常に配列の最後の項目となります。

FID entries

getFID(), でログに記録されたオブジェクトの entries プロパティを開くと、ページの最初のユーザー入力に対する PerformanceEventTiming エントリを含む配列が表示されます。

a63ef33575c3218d.png

name プロパティは、メインスレッドの可用性についてタイマーをトリガーしたユーザー入力の種類を示します。web-vitals がレポートする value は、PerformanceEventTiming エントリの startTime プロパティと processingStart プロパティの間の遅延を、マイクロ秒からミリ秒に変換したものです。この例では、測定された FID は 2 ミリ秒です。

5. web-vitals データを準備して Google アナリティクス 4 に送信する

web-vitals データを Google アナリティクス 4 に送信する前に、GA4 で受信できる形式に変換する必要があります。また、有用な診断情報を引き出す便利な関数も追加します。

エントリのターゲット ノードを特定しやすくするためにセレクタを生成する

まず、CSS セレクタと同様の形式で、ノードとその DOM 内での位置の文字列表現を生成する関数を、スクリプト ブロックに追加します。この関数の出力は、ページ内のどの要素が CWV 値に関与しているのかを特定するために役立ちます。

diagnostics.html

function getSelector(node, maxLen = 100) {
 let sel = '';
 try {
   while (node && node.nodeType !== 9) {
     const part = node.id ? '#' + node.id : node.nodeName.toLowerCase() + (
       (node.className && node.className.length) ?
       '.' + Array.from(node.classList.values()).join('.') : '');
     if (sel.length + part.length > maxLen - 1) return sel || part;
     sel = sel ? part + '>' + sel : part;
     if (node.id) break;
     node = node.parentNode;
   }
 } catch (err) {
   // Do nothing...
 }
 return sel;
}

LayoutShift 情報を取得する

発生したレイアウト シフトをすべてログに記録すると、データが過剰に生成される可能性が高くなります。以下の関数は、最大の LayoutShift エントリと、その中の最大の LayoutShiftSource にのみ着目しています。これにより、サイトにおけるレイアウト シフトの最も重要な原因に集中して最適化できます。レイアウト シフトの原因が特定され、それを最小限に抑える方法が見つかると、レポートに表示されるレイアウト シフト ソースが新たな最悪の原因を示すように変更されます。

diagnostics.html

function getLargestLayoutShiftEntry(entries) {
 return entries.reduce((a, b) => a && a.value > b.value ? a : b);
}

function getLargestLayoutShiftSource(sources) {
 return sources.reduce((a, b) => {
   return a.node && a.previousRect.width * a.previousRect.height >
       b.previousRect.width * b.previousRect.height ? a : b;
 });
}
  • getLargestLayoutShiftEntry() は、ページビューのライフサイクルで最大のレイアウト シフト エントリのみを返します。
  • getLargestLayoutShiftSource() は、そのエントリ内で最大のレイアウト シフト ソースのみを返します。

FID が DOMContentLoaded の前に発生したか後に発生したかを判断する

DOMContentLoaded イベントは、ページの HTML が完全に読み込まれ、解析された後に発生します。これには、同期、遅延、またはモジュール スクリプト(静的にインポートされたすべてのモジュールを含む)の読み込みの待機が含まれます。この関数は、最初のユーザー入力が DOMContentLoaded より前に発生した場合は true を返し、後に発生した場合は false を返します。

diagnostics.html

function wasFIDBeforeDCL(fidEntry) {
 const navEntry = performance.getEntriesByType('navigation')[0];
 return navEntry && fidEntry.startTime < navEntry.domContentLoadedEventStart;
}

FID ターゲット要素を特定する

他に役立つ可能性のあるデバッグ シグナルは、操作した要素です。要素の操作自体は FID に寄与しませんが(FID はイベント全体のレイテンシの遅延部分にすぎません)、ユーザーがどの要素を操作しているかを把握しておくと、FID を改善する最善の方法を判断する際に役立つことがあります。

最初の入力イベントに関連する要素を取得するには、first-input エントリの target プロパティを参照します。

diagnostics.html

function getFIDDebugTarget(entries) {
  return entries[0].target;
}

FID 入力イベントタイプを特定する

FID 測定をトリガーしたイベントのタイプを把握して、ページ上でのユーザーの操作を特定することも有用です。

diagnostics.html

function getFIDEventType(entries) {
  return entries[0].name;
}

各 CWV のデバッグ情報を構造化する

このコードを Google アナリティクスに送信する最後のステップは、前述の関数から返された情報を含め、エントリの情報を構造化することです。

diagnostics.html

function getDebugInfo(name, entries = []) {
  // In some cases there won't be any entries (e.g. if CLS is 0,
  // or for LCP after a bfcache restore), so we have to check first.
  if (entries.length) {
    if (name === 'LCP') {
      const lastEntry = entries[entries.length - 1];
      return {
        debug_target: getSelector(lastEntry.element),
        event_time: lastEntry.startTime,
      };
    } else if (name === 'FID') {
      const firstEntry = entries[0];
      return {
        debug_target: getSelector(firstEntry.target),
        debug_event: firstEntry.name,
        debug_timing: wasFIDBeforeDCL(firstEntry) ? 'pre_dcl' : 'post_dcl',
        event_time: firstEntry.startTime,
      };
    } else if (name === 'CLS') {
      const largestEntry = getLargestLayoutShiftEntry(entries);
      if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
        const largestSource = getLargestLayoutShiftSource(largestEntry.sources);
        if (largestSource) {
          return {
            debug_target: getSelector(largestSource.node),
            event_time: largestEntry.startTime,
          };
        }
      }
    }
  }
  // Return default/empty params in case there are no entries.
  return {
    debug_target: '(not set)',
  };
}

データを Google アナリティクスに送信する

最後に、web-vitals イベントからパラメータを受け取って Google アナリティクスに渡す関数を作成します。

diagnostics.html

function sendToGoogleAnalytics({ name, delta, value, id, entries }) {
  gtag('event', name, {
    // Built-in params:
    value: delta, // Use `delta` so the value can be summed.
    // Custom params:
    metric_id: id, // Needed to aggregate events.
    metric_value: value, // Value for querying in BQ
    metric_delta: delta, // Delta for querying in BQ
    // Send the returned values from getDebugInfo() as custom parameters
      ...getDebugInfo(name, entries)
  });
}

関数を、各 web-vitals 関数に登録します。これはブラウザで各イベントを測定する準備が整うと呼び出されます。

diagnostics.html

getLCP(sendToGoogleAnalytics);
getFID(sendToGoogleAnalytics);
getCLS(sendToGoogleAnalytics);

これで、web-vitals イベントを Google アナリティクスに送信できるようになりました。

6. web-vitals データが Google アナリティクスに入力されていることを確認する

Google アナリティクス 4 プロパティでイベントが記録されていることを確認するには:

  1. Google アナリティクス 4 プロパティを開き、[レポート] に移動します。

ab1bf51ba70f3609.png

  1. [リアルタイム] を選択します。

65a5b8087b09b2a.png

  1. テストページを数回更新し、更新の間にページをクリックして FID イベントをトリガーします。
  2. [リアルタイムの概要] UI の [イベント名別のイベント数] セクションを確認します。LCP、FID、CLS イベントが表示されているはずです。

f92b276df1c2f6ce.png

  1. いずれかのイベント名をクリックすると、そのイベントで渡されるパラメータが表示されます。

8529bd743f121dd9.png

  1. こうしたパラメータキーをクリックすると、Google アナリティクスが受け取った値の概要が表示されます。

f0cf6a3dd607d533.png

デバッグ情報に、ページ テンプレート名や、この Codelab で説明した FID に関連するその他のページイベントといったデータを追加することをおすすめします。getDebugInfo() 関数の return ステートメントを変更するだけです。

テストページからのデータに問題がなければ、新しい GA コードをサイトの本番環境にデプロイし、次のステップに進みます。

7. BigQuery でデータをクエリする

Google アナリティクスのコードを公開して数日経過したら、BigQuery でデータのクエリを開始できます。まず、データが BigQuery に転送されていることを確認します。

  1. Google Cloud Console を開き、画面上部のプルダウン メニューからプロジェクトを選択します。
  2. 画面左上のナビゲーション メニュー 3cbb0e5fcc230aef.png で、[アナリティクス] ヘッダーの下にある [BigQuery] をクリックします。
  3. [エクスプローラ] ペインでプロジェクトを開き、Google アナリティクスのデータセットを表示します。データセット名は analytics_ で、その後に Google アナリティクス 4 プロパティ ID が続きます(例: analytics_229787100)
  4. データセットを開くと、events_ テーブルが表示されます。かっこ内の数字はクエリに利用できる日数です。

CWV イベントのみを選択するサブクエリ

CWV イベントのみを含むデータセットをクエリするには、まず、過去 28 日間の LCP、CLS、FID イベントを選択するサブクエリを使用します。具体的には、metric_id キーを使用して各 web-vitals イベント ID について最後に報告された値を探し、同じ CWV イベントが複数回カウントされないようにします。

# Subquery all Web Vitals events from the last 28 days
WITH web_vitals_events AS (
 SELECT event_name as metric_name, * EXCEPT(event_name, is_last_received_value) FROM
 (
   SELECT *
   , IF (ROW_NUMBER() OVER (
     PARTITION BY (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'metric_id')
     ORDER BY (SELECT COALESCE(value.double_value, value.int_value) FROM UNNEST(event_params) WHERE key = 'metric_value') DESC
   ) = 1, true, false) AS is_last_received_value
   # Make sure to update your project ID and GA4 property ID here!
   FROM `YOUR_PROJECT_ID.analytics_YOUR_GA_PROPERTY_ID.events_*`
   WHERE event_name in ('CLS', 'FID', 'LCP') AND
     _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE, INTERVAL 28 DAY)) AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY))
  )
  WHERE is_last_received_value
)

これが、このデータセットに対するすべてのクエリの基礎となります。メインクエリは、一時テーブル web_vitals_events に対して実行されます。

GA4 イベントの構造

各 Google アナリティクス 4 イベントデータは、event_params 列で STRUCT に保持されています。サイトで GA4 に渡す各イベント パラメータはそのキーで表されます。値は STRUCT であり、可能性のあるデータ型ごとにキーを持ちます。上記の例では、metric_value キーが int_value または double_value を持つ可能性があるため、COALESCE() 関数が使用されています。前に渡した debug_target を取得するために、debug_targetstring_value キーを選択します。

...
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = "debug_target") as debug_target
...

パフォーマンスが最も低いページと要素を特定する

debug_target は、ページ上の要素のうち指標値に最も関連するものに対応する CSS セレクタ文字列です。

CLS では、debug_target は CLS 値に寄与した最大のレイアウト シフトから最大の要素を表します。シフトされた要素がない場合、debug_target の値は null です。

次のクエリは、CLS 値の低いものから高いものまでページをリストします(75 パーセンタイル、debug_target でグループ化)。

# Main query logic
SELECT
  page_path,
  debug_target,
  APPROX_QUANTILES(metric_value, 100)[OFFSET(75)] AS metric_p75,
  COUNT(1) as page_views
FROM (
  SELECT
    REGEXP_SUBSTR((SELECT value.string_value FROM UNNEST(event_params) WHERE key = "page_location"), r'\.com(\/[^?]*)') AS page_path,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "debug_target") as debug_target,
    ROUND((SELECT COALESCE(value.double_value, value.int_value) FROM UNNEST(event_params) WHERE key = "metric_value"), 3) AS metric_value,
    *
  FROM web_vitals_events
  WHERE metric_name = 'CLS'
)
GROUP BY 1, 2
# OPTIONAL: You may want to limit your calculations to pages with a
# minimum number of pageviews to reduce noise in your reports.
# HAVING page_views > 50
ORDER BY metric_p75 DESC

1bbbd957b4292ced.png

ページ上のどの要素がシフトしているのかがわかれば、問題の根本原因の特定と修正が非常に容易になります。

ここでレポートされる要素は、ローカルでページをデバッグした際にシフトが確認される要素と同じでない可能性があることに留意してください。そのため、このデータを最初にキャプチャすることが非常に重要です。問題だと気付いていないものを修正することは非常に困難です。

他の指標をデバッグする

前述のクエリでは CLS 指標の結果が表示されますが、まったく同じ手法を使用して LCP と FID のデバッグ ターゲットをレポートできます。WHERE 句を関連する指標に置き換えてデバッグするだけです。

# Replace:
# WHERE metric_name = 'CLS'
# With:
WHERE metric_name = 'LCP'

8. データポータルでクエリ結果を可視化する

BigQuery では、データポータルを通じてクエリ結果をすばやく可視化できます。データポータルは、データの可視化とダッシュボードの機能を備えた、無料で使用できるツールです。BigQuery UI でクエリを実行した後にクエリ結果を可視化するには、[データを探索] をクリックし、[データポータルで調べる] を選択します。

BigQuery のデータポータル オプションで調べる

これにより、探索ビューに BigQuery からデータポータルへの直接リンクが作成されます。このビューでは、可視化するフィールドの選択、グラフの種類の選択、フィルタの設定、アドホック グラフの作成を行って、視覚的な分析を迅速に行うことができます。前述のクエリ結果から次の折れ線グラフを作成して、LCP 値の推移を確認できます。

データポータルによる毎日の LCP 値の折れ線グラフ

こうした BigQuery とデータポータルの直接リンクにより、任意のクエリから簡単なグラフを作成し、視覚的な分析を行うことができます。ただし、さらに分析する場合は、インタラクティブなダッシュボードで複数のグラフを確認して、全体像を把握するか、データを詳しく確認できるようにすることをおすすめします。便利なダッシュボードがあれば、指標を分析するたびにクエリを記述してグラフを手動で生成する必要がなくなります。

データポータルでは、ネイティブ BigQuery コネクタを使用してダッシュボードを作成できます。そのためには、datastudio.google.com に移動して新しいデータソースを作成し、BigQuery コネクタを選択して、使用するデータセットを選択します。

データポータルで BigQuery ネイティブ コネクタを使用する

9. ウェブに関する指標データを実体化する

前述のようにウェブに関する指標のイベントデータのダッシュボードを作成する場合、Google アナリティクス 4 のエクスポート データセットを直接使用することは効率的ではありません。GA4 データの構造とウェブに関する指標に前処理が必要であるため、クエリの一部が複数回実行されます。このため、ダッシュボードのパフォーマンスと BigQuery の費用という 2 つの問題が生じます。

BigQuery サンドボックス モードは無料で使用できます。BigQuery の無料枠では、処理されるクエリデータのうち、毎月最初の 1 TB までが無料です。この記事で説明している分析方法では、かなり大規模なデータセットを使用している場合や、データセットを定期的かつ頻繁にクエリしている場合を除き、毎月この無料枠の制限内に収まるはずです。ただし、トラフィック量の多いウェブサイトがあり、高速なインタラクティブ ダッシュボードを使用してさまざまな指標を定期的にモニタリングする場合は、パーティショニング、クラスタリング、キャッシュ保存などの BigQuery の効率化機能を利用しながら、ウェブに関する指標データの前処理と実体化を行うことをおすすめします。

次のスクリプトは、BigQuery データ(ソーステーブル)を前処理し、実体化テーブル(ターゲット テーブル)を作成します。

# Materialize Web Vitals metrics from GA4 event export data

# Replace target table name
CREATE OR REPLACE TABLE YOUR_PROJECT_ID.analytics_YOUR_GA_PROPERTY_ID.web_vitals_summary
  PARTITION BY DATE(event_timestamp)
AS
SELECT
  ga_session_id,
  IF(
    EXISTS(SELECT 1 FROM UNNEST(events) AS e WHERE e.event_name = 'first_visit'),
    'New user',
    'Returning user') AS user_type,
  IF(
    (SELECT MAX(session_engaged) FROM UNNEST(events)) > 0, 'Engaged', 'Not engaged')
    AS session_engagement,
  evt.* EXCEPT (session_engaged, event_name),
  event_name AS metric_name,
  FORMAT_TIMESTAMP('%Y%m%d', event_timestamp) AS event_date
FROM
  (
    SELECT
      ga_session_id,
      ARRAY_AGG(custom_event) AS events
    FROM
      (
        SELECT
          ga_session_id,
          STRUCT(
            country,
            device_category,
            device_os,
            traffic_medium,
            traffic_name,
            traffic_source,
            page_path,
            debug_target,
            event_timestamp,
            event_name,
            metric_id,
            IF(event_name = 'LCP', metric_value / 1000, metric_value) AS metric_value,
            user_pseudo_id,
            session_engaged,
            session_revenue) AS custom_event
        FROM
          (
            SELECT
              (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id')
                AS ga_session_id,
              (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'metric_id')
                AS metric_id,
              ANY_VALUE(device.category) AS device_category,
              ANY_VALUE(device.operating_system) AS device_os,
              ANY_VALUE(traffic_source.medium) AS traffic_medium,
              ANY_VALUE(traffic_source.name) AS traffic_name,
              ANY_VALUE(traffic_source.source) AS traffic_source,
              ANY_VALUE(
                REGEXP_SUBSTR(
                  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location'),
                  r'^[^?]+')) AS page_path,
              ANY_VALUE(
                (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'debug_target'))
                AS debug_target,
              ANY_VALUE(user_pseudo_id) AS user_pseudo_id,
              ANY_VALUE(geo.country) AS country,
              ANY_VALUE(event_name) AS event_name,
              SUM(ecommerce.purchase_revenue) AS session_revenue,
              MAX(
                (
                  SELECT
                    COALESCE(
                      value.double_value, value.int_value, CAST(value.string_value AS NUMERIC))
                  FROM UNNEST(event_params)
                  WHERE key = 'session_engaged'
                )) AS session_engaged,
              TIMESTAMP_MICROS(MAX(event_timestamp)) AS event_timestamp,
              MAX(
                (
                  SELECT COALESCE(value.double_value, value.int_value)
                  FROM UNNEST(event_params)
                  WHERE key = 'metric_value'
                )) AS metric_value,
            FROM
              # Replace source table name
              `YOUR_PROJECT_ID.analytics_YOUR_GA_PROPERTY_ID.events_*`
            WHERE
              event_name IN ('LCP', 'FID', 'CLS', 'first_visit', 'purchase')
            GROUP BY
              1, 2
          )
      )
    WHERE
      ga_session_id IS NOT NULL
    GROUP BY ga_session_id
  )
CROSS JOIN UNNEST(events) AS evt
WHERE evt.event_name NOT IN ('first_visit', 'purchase');

この実体化データセットには複数の利点があります。

  • データ構造がフラット化され、クエリしやすくなります。
  • 元の GA4 データセットのウェブに関する指標イベントのみが保持されます。
  • セッション ID、ユーザータイプ(新規かリピーターか)、セッション エンゲージメント情報を、列で直接利用できます。
  • テーブルが日付で分割され、指標名でクラスタ化されます。これにより、通常は各クエリで処理されるデータ量が削減されます。
  • このテーブルをクエリするためにワイルドカードを使用する必要はないため、クエリ結果を最大 24 時間キャッシュに保存できます。これにより、同じクエリを繰り返すことによる費用を削減できます。
  • BigQuery BI Engine を使用する場合は、最適化された SQL 関数と演算子をこのテーブルで実行できます。

この実体化テーブルは、BigQuery UI から直接クエリできます。また、BigQuery コネクタを使用してデータポータルで使用することもできます。

定期的な実体化ジョブを実行する

期間を指定せずに上記のクエリを実行すると、Google アナリティクスのデータセット全体に対して実行されます。過去のデータを大量に再処理するため、これを毎日行うことは避ける必要があります。クエリの先頭で CREATE or REPLACE TABLE ステートメントを削除し、events_intraday_ テーブルに対するサブクエリの WHERE 句に別の条件を追加することで、前日のデータのみを追加するようにクエリを更新できます。

FROM
  # Replace source table name
  `YOUR_PROJECT_ID.analytics_YOUR_GA_PROPERTY_ID.events_intraday_*`
WHERE
  event_name IN ('LCP', 'FID', 'CLS', 'first_visit', 'purchase')
  # The _TABLE_SUFFIX replaces the asterisk (*) in the table name
  #
  AND _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY)

このクエリは昨日のデータのみを返します。その後、BigQuery Console を使用して、クエリが毎日実行されるようにスケジュール設定できます。

10. Google データポータルでデータを可視化する

Google データポータルは、Google BigQuery からのデータの読み取りをネイティブにサポートしています。Google アナリティクス 4 の web-vitals データが BigQuery に入力されたので、データポータルの BigQuery コネクタを使用して、実体化テーブルを直接読み取れるようになりました。

ウェブに関する指標コネクタを使用する

ダッシュボードを最初から作成するには時間がかかるため、Google はテンプレート ダッシュボードを作成するパッケージ化されたソリューションを開発しました。まず、前述のクエリを使用してウェブに関する指標テーブルを実体化したことを確認します。次に goo.gle/web-vitals-connector を使用して、データポータルのウェブに関する指標コネクタにアクセスします。

1 回限りの承認を行うと、次の設定画面が表示されます。

ウェブに関する指標コネクタの承認画面

実体化 BigQuery テーブル ID(ターゲット テーブル)と BigQuery の請求プロジェクト ID を指定します。[接続] をクリックすると、テンプレート化された新しいダッシュボードが作成され、データが関連付けられます。ダッシュボードは必要に応じて編集、変更、共有できます。ダッシュボードを一度作成すれば、異なるデータセットから複数のダッシュボードを作成する場合を除き、コネクタのリンクに再度アクセスする必要はありません。

ダッシュボードを操作すると、[サマリー] タブで、ウェブに関する指標の毎日の傾向と、ユーザーやセッションなどのウェブサイトの使用状況に関する情報を確認できます。

[ユーザー分析] タブでは、指標を選択し、ユーザー数だけでなく、種々の使用状況やビジネス指標別に指標のパーセンタイルの内訳を確認できます。

[ページ経路の分析] タブでは、ウェブサイトの問題のある部分を特定できます。ここでは指標を選択して概要を表示できますが、すべてのページパスの散布図も表示されます(Y 軸はパーセンタイル値、X 軸はレコード数)。散布図では、指標の値が想定よりも低いページを特定できます。ページを選択すると、ページパス テーブルの散布図やデバッグ ターゲット テーブルを表示して、問題のある部分をさらに詳しく確認できます。

[収益の分析] タブは、ビジネス指標とパフォーマンス指標を同じ場所でモニタリングする方法の一例です。このセクションでは、ユーザーが購入を行ったセッションがすべてプロットされます。特定のセッションで得た収益とユーザー エクスペリエンスを比較できます。

11. その他のリソース

この Codelab は以上です。これで、サイト全体でウェブに関する主な指標のパフォーマンスをきめ細かくトラッキングできるようになりました。また、CWV を高くしているサイトのページの種類や要素を特定できるようになったため、最適化に専念できます。

参考資料

web.dev には、ウェブに関する主な指標を改善するための戦略についての記事や事例紹介が多数掲載されています。まずは指標ごとに、最適化の記事をご覧ください。

リファレンス ドキュメント