Tạo giao diện tìm kiếm bằng tiện ích tìm kiếm

Tiện ích tìm kiếm cung cấp một giao diện tìm kiếm có thể tuỳ chỉnh cho các ứng dụng web. Bạn chỉ cần dùng một lượng nhỏ HTML và JavaScript để triển khai và hỗ trợ các tính năng phổ biến như khía cạnh và phân trang. Bạn cũng có thể tuỳ chỉnh giao diện bằng CSS và JavaScript.

Nếu bạn cần linh hoạt hơn, hãy sử dụng Query API. Xem phần Tạo giao diện tìm kiếm bằng Query API.

Tạo giao diện tìm kiếm

Để tạo giao diện tìm kiếm, bạn cần thực hiện các bước sau:

  1. Định cấu hình một ứng dụng tìm kiếm.
  2. Tạo mã ứng dụng cho ứng dụng.
  3. Thêm mã đánh dấu HTML cho hộp tìm kiếm và kết quả.
  4. Tải tiện ích trên trang.
  5. Khởi chạy tiện ích.

Định cấu hình ứng dụng tìm kiếm

Mỗi giao diện tìm kiếm đều yêu cầu một ứng dụng tìm kiếm được xác định trong Bảng điều khiển dành cho quản trị viên. Ứng dụng này cung cấp các chế độ cài đặt truy vấn, chẳng hạn như nguồn dữ liệu, khía cạnh và các thông số chất lượng tìm kiếm.

Để tạo một ứng dụng tìm kiếm, hãy xem bài viết Tạo trải nghiệm tìm kiếm tuỳ chỉnh.

Tạo mã ứng dụng cho ứng dụng

Ngoài các bước trong phần Định cấu hình quyền truy cập vào Cloud Search API, hãy tạo mã ứng dụng khách cho ứng dụng web của bạn.

Định cấu hình dự án

Khi định cấu hình dự án:

  • Chọn loại ứng dụng khách Trình duyệt web.
  • Cung cấp URI gốc của ứng dụng.
  • Ghi lại mã ứng dụng khách. Tiện ích này không yêu cầu khoá bí mật của ứng dụng.

Để biết thêm thông tin, hãy xem bài viết OAuth 2.0 cho ứng dụng web phía máy khách.

Thêm mã đánh dấu HTML

Tiện ích này yêu cầu các phần tử HTML sau:

  • Một phần tử input cho hộp tìm kiếm.
  • Một phần tử để cố định hộp thoại đề xuất.
  • Một phần tử cho kết quả tìm kiếm.
  • (Không bắt buộc) Một phần tử cho các chế độ kiểm soát khía cạnh.

Đoạn mã này cho thấy các phần tử được xác định bằng thuộc tính 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>

Tải tiện ích

Thêm trình tải bằng thẻ <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>

Cung cấp một lệnh gọi lại onload. Khi trình tải đã sẵn sàng, hãy gọi gapi.load() để tải ứng dụng API, tính năng Đăng nhập bằng Google và các mô-đun Tìm kiếm trên đám mây.

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

Khởi chạy tiện ích

Khởi động thư viện ứng dụng bằng gapi.client.init() hoặc gapi.auth2.init() bằng mã ứng dụng và phạm vi https://www.googleapis.com/auth/cloud_search.query. Sử dụng các lớp trình tạo để định cấu hình và liên kết tiện ích.

Ví dụ về quá trình khởi chạy:

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

Biến cấu hình:

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/...";

Tuỳ chỉnh trải nghiệm đăng nhập

Tiện ích này nhắc người dùng đăng nhập khi họ bắt đầu nhập. Bạn có thể sử dụng tính năng Đăng nhập bằng Google cho trang web để có trải nghiệm phù hợp.

Uỷ quyền trực tiếp cho người dùng

Sử dụng tính năng Đăng nhập bằng Google để theo dõi và quản lý trạng thái đăng nhập. Ví dụ này sử dụng GoogleAuth.signIn() khi người dùng nhấp vào một nút:

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

Tự động đăng nhập người dùng

Uỷ quyền trước cho ứng dụng để người dùng trong tổ chức của bạn có thể đăng nhập dễ dàng hơn. Tính năng này cũng hữu ích với Cloud Identity Aware Proxy. Xem bài viết Sử dụng tính năng Đăng nhập bằng Google với IT Apps.

Tuỳ chỉnh giao diện

Bạn có thể thay đổi giao diện của tiện ích bằng cách:

  • Ghi đè các kiểu bằng CSS.
  • Trang trí các phần tử bằng một bộ chuyển đổi.
  • Tạo các phần tử tuỳ chỉnh bằng một bộ chuyển đổi.

Ghi đè các kiểu bằng CSS

Tiện ích này có CSS riêng. Để ghi đè, hãy dùng bộ chọn tổ tiên để tăng độ cụ thể:

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

Xem tài liệu tham khảo về Các lớp CSS được hỗ trợ.

Trang trí các phần tử bằng một bộ chuyển đổi

Tạo và đăng ký một bộ chuyển đổi để sửa đổi các phần tử trước khi kết xuất. Ví dụ này thêm một lớp CSS tuỳ chỉnh:

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

Đăng ký bộ chuyển đổi trong quá trình khởi chạy:

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

Tạo các phần tử tuỳ chỉnh bằng một bộ chuyển đổi

Triển khai createSuggestionElement, createFacetResultElement hoặc createSearchResultElement để tạo các thành phần giao diện người dùng tuỳ chỉnh. Ví dụ này sử dụng thẻ <template> HTML:

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

Đăng ký bộ chuyển đổi:

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

Các phần tử khía cạnh tuỳ chỉnh phải tuân thủ các quy tắc sau:

  • Đính kèm cloudsearch_facet_bucket_clickable vào các phần tử có thể nhấp vào.
  • Bọc từng nhóm trong một cloudsearch_facet_bucket_container.
  • Duy trì thứ tự nhóm trong phản hồi.

Ví dụ: đoạn mã sau đây hiển thị các khía cạnh bằng cách sử dụng đường liên kết thay vì hộp đánh dấu.

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) {
  // ...
}

Tuỳ chỉnh hành vi tìm kiếm

Ghi đè chế độ cài đặt ứng dụng tìm kiếm bằng cách chặn các yêu cầu bằng một bộ chuyển đổi. Triển khai interceptSearchRequest để sửa đổi các yêu cầu trước khi thực thi. Ví dụ này giới hạn các truy vấn ở một nguồn đã chọn:

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

Đăng ký bộ chuyển đổi:

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 sau đây được dùng để hiển thị một hộp chọn để lọc theo nguồn:

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>

Đoạn mã sau đây sẽ theo dõi sự thay đổi, đặt lựa chọn và thực thi lại truy vấn nếu cần.

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

Bạn cũng có thể chặn phản hồi tìm kiếm bằng cách triển khai interceptSearchResponse trong bộ chuyển đổi.

Ghim phiên bản

  • Phiên bản API: Đặt cloudsearch.config/apiVersion trước khi khởi chạy.
  • Phiên bản tiện ích: Sử dụng gapi.config.update('cloudsearch.config/clientVersion', 1.1).

Cả hai đều mặc định là 1.0 nếu bạn không đặt.

Ví dụ: để ghim tiện ích vào phiên bản 1.1:

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

Bảo mật giao diện tìm kiếm

Làm theo các phương pháp hay nhất về bảo mật cho ứng dụng web, đặc biệt là để ngăn chặn hành vi tấn công bằng cách nhấp chuột.

Bật tính năng gỡ lỗi

Sử dụng interceptSearchRequest để bật tính năng gỡ lỗi:

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