使用搜尋小工具建立搜尋介面

搜尋小工具提供網頁應用程式的自訂搜尋介面。小工具只需要少量的 HTML 和 JavaScript 就能實作及啟用常用的搜尋功能,例如商情項目和分頁。您也可以使用 CSS 和 JavaScript 自訂介面的部分。

如果您需要的彈性超過小工具提供的彈性,請考慮使用 Query API。如要瞭解如何使用 Query API 建立搜尋介面,請參閱使用 Query API 建立搜尋介面

建構搜尋介面

建構搜尋介面需要幾個步驟:

  1. 設定搜尋應用程式
  2. 產生應用程式的用戶端 ID
  3. 為搜尋框和結果新增 HTML 標記
  4. 在頁面上載入小工具
  5. 初始化小工具

設定搜尋應用程式

每個搜尋介面都必須在管理控制台中定義搜尋應用程式搜尋應用程式會提供查詢的其他資訊,例如資料來源、商情項目和搜尋品質設定。

如要建立搜尋應用程式,請參閱「建立自訂搜尋體驗」一文。

產生應用程式的用戶端 ID

除了設定 Google Cloud Search API 的存取權一文中的步驟,您還必須為網頁應用程式產生用戶端 ID。

設定專案

設定專案時:

  • 選取「網路瀏覽器」用戶端類型
  • 提供應用程式的來源 URI
  • 記下您建立的用戶端 ID,您需要用戶端 ID 才能完成後續步驟。小工具不需要用戶端密鑰。

詳情請參閱「用戶端網頁應用程式的 OAuth 2.0」。

新增 HTML 標記

小工具需要少量 HTML 程式碼才能運作。您必須提供:

  • 搜尋框的 input 元素。
  • 固定建議彈出式視窗的元素。
  • 包含搜尋結果的元素。
  • (選用) 提供包含商情項目控制項的元素。

下列 HTML 程式碼片段是搜尋小工具的 HTML,其中要繫結的元素是以其 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)
}

系統會在載入所有模組後呼叫 initializeApp() 函式。

初始化小工具

首先,請使用產生的用戶端 ID 和 https://www.googleapis.com/auth/cloud_search.query 範圍呼叫 gapi.client.init()gapi.auth2.init(),初始化用戶端程式庫。接下來,請使用 gapi.cloudsearch.widget.resultscontainer.Buildergapi.cloudsearch.widget.searchbox.Builder 類別設定小工具,並將其繫結至 HTML 元素。

以下範例說明如何初始化小工具:

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 帳戶登入」功能,依需求監控使用者的登入狀態,以及使用者登入或登出。舉例來說,以下範例會觀察 isSignedIn 狀態以監控登入變更,並使用 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();
};

詳情請參閱「使用 Google 帳戶登入」一文。

自動登入使用者

如要進一步簡化登入體驗,您可以代表機構中的使用者預先授權應用程式。如果您使用 Cloud Identity Aware Proxy 來保護應用程式,這項技術也非常實用。

如需其他資訊,請參閱在 IT 應用程式中使用 Google 登入

自訂介面

您可以透過以下技巧組合變更搜尋介面的外觀:

  • 使用 CSS 覆寫樣式
  • 使用轉接程式裝飾元素
  • 使用轉接程式建立自訂元素

使用 CSS 覆寫樣式

搜尋小工具隨附專屬的 CSS,可用來設定建議、結果元素和分頁控制項的樣式。您可以視需求重新設定這些元素的樣式。

在載入期間,搜尋小工具會動態載入其預設樣式表。此作業會在應用程式樣式表載入後,提高規則的優先順序。如要確保自有樣式的優先順序高於預設樣式,請使用祖系選取器來提高預設規則的明確性。

舉例來說,如果在文件的靜態 linkstyle 標記中載入下列規則,則不會有任何作用。

.cloudsearch_suggestion_container {
  font-size: 14px;
}

請改為使用網頁中宣告的祖系容器 ID 或類別,篩選規則。

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

如需小工具產生的支援類別清單和 HTML 範例,請參閱支援的 CSS 類別參考資料。

使用轉接程式裝飾元素

如要在顯示前修飾元素,請建立並復原轉接程式,以導入其中一種裝飾方法,例如 decorateSuggestionElementdecorateSearchResultElement.

舉例來說,下列轉接程式會在建議和結果元素中新增自訂類別。

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');
}

如要在初始化小工具時註冊轉接器,請使用個別 Builder 類別的 setAdapter() 方法:

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 的轉接程式。

下列轉接程式說明如何使用 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;
}

如要在初始化小工具時註冊轉接器,請使用個別 Builder 類別的 setAdapter() 方法:

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();

使用 createFacetResultElement 建立自訂 facet 元素時必須遵守幾項限制:

  • 您必須將 CSS 類別 cloudsearch_facet_bucket_clickable 附加至使用者按一下以切換值區的元素。
  • 您必須使用 CSS 類別 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 方法實作轉接程式,在執行之前修改對 search API 發出的要求。

舉例來說,下列轉接程式會攔截將查詢限制於使用者所選來源的要求:

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;
}

如要在初始化小工具時註冊轉接器,請在建構 ResultsContainer 時使用 setAdapter() 方法

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 版本

根據預設,小工具會使用最新的 API 穩定版。如要鎖定特定版本,請在初始化小工具前,將 cloudsearch.config/apiVersion 設定參數設為偏好版本。

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

如果未設定或設為無效的值,API 版本會預設為 1.0。

固定小工具版本

為避免對搜尋介面發生非預期的變更,請設定 cloudsearch.config/clientVersion 設定參數,如下所示:

gapi.config.update('cloudsearch.config/clientVersion', 1.1);

如果未設定或設為無效值,小工具版本會預設為 1.0。

保護搜尋介面

搜尋結果包含高度機密資訊。請遵循保護網頁應用程式的最佳做法,尤其是防範點擊攻擊攻擊。

詳情請參閱 OWASP 指南專案

啟用偵錯功能

請使用 interceptSearchRequest 開啟搜尋小工具的偵錯功能。例如:

  if (!request.requestOptions) {
  // Make sure requestOptions is populated
  request.requestOptions = {};
  }
  // Enable debugging
  request.requestOptions.debugOptions = {enableDebugging: true}

  return request;