# 検索連携フロントエンドインストールガイド

<figure><img src="https://3298751723-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F86EGDbqiEQQc91OjPHgn%2Fuploads%2FMNCLWATbLzVyzBMw55cT%2F%E1%84%8C%E1%85%A6%E1%86%AB%E1%84%89%E1%85%A5%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%80%E1%85%A7%E1%86%AF%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5.png?alt=media&#x26;token=0ddf6a2e-399e-4f42-94c7-53119dc00ce0" alt=""><figcaption></figcaption></figure>

フロントエンドでgenser検索エンジンを連携する全体の手順を案内します。複雑な開発なしに、 **スクリプトの導入**と **関数呼び出し**だけでAI検索を実装できます。

{% stepper %}
{% step %}

### 連携キーを準備する (Setup)

**サービスキーおよびインスタンスキーの確認**

スクリプト連携に必要な2つの必須キー(Key)値をgenser管理画面で事前に取得しておく必要があります。

<table data-full-width="false"><thead><tr><th width="209.5078125">キー名</th><th width="250.69921875">説明</th><th>用途</th></tr></thead><tbody><tr><td>サービスキー(Service Key)</td><td>サービス連携のための固有認証キー</td><td>初期化スクリプト(<code>head</code> 領域) 設定用</td></tr><tr><td>インスタンスキー(Instance Key)</td><td>作成された検索インスタンスの固有識別子</td><td>検索API(<code>searchProducts</code>)およびログ収集呼び出し用</td></tr></tbody></table>

{% hint style="info" %}
**サービスキー確認経路**

genser管理画面の設定メニューで「サービスキー」を確認できます。
{% endhint %}

<figure><img src="https://3298751723-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F86EGDbqiEQQc91OjPHgn%2Fuploads%2FGhA3PN9r2C74IJf7X3mO%2F%E1%84%89%E1%85%A5%E1%84%87%E1%85%B5%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B5.png?alt=media&#x26;token=ca5f9a87-619a-491f-95ac-fc06bde27fba" alt=""><figcaption><p>管理画面の設定画面でのサービスキーの位置</p></figcaption></figure>

{% hint style="info" %}
**インスタンスキー確認経路**

genser管理画面のインスタンス設定メニューで、テーブルのインスタンス管理ツール「コードコピー」からインスタンスキーをコピーできます。
{% endhint %}

<figure><img src="https://3298751723-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F86EGDbqiEQQc91OjPHgn%2Fuploads%2FGLAG0UVGUNk7uH7eDBIk%2F%E1%84%8B%E1%85%B5%E1%86%AB%E1%84%89%E1%85%B3%E1%84%90%E1%85%A5%E1%86%AB%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B5.png?alt=media&#x26;token=9ebd9dd6-4749-4b0b-88b3-d8751bedb58d" alt=""><figcaption><p>管理画面インスタンス設定でのインスタンスキーコピー位置</p></figcaption></figure>

**機能有効化の確認**

APIレスポンスに特定のデータを含めたい場合、管理画面のインスタンス設定で該当機能を事前に「有効(ON)」に変更する必要があります。
{% endstep %}

{% step %}

### スクリプトを設置する (Install)

genserサービスを利用するには、全てのページの共通レイアウトに基本スクリプトを挿入する必要があります。

{% hint style="warning" %}
共通レイアウトが適用されないページがある場合は、そのページにも個別にスクリプトを挿入する必要があります。
{% endhint %}

* **場所：** HTMLドキュメントの `<head>` タグ終了直前。
* **注意事項：**
  * 共通レイアウトが適用されないページがある場合は、そのページにも個別に挿入する必要があります。
  * `発行されたサービスキー` の部分に実際に割り当てられたキー値を入力する必要があります。

```js
<head>
    <!-- ... 既存ヘッダー内容 ... -->
    
    <!-- genser スクリプト設置コード 開始 -->
    <script type="text/javascript">
        (function (a, i, u, e, o) {
            a[u] = a[u] || function () { (a[u].q = a[u].q || []).push(arguments) };
        })(window, document, "genser");
        
        // 発行されたサービスキーを入力してください
        genser("serviceKey", "発行されたサービスキー"); 
        genser("siteType", "custom");
    </script>
    <script charset="utf-8" src="//static.groobee.io/dist/g2/genser.init.min.js"></script>
    <!-- genser スクリプト設置コード 終了 -->
</head>
```

{% endstep %}

{% step %}

### 検索結果を呼び出す (Search)

基本スクリプトが全ページに挿入されていることを前提に、該当ページ内で以下のようなJavaScript関数を使用できます。

下記の関数に型に合ったデータを入れて呼び出すと、商品検索結果データを受け取れます。

#### 検索呼び出し(searchProducts)

```js
genser.call('searchProducts', {
    instanceKey: 'instanceKey', // 必須: 管理画面で作成したインスタンスキー
    queryText: '商品検索語',     // 必須: ユーザーが入力した検索語
  })
  .success((res) => {
    console.log(res);           // 検索結果データを返却 (下記レスポンス構造参照)
  })
  .error((err) => {
    console.error(err);         // エラー発生時
  });
```

<table data-full-width="false"><thead><tr><th width="131.3828125">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>instanceKey</td><td>string</td><td>必須</td><td>genser管理画面で作成したインスタンスキー（固有識別子）</td></tr><tr><td>queryText</td><td>string</td><td>必須</td><td>ユーザーが入力した商品検索語</td></tr><tr><td>...</td><td></td><td>任意</td><td>その他拡張フィールド（用途に応じて追加可能）</td></tr></tbody></table>

#### 応答データ構造(Response)

`success` コールバックで返されるデータは `type`に従って4つの形態に分類されます。

**1. 商品情報(SEARCH)**

検索された商品の一覧です。

```js
{
    "requestId": "検索語に対するID",
    "type": "SEARCH",
    "products": [
        {
            "code": "商品コード",
            "name": "商品名",
            ...
        },
        {
            "code": "商品コード",
            "name": "商品名",
            ...
        },
    ],
    "facet": {},
    "nextPageToken": ''
}
```

<table data-full-width="false"><thead><tr><th width="151.2421875">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>requestId</td><td>string</td><td>必須</td><td>検索語に対する固有のリクエストID</td></tr><tr><td>type</td><td>string</td><td>必須</td><td>応答タイプ（例: "SEARCH"）</td></tr><tr><td>products</td><td>array</td><td>必須</td><td>商品リストの配列</td></tr><tr><td>products[].code</td><td>string</td><td>必須</td><td>商品コード</td></tr><tr><td>products[].name</td><td>string</td><td>必須</td><td>商品名</td></tr><tr><td>facet</td><td>object</td><td>任意</td><td>-</td></tr><tr><td>nextPageToken</td><td>string</td><td>任意</td><td>-</td></tr></tbody></table>

**2. 検索結果の説明(SUMMARY)**

`検索結果説明の設定`この管理画面で有効化されている場合、検索結果に対する要約テキストを返します。

```js
{
    "requestId": "検索語に対するID",
    "type": "SUMMARY",
    "summary": "要約内容のテキスト"
}
```

<table data-full-width="false"><thead><tr><th width="151.2421875">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>requestId</td><td>string</td><td>必須</td><td>検索語に対する固有のリクエストID</td></tr><tr><td>type</td><td>string</td><td>必須</td><td>応答タイプ（例: "SUMMARY"）</td></tr><tr><td>summary</td><td>string</td><td>必須</td><td>検索語または結果に対する要約内容のテキスト</td></tr></tbody></table>

**3. 関連キーワード(KEYWORDS)**

`関連キーワードの設定`この管理画面で有効化されている場合、関連する推奨キーワードのリストを返します。

```js
{
    "requestId": "検索語に対するID",
    "type": "KEYWORDS",
    "keywords": ["キーワード1", "キーワード2"]
}
```

<table data-full-width="false"><thead><tr><th width="151.2421875">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>requestId</td><td>string</td><td>必須</td><td>検索語に対する固有のリクエストID</td></tr><tr><td>type</td><td>string</td><td>必須</td><td>応答タイプ（例: "KEYWORDS"）</td></tr><tr><td>keywords</td><td>string[]</td><td>必須</td><td>推奨または関連キーワードの配列（文字列リスト）</td></tr></tbody></table>

**4. 提案質問(QUESTIONS)**

`提案質問の設定`この管理画面で有効化されている場合、ユーザーが追加で気にする可能性のある質問リストを返します。

```js
{
    "requestId": "検索語に対するID",
    "type": "QUESTIONS",
    "questions": [
        "質問内容 1",
        "質問内容 2"
    ]
}
```

<table data-full-width="false"><thead><tr><th width="151.2421875">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>requestId</td><td>string</td><td>必須</td><td>検索語に対する固有のリクエストID</td></tr><tr><td>type</td><td>string</td><td>必須</td><td>応答タイプ（例: "QUESTIONS"）</td></tr><tr><td>questions</td><td>string[]</td><td>必須</td><td>提案質問の配列（文字列リスト）</td></tr></tbody></table>
{% endstep %}

{% step %}

### ログデータを収集する (Analytics)

検索品質向上と統計のためにユーザー行動データを収集します。

* **検索(SE):** `searchProducts` 関数呼び出し時に自動的に収集されます。
* **表示(DI) & クリック(CL):** 以下の関数を使用して直接呼び出す必要があります。

#### 表示 (DI)

検索結果の商品の画面表示時に呼び出します。

```js
genser.call('DI', {
    instance: { key: 'instanceKey' }, // インスタンスキー
    goods: [
      {
        code: '商品コード', // searchProducts の結果の code
        name: '商品名', // searchProducts の結果の name
      },
      ...
    ], // 商品情報
    requestId: '検索語に対するID', // searchProducts の結果で受け取った requestId
  })
  .success((res) => {
    console.log(res);   // 正常コールバック
  })
  .error((err) => {
    console.error(err); // エラーコールバック
  });

```

<table data-full-width="false"><thead><tr><th width="151.2421875">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>instance</td><td>object</td><td>必須</td><td>genser管理画面で作成したインスタンス情報</td></tr><tr><td>instance.key</td><td>string</td><td>必須</td><td>genser管理画面で作成したインスタンスキー（固有識別子）</td></tr><tr><td>goods</td><td>array</td><td>必須</td><td>ユーザーに推奨/選択された商品一覧</td></tr><tr><td>goods[].code</td><td>string</td><td>必須</td><td>商品コード（<code>searchProducts</code> レスポンスの商品のコードを使用）</td></tr><tr><td>goods[].name</td><td>string</td><td>必須</td><td>商品名（<code>searchProducts</code> レスポンスの商品の名前を使用）</td></tr><tr><td>requestId</td><td>string</td><td>必須</td><td>商品検索結果で受け取ったリクエストID（<code>searchProducts</code> レスポンスのrequestIdを使用）</td></tr></tbody></table>

#### クリック (CL)

ユーザーが商品をクリックしたときに呼び出します。

```js
genser.call('CL', {
    instance: { key: 'instanceKey' }, // インスタンスキー
    goods: [
      {
        code: '商品コード', // searchProducts の結果の code
        name: '商品名', // searchProducts の結果の name
      },
      ...
    ], // 商品情報
    requestId: '検索語に対するID', // searchProducts の結果で受け取った requestId
  })
  .success((res) => {
    console.log(res);   // 正常コールバック
  })
  .error((err) => {
    console.error(err); // エラーコールバック
  });

```

<table data-full-width="false"><thead><tr><th width="151.2421875">項目</th><th width="79.3046875">タイプ</th><th width="79.578125">必須</th><th>説明</th></tr></thead><tbody><tr><td>instance</td><td>object</td><td>必須</td><td>genser管理画面で作成したインスタンス情報</td></tr><tr><td>instance.key</td><td>string</td><td>必須</td><td>genser管理画面で作成したインスタンスキー（固有識別子）</td></tr><tr><td>goods</td><td>array</td><td>必須</td><td>ユーザーに推奨/選択された商品一覧</td></tr><tr><td>goods[].code</td><td>string</td><td>必須</td><td>商品コード（<code>searchProducts</code> レスポンスの商品のコードを使用）</td></tr><tr><td>goods[].name</td><td>string</td><td>必須</td><td>商品名（<code>searchProducts</code> レスポンスの商品の名前を使用）</td></tr><tr><td>requestId</td><td>string</td><td>必須</td><td>商品検索結果で受け取ったリクエストID（<code>searchProducts</code> レスポンスのrequestIdを使用）</td></tr></tbody></table>
{% endstep %}
{% endstepper %}

<details>

<summary>Reactコード例</summary>

```js
import React, { useState, useEffect } from 'react';

/**
 * Genser Search React Component
 * 前提条件: public/index.htmlの<head>にGenser SDKスクリプトがロードされている必要があります。
 */
const GenserSearch = () => {
  const [query, setQuery] = useState('');
  const [products, setProducts] = useState([]);
  const [requestId, setRequestId] = useState(null);
  
  // インスタンスキー（環境変数や定数で管理することを推奨）
  const INSTANCE_KEY = 'YOUR_INSTANCE_KEY';

  // 1. 検索ハンドラー
  const handleSearch = (e) => {
    e.preventDefault();
    if (!query.trim()) return;

    if (!window.genser) {
      console.error("Genser SDKがロードされていません。");
      return;
    }

    // searchProducts 呼び出し
    window.genser.call('searchProducts', {
      instanceKey: INSTANCE_KEY,
      queryText: query
    })
    .success((res) => {
      if (res.type === 'SEARCH') {
        // ステート更新 -> 再レンダリングを引き起こす
        setProducts(res.products);
        setRequestId(res.requestId);
      }
    })
    .error((err) => {
      console.error('検索エラー:', err);
    });
  };

  // 2. 表示(DI)ログ送信
  // productsやrequestIdが変更されたとき（つまり検索結果がレンダリングされたとき）に実行
  useEffect(() => {
    if (products.length > 0 && requestId) {
      window.genser.call('DI', {
        instance: { key: INSTANCE_KEY },
        goods: products.map(p => ({ code: p.code, name: p.name })),
        requestId: requestId
      });
      // console.log('DI(表示)ログ送信完了');
    }
  }, [products, requestId]);

  // 3. クリック(CL)ログ送信ハンドラー
  const handleProductClick = (product) => {
    if (!requestId) return;

    window.genser.call('CL', {
      instance: { key: INSTANCE_KEY },
      goods: [{ code: product.code, name: product.name }],
      requestId: requestId
    });
    // console.log('CL(クリック)ログ送信完了:', product.name);

    // 実際の商品ページへ遷移するロジック
    // window.location.href = `/product/${product.code}`;
  };

  return (
    <div className="genser-search-container">
      {/* 検索フォーム */}
      <form onSubmit={handleSearch}>
        <input 
          type="text" 
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="検索語を入力してください"
        />
        <button type="submit">検索</button>
      </form>

      {/* 検索結果リスト */}
      <div className="product-list">
        {products.map((product) => (
          <div 
            key={product.code} 
            className="product-item"
            onClick={() => handleProductClick(product)}
            style={{ cursor: 'pointer', margin: '10px 0', border: '1px solid #ddd', padding: '10px' }}
          >
            {/* 画像処理 (APIレスポンスに応じてフィールド名を調整する必要あり) */}
            {product.imageUrl && <img src={product.imageUrl} alt={product.name} width="100" />}
            <h3>{product.name}</h3>
            <p>{product.code}</p>
          </div>
        ))}
      </div>
    </div>
  );
};

export default GenserSearch;
```

</details>

<details>

<summary>Vueコード例</summary>

```js
<template>
  <div class="genser-search">
    <div class="search-bar">
      <input 
        v-model="query" 
        @keyup.enter="handleSearch" 
        placeholder="商品を検索してください" 
      />
      <button @click="handleSearch">検索</button>
    </div>

    <ul v-if="products.length > 0">
      <li 
        v-for="product in products" 
        :key="product.code" 
        @click="handleProductClick(product)"
      >
        {{ product.name }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue';

// 前提条件: public/index.htmlの<head>にGenser SDKスクリプトがロードされている必要があります。 [cite: 64, 239]

// 管理画面で発行されたインスタンスキー [cite: 11]
const INSTANCE_KEY = 'YOUR_INSTANCE_KEY';

const query = ref('');
const products = ref([]);
const requestId = ref(null);

// 1. 検索ハンドラー [cite: 95]
const handleSearch = () => {
  if (!query.value.trim()) return;

  if (!window.genser) {
    console.error("Genser SDKがロードされていません。");
    return;
  }

  // genser 検索API 呼び出し
  window.genser.call('searchProducts', {
    instanceKey: INSTANCE_KEY, // [cite: 98]
    queryText: query.value     // [cite: 99]
  })
  .success((res) => {
    // レスポンスタイプがSEARCHの場合データ更新 [cite: 118]
    if (res.type === 'SEARCH') {
      products.value = res.products;   // [cite: 119]
      requestId.value = res.requestId; // [cite: 117]
    }
  })
  .error((err) => {
    console.error('検索エラー:', err);
  });
};

// 2. 表示(DI)ログ送信 [cite: 177]
// products状態が変更され画面にレンダリングされた後に呼び出す
watch([products, requestId], ([newProducts, newRequestId]) => {
  if (newProducts.length > 0 && newRequestId) {
    window.genser.call('DI', {
      instance: { key: INSTANCE_KEY }, // [cite: 180]
      goods: newProducts.map(p => ({ 
        code: p.code, 
        name: p.name 
      })), // [cite: 181]
      requestId: newRequestId // [cite: 186]
    })
    .success((res) => {
       // console.log('DIログ送信成功');
    });
  }
});

// 3. クリック(CL)ログ送信ハンドラー [cite: 200]
const handleProductClick = (product) => {
  if (!requestId.value) return;

  window.genser.call('CL', {
    instance: { key: INSTANCE_KEY }, // [cite: 206]
    goods: [{ 
      code: product.code, 
      name: product.name 
    }], // [cite: 207]
    requestId: requestId.value // [cite: 212]
  })
  .success((res) => {
    console.log('CLログ送信成功、商品ページへの遷移ロジックを実行');
  });
};
</script>
```

</details>

<details>

<summary>HTML/JS コード例</summary>

```js
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Genser Search Example</title>
    </head>
<body>

    <div id="search-container">
        <input type="text" id="searchInput" placeholder="検索語を入力してください">
        <button id="searchBtn">検索</button>
    </div>

    <ul id="resultList"></ul>

    <script type="text/javascript">
        // 設定値
        const INSTANCE_KEY = 'YOUR_INSTANCE_KEY'; // [cite: 11]
        
        // 状態変数
        let currentRequestId = null;

        // DOM要素参照
        const searchInput = document.getElementById('searchInput');
        const searchBtn = document.getElementById('searchBtn');
        const resultList = document.getElementById('resultList');

        // 1. 検索ボタンクリックイベント
        searchBtn.addEventListener('click', function() {
            const query = searchInput.value;
            if (!query.trim()) return;
            
            doSearch(query);
        });

        // 検索実行関数 [cite: 95]
        function doSearch(queryText) {
            if (!window.genser) return console.error("SDK 未ロード");

            window.genser.call('searchProducts', {
                instanceKey: INSTANCE_KEY, // [cite: 98]
                queryText: queryText       // [cite: 99]
            })
            .success((res) => {
                if (res.type === 'SEARCH') {
                    currentRequestId = res.requestId; // [cite: 117]
                    renderProducts(res.products);
                    
                    // レンダリング直後に表示(DI)ログ送信
                    sendImpressionLog(res.products, res.requestId);
                }
            })
            .error((err) => {
                console.error("検索失敗:", err);
            });
        }

        // 画面レンダリング関数
        function renderProducts(products) {
            resultList.innerHTML = ''; // 既存リスト初期化

            products.forEach(product => {
                const li = document.createElement('li');
                li.textContent = product.name;
                li.style.cursor = 'pointer';
                
                // クリックイベントバインド
                li.addEventListener('click', () => {
                    handleProductClick(product);
                });

                resultList.appendChild(li);
            });
        }

        // 2. 表示(DI)ログ送信関数 [cite: 177]
        function sendImpressionLog(products, reqId) {
            window.genser.call('DI', {
                instance: { key: INSTANCE_KEY }, // [cite: 180]
                goods: products.map(p => ({
                    code: p.code,
                    name: p.name
                })), // [cite: 181]
                requestId: reqId // [cite: 186]
            });
        }

        // 3. クリック(CL)ログ送信関数 [cite: 200]
        function handleProductClick(product) {
            if (!currentRequestId) return;

            window.genser.call('CL', {
                instance: { key: INSTANCE_KEY }, // [cite: 206]
                goods: [{
                    code: product.code,
                    name: product.name
                }], // [cite: 207]
                requestId: currentRequestId // [cite: 212]
            })
            .success(() => {
                console.log('クリックログ送信完了: ' + product.name);
                // その後、商品詳細ページへの遷移などのロジックを実行
            });
        }
    </script>
</body>
</html>
```

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.genser.ai/ja/integration/frontend-installation/installation-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
