検索ウィジェットを使用して検索インターフェースを作成する

検索ウィジェットは、ウェブ アプリケーション用のカスタマイズ可能な検索インターフェースを提供します。 実装に必要な HTML と JavaScript は最小限で、ファセットやページ分割などの一般的な機能をサポートしています。CSS と JavaScript を使用してインターフェースをカスタマイズすることもできます。

より柔軟な対応が必要な場合は、Query API を使用してください。 Query API を使用して検索インターフェースを作成するをご覧ください。

検索インターフェースを作成する

検索インターフェースを作成するには、次の手順を行います。

  1. 検索アプリケーションを構成する。
  2. アプリケーションのクライアント ID を生成する。
  3. 検索ボックスと結果用の HTML マークアップを追加する。
  4. ページにウィジェットを読み込む。
  5. ウィジェットを初期化する。

検索アプリケーションを構成する

各検索インターフェースには、管理コンソールで定義された検索アプリケーションが必要です。 アプリケーションは、データソース、ファセット、検索品質パラメータなどのクエリ設定を提供します。

検索アプリケーションを作成するには、 Cloud Search でカスタム検索を行えるようにするをご覧ください。

アプリケーションのクライアント ID を生成する

Cloud Search API へのアクセスを構成するの手順に加えて、 ウェブ アプリケーションのクライアント ID を生成します。

プロジェクトを構成する

プロジェクトを構成する際は、次の点に注意してください。

  • クライアント タイプとして [ウェブブラウザ] を選択します。
  • アプリの生成元 URIを 指定します。
  • クライアント ID をメモします。ウィジェットにはクライアント シークレットは必要ありません。

詳細については、クライアントサイド ウェブ アプリケーションの OAuth 2.0 をご覧ください。

HTML マークアップを追加する

ウィジェットには次の HTML 要素が必要です。

  • 検索ボックスの input 要素。
  • 候補ダイアログを固定するための要素。
  • 検索結果の要素。
  • (省略可)ファセット コントロールの要素。

次のスニペットは、id 属性で識別される要素を示しています。

serving/widget/public/with_css/index.html
<div id="search_bar">
  <div id="suggestions_anchor">
    <input type="text" id="search_input" placeholder="Search for...">
  </div>
</div>
<div id="facet_results"></div>
<div id="search_results"></div>

ウィジェットを読み込む

<script> タグを使用してローダーを含めます。

serving/widget/public/with_css/index.html
<!-- Google API loader -->
<script src="https://apis.google.com/js/api.js?mods=enable_cloud_search_widget&onload=onLoad" async defer></script>

onload コールバックを指定します。ローダーの準備ができたら、 gapi.load() を呼び出して API クライアント、Google ログイン、Cloud Search の各モジュールを読み込みます。

serving/widget/public/with_css/app.js
/**
* Load the cloud search widget & auth libraries. Runs after
* the initial gapi bootstrap library is ready.
*/
function onLoad() {
  gapi.load('client:auth2:cloudsearch-widget', initializeApp)
}

ウィジェットを初期化する

クライアント ライブラリを初期化するには、クライアント ID と https://www.googleapis.com/auth/cloud_search.query スコープを使用して gapi.client.init() または gapi.auth2.init() を使用します。ビルダー クラスを使用して、ウィジェットを構成してバインドします。

初期化の例:

serving/widget/public/with_css/app.js
/**
 * Initialize the app after loading the Google API client &
 * Cloud Search widget.
 */
function initializeApp() {
  // Load client ID & search app.
  loadConfiguration().then(function() {
    // Set API version to v1.
    gapi.config.update('cloudsearch.config/apiVersion', 'v1');

    // Build the result container and bind to DOM elements.
    var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setSearchResultsContainerElement(document.getElementById('search_results'))
      .setFacetResultsContainerElement(document.getElementById('facet_results'))
      .build();

    // Build the search box and bind to DOM elements.
    var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setInput(document.getElementById('search_input'))
      .setAnchor(document.getElementById('suggestions_anchor'))
      .setResultsContainer(resultsContainer)
      .build();
  }).then(function() {
    // Init API/oauth client w/client ID.
    return gapi.auth2.init({
        'clientId': clientId,
        'scope': 'https://www.googleapis.com/auth/cloud_search.query'
    });
  });
}

構成変数:

serving/widget/public/with_css/app.js
/**
* Client ID from OAuth credentials.
*/
var clientId = "...apps.googleusercontent.com";

/**
* Full resource name of the search application, such as
* "searchapplications/<your-id>".
*/
var searchApplicationName = "searchapplications/...";

ログイン エクスペリエンスをカスタマイズする

ユーザーが入力を開始すると、ウィジェットはログインを求めるメッセージを表示します。ウェブサイト用の Google ログインを使用すると、カスタマイズされた エクスペリエンスを提供できます。

ユーザーを直接承認する

ログイン状態をモニタリングして管理するには、「Google でログイン」を使用します。 この例では、ボタンクリック時に GoogleAuth.signIn() を使用します。

serving/widget/public/with_signin/app.js
// Handle sign-in/sign-out.
let auth = gapi.auth2.getAuthInstance();

// Watch for sign in status changes to update the UI appropriately.
let onSignInChanged = (isSignedIn) => {
  // Update UI to switch between signed in/out states
  // ...
}
auth.isSignedIn.listen(onSignInChanged);
onSignInChanged(auth.isSignedIn.get()); // Trigger with current status.

// Connect sign-in/sign-out buttons.
document.getElementById("sign-in").onclick = function(e) {
  auth.signIn();
};
document.getElementById("sign-out").onclick = function(e) {
  auth.signOut();
};

ユーザーを自動的にログインさせる

ログインを効率化するには、組織内のユーザーに対してアプリケーションを事前承認します。これは、Cloud Identity Aware Proxy でも役立ちます。IT アプリで Google ログインを使用する をご覧ください。

インターフェースをカスタマイズする

ウィジェットの外観は、次の方法で変更できます。

  • CSS でスタイルをオーバーライドする。
  • アダプタで要素を修飾する。
  • アダプタを使用してカスタム要素を作成する。

CSS でスタイルをオーバーライドする

ウィジェットには独自の CSS が含まれています。オーバーライドするには、祖先セレクタを使用して特異性を高めます。

#suggestions_anchor .cloudsearch_suggestion_container {
  font-size: 14px;
}

サポートされている CSS クラスのリファレンスをご覧ください。

アダプタで要素を修飾する

レンダリング前に要素を変更するアダプタを作成して登録します。この例では、カスタム CSS クラスを追加します。

serving/widget/public/with_decorated_element/app.js
/**
 * Search box adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.decorateSuggestionElement = function(element) {
  element.classList.add('my-suggestion');
}

/**
 * Results container adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.decorateSearchResultElement = function(element) {
  element.classList.add('my-result');
}

初期化時にアダプタを登録します。

serving/widget/public/with_decorated_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

アダプタを使用してカスタム要素を作成する

createSuggestionElementcreateFacetResultElementcreateSearchResultElement を実装して、カスタム UI コンポーネントを作成します。この例では、 HTML <template> タグを使用します。

serving/widget/public/with_custom_element/app.js
/**
 * Search box adapter that overrides creation of suggestion elements.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.createSuggestionElement = function(suggestion) {
  let template = document.querySelector('#suggestion_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.suggested_query').textContent = suggestion.suggestedQuery;
  return fragment.firstElementChild;
}

/**
 * Results container adapter that overrides creation of result elements.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.createSearchResultElement = function(result) {
  let template = document.querySelector('#result_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.title').textContent = result.title;
  fragment.querySelector('.title').href = result.url;
  let snippetText = result.snippet != null ?
    result.snippet.snippet : '';
  fragment.querySelector('.query_snippet').innerHTML = snippetText;
  return fragment.firstElementChild;
}

アダプタを登録します。

serving/widget/public/with_custom_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

カスタム ファセット要素は、次のルールに従う必要があります。

  • クリック可能な要素に cloudsearch_facet_bucket_clickable を追加します。
  • 各バケットを cloudsearch_facet_bucket_container でラップします。
  • レスポンスのバケットの順序を維持します。

たとえば、次のスニペットは、チェックボックスの代わりにリンクを使用してファセットをレンダリングします。

serving/widget/public/with_custom_facet/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}

ResultsContainerAdapter.prototype.createFacetResultElement = function(result) {
  // container for the facet
  var container = document.createElement('div');

  // Add a label describing the facet (operator/property)
  var label = document.createElement('div')
  label.classList.add('facet_label');
  label.textContent = result.operatorName;
  container.appendChild(label);

  // Add each bucket
  for(var i in result.buckets) {
    var bucket = document.createElement('div');
    bucket.classList.add('cloudsearch_facet_bucket_container');

    // Extract & render value from structured value
    // Note: implementation of renderValue() not shown
    var bucketValue = this.renderValue(result.buckets[i].value)
    var link = document.createElement('a');
    link.classList.add('cloudsearch_facet_bucket_clickable');
    link.textContent = bucketValue;
    bucket.appendChild(link);
    container.appendChild(bucket);
  }
  return container;
}

// Renders a value for user display
ResultsContainerAdapter.prototype.renderValue = function(value) {
  // ...
}

検索行動をカスタマイズする

アダプタでリクエストをインターセプトして、検索アプリケーションの設定をオーバーライドします。 実行前にリクエストを変更するには、interceptSearchRequest を実装します。この例では、クエリを選択したソースに限定します。

serving/widget/public/with_request_interceptor/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}
ResultsContainerAdapter.prototype.interceptSearchRequest = function(request) {
  if (!this.selectedSource || this.selectedSource == 'ALL') {
    // Everything selected, fall back to sources defined in the search
    // application.
    request.dataSourceRestrictions = null;
  } else {
    // Restrict to a single selected source.
    request.dataSourceRestrictions = [
      {
        source: {
          predefinedSource: this.selectedSource
        }
      }
    ];
  }
  return request;
}

アダプタを登録します。

serving/widget/public/with_request_interceptor/app.js
var resultsContainerAdapter = new ResultsContainerAdapter();
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(resultsContainerAdapter)
  // ...
  .build();

次の HTML は、ソースでフィルタリングするために選択ボックスを表示する目的で使用されます。

serving/widget/public/with_request_interceptor/index.html
<div>
  <span>Source</span>
  <select id="sources">
    <option value="ALL">All</option>
    <option value="GOOGLE_GMAIL">Gmail</option>
    <option value="GOOGLE_DRIVE">Drive</option>
    <option value="GOOGLE_SITES">Sites</option>
    <option value="GOOGLE_GROUPS">Groups</option>
    <option value="GOOGLE_CALENDAR">Calendar</option>
    <option value="GOOGLE_KEEP">Keep</option>
  </select>
</div>

次のコードは、変更をリッスンし、選択を設定し、必要に応じてクエリを再実行します。

serving/widget/public/with_request_interceptor/app.js
// Handle source selection
document.getElementById('sources').onchange = (e) => {
  resultsContainerAdapter.selectedSource = e.target.value;
  let request = resultsContainer.getCurrentRequest();
  if (request.query) {
    // Re-execute if there's a valid query. The source selection
    // will be applied in the interceptor.
    resultsContainer.resetState();
    resultsContainer.executeRequest(request);
  }
}

アダプタに interceptSearchResponse を実装して、検索レスポンスをインターセプトすることもできます。

バージョンを固定する

  • API バージョン: 初期化する前に cloudsearch.config/apiVersion を設定します。
  • ウィジェットのバージョン: gapi.config.update('cloudsearch.config/clientVersion', 1.1) を使用します。

設定しない場合、デフォルトは 1.0 です。

たとえば、ウィジェットをバージョン 1.1 に固定するには、次のようにします。

serving/widget/public/basic/app.js
gapi.config.update('cloudsearch.config/apiVersion', 'v1');

検索インターフェースを保護する

ウェブ アプリケーションのセキュリティに関するベスト プラクティスに沿って、特に クリックジャッキングを防ぐようにしてください。

デバッグを有効にする

デバッグを有効にするには、 interceptSearchRequest を使用します。

if (!request.requestOptions) {
  request.requestOptions = {};
}
request.requestOptions.debugOptions = {enableDebugging: true};
return request;