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 giao diện tìm kiếm có thể tuỳ chỉnh cho các ứng dụng web. Tiện ích này chỉ yêu cầu một lượng nhỏ HTML và JavaScript để triển khai và bật các tính năng tìm kiếm phổ biến như các thuộc tính và phân trang. Bạn cũng có thể tuỳ chỉnh các phần của giao diện bằng CSS và JavaScript.

Nếu bạn cần linh hoạt hơn so với tiện ích mà tiện ích đó cung cấp, hãy cân nhắc sử dụng Query API (API Truy vấn). Để biết thông tin về cách tạo giao diện tìm kiếm bằng API Truy vấn, hãy tham khảo phần Tạo giao diện tìm kiếm bằng API Truy vấn.

Xây dựng giao diện tìm kiếm

Việc xây dựng giao diện tìm kiếm cần có một số bước:

  1. Định cấu hình ứng dụng tìm kiếm
  2. Tạo mã ứng dụng khách 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 phải có 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 tìm kiếm cung cấp thêm thông tin cho cụm từ tìm kiếm, chẳng hạn như nguồn dữ liệu, các thuộc tính và chế độ cài đặt chất lượng tìm kiếm.

Để tạo ứng dụng tìm kiếm, hãy tham khảo phần Tạo trải nghiệm tìm kiếm tuỳ chỉnh.

Tạo mã ứng dụng khách 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 API Google Cloud Search, bạn cũng phải tạo mã ứng dụng khách cho ứng dụng web.

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

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

  • Chọn loại ứng dụng Trình duyệt web
  • Cung cấp URI gốc của ứng dụng.
  • Ghi chú về mã ứng dụng khách đã tạo. Bạn sẽ cần mã ứng dụng khách để hoàn tất các bước tiếp theo. Không bắt buộc phải có mật khẩu ứng dụng khách cho tiện ích.

Để 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 một lượng nhỏ HTML để hoạt động. Bạn phải cung cấp:

  • Phần tử input cho hộp tìm kiếm.
  • Một phần tử để liên kết cửa sổ đề xuất bật lên.
  • Một phần tử chứa kết quả tìm kiếm.
  • (Không bắt buộc) Cung cấp một phần tử chứa các chế độ kiểm soát các thuộc tính.

Đoạn mã HTML sau đây minh hoạ HTML dành cho một tiện ích tìm kiếm, trong đó các phần tử cần liên kế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

Tiện ích này được tải động thông qua một tập lệnh trình tải. Để đưa trình tải vào, hãy sử dụng thẻ <script> như sau:

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>

Bạn phải cung cấp lệnh gọi lại onload trong thẻ tập lệnh. Hàm này được gọi khi trình tải đã sẵn sàng. Khi trình tải đã sẵn sàng, hãy tiếp tục tải tiện ích này bằng cách gọi gapi.load() để tải ứng dụng API, các mô-đun Đăng nhập bằng Google và 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)
}

Hàm initializeApp() được gọi sau khi tất cả các mô-đun được tải.

Khởi chạy tiện ích

Trước tiên, hãy khởi động thư viện ứng dụng bằng cách gọi gapi.client.init() hoặc gapi.auth2.init() cùng với mã ứng dụng khách đã tạo và phạm vi https://www.googleapis.com/auth/cloud_search.query. Tiếp theo, hãy sử dụng các lớp gapi.cloudsearch.widget.resultscontainer.Buildergapi.cloudsearch.widget.searchbox.Builder để định cấu hình tiện ích và liên kết tiện ích đó với các phần tử HTML.

Ví dụ sau cho thấy cách khởi chạy tiện ích:

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

Ví dụ trên tham chiếu hai biến cho cấu hình được xác định là:

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

Theo mặc định, tiện ích này nhắc người dùng đăng nhập và cấp quyền cho ứng dụng tại thời điểm họ bắt đầu nhập truy vấn. Bạn có thể sử dụng tính năng Đăng nhập bằng Google cho trang web để cung cấp trải nghiệm đăng nhập phù hợp hơn cho người dùng.

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

Hãy sử dụng tính năng Đăng nhập bằng Google để theo dõi trạng thái đăng nhập của người dùng và người dùng đăng nhập hoặc đăng xuất khi cần. Ví dụ: ví dụ sau quan sát trạng thái isSignedIn để theo dõi các thay đổi về việc đăng nhập và sử dụng phương thức GoogleAuth.signIn() để bắt đầu đăng nhập bằng một lượt nhấp vào 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();
};

Để biết thêm thông tin chi tiết, hãy xem bài viết Đăng nhập bằng Google.

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

Bạn có thể đơn giản hoá trải nghiệm đăng nhập hơn nữa bằng cách uỷ quyền trước cho ứng dụng thay mặt cho người dùng trong tổ chức của bạn. Kỹ thuật này cũng hữu ích nếu bạn sử dụng Cloud Identity Aware Proxy để bảo vệ ứng dụng.

Để biết thêm thông tin, hãy xem phần Sử dụng tính năng Đăng nhập bằng Google bằng ứng dụng CNTT.

Tuỳ chỉnh giao diện

Bạn có thể thay đổi giao diện tìm kiếm thông qua kết hợp các kỹ thuật:

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

Ghi đè kiểu bằng CSS

Tiện ích tìm kiếm đi kèm với CSS riêng để tạo kiểu cho các phần tử đề xuất và kết quả cũng như các chế độ kiểm soát phân trang. Bạn có thể định lại kiểu cho các thành phần này nếu cần.

Trong khi tải, tiện ích tìm kiếm sẽ tự động tải biểu định kiểu mặc định của nó. Điều này xảy ra sau khi biểu định kiểu ứng dụng được tải, việc này làm tăng mức độ ưu tiên của các quy tắc. Để đảm bảo kiểu riêng của bạn được ưu tiên hơn các kiểu mặc định, hãy sử dụng bộ chọn đối tượng cấp trên để tăng mức độ cụ thể của các quy tắc mặc định.

Ví dụ: quy tắc sau đây không có hiệu lực nếu được tải trong thẻ link hoặc style tĩnh trong tài liệu.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

Thay vào đó, hãy đáp ứng điều kiện về quy tắc bằng mã nhận dạng hoặc lớp của vùng chứa đối tượng cấp trên đã khai báo trên trang.

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

Để biết danh sách các lớp hỗ trợ và HTML mẫu do tiện ích tạo ra, hãy xem tài liệu tham khảo Các lớp CSS được hỗ trợ.

Trang trí các thành phần bằng bộ chuyển đổi

Để trang trí một phần tử trước khi kết xuất, hãy tạo và lưu lại một bộ chuyển đổi giúp triển khai một trong các phương thức trang trí như decorateSuggestionElement hoặc decorateSearchResultElement.

Ví dụ: các bộ chuyển đổi sau đây thêm một lớp tuỳ chỉnh vào phần tử đề xuất và kết quả.

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 khi khởi chạy tiện ích, hãy sử dụng phương thức setAdapter() của lớp Builder tương ứng:

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

Trang trí có thể sửa đổi các thuộc tính của phần tử vùng chứa cũng như mọi phần tử con. Bạn có thể thêm hoặc xoá các phần tử con trong khi trang trí. Tuy nhiên, nếu thay đổi cấu trúc cho các phần tử, hãy cân nhắc tạo trực tiếp các phần tử đó thay vì trang trí.

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

Để tạo phần tử tuỳ chỉnh cho một đề xuất, vùng chứa thuộc tính hoặc kết quả tìm kiếm, hãy tạo và đăng ký một bộ chuyển đổi giúp triển khai createSuggestionElement, createFacetResultElement hoặc createSearchResultElement theo cách lặp lại.

Các bộ chuyển đổi sau minh hoạ việc tạo các phần tử kết quả tìm kiếm và đề xuất tuỳ chỉnh bằng cách sử dụng thẻ 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;
}

Để đăng ký lại bộ chuyển đổi khi khởi chạy tiện ích, hãy sử dụng phương thức setAdapter() của lớp Builder tương ứng:

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

Việc tạo các phần tử thuộc tính tuỳ chỉnh bằng createFacetResultElement phải tuân theo một số hạn chế:

  • Bạn phải đính kèm lớp CSS cloudsearch_facet_bucket_clickable vào phần tử mà người dùng nhấp vào để bật/tắt bộ chứa.
  • Bạn phải gói từng bộ chứa trong một phần tử chứa lớp CSS cloudsearch_facet_bucket_container.
  • Bạn không thể hiển thị các nhóm theo thứ tự khác với thứ tự xuất hiện trong phản hồi.

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

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

Các chế độ cài đặt của Ứng dụng tìm kiếm đại diện cho cấu hình mặc định của giao diện tìm kiếm và ở dạng tĩnh. Để triển khai các thuộc tính hoặc bộ lọc động, chẳng hạn như cho phép người dùng chuyển đổi nguồn dữ liệu, bạn có thể ghi đè chế độ cài đặt của ứng dụng tìm kiếm bằng cách chặn yêu cầu tìm kiếm bằng bộ chuyển đổi.

Triển khai bộ chuyển đổi bằng phương thức interceptSearchRequest để sửa đổi các yêu cầu được gửi đến API tìm kiếm trước khi thực thi.

Ví dụ: bộ chuyển đổi sau đây chặn các yêu cầu hạn chế truy vấn đối với một nguồn do người dùng 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 khi khởi chạy tiện ích, hãy sử dụng phương thức setAdapter() khi tạo ResultsContainer

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ị 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>

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 API

Theo mặc định, tiện ích này sử dụng phiên bản ổn định mới nhất của API. Để khoá một phiên bản cụ thể, hãy đặt tham số cấu hình cloudsearch.config/apiVersion thành phiên bản ưu tiên trước khi khởi chạy tiện ích.

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

Phiên bản API sẽ mặc định là 1.0 nếu bạn không đặt hoặc đặt thành một giá trị không hợp lệ.

Ghim phiên bản tiện ích

Để tránh những thay đổi không mong muốn đối với giao diện tìm kiếm, hãy đặt tham số cấu hình cloudsearch.config/clientVersion như sau:

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

Phiên bản tiện ích sẽ mặc định là 1.0 nếu không được đặt hoặc được đặt thành giá trị không hợp lệ.

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

Kết quả tìm kiếm chứa thông tin có mức độ nhạy cảm cao. Làm theo các phương pháp hay nhất để bảo mật các ứng dụng web, đặc biệt là chống lại các cuộc tấn công clickjacking.

Để biết thêm thông tin, hãy xem Dự án hướng dẫn OWASP

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

Sử dụng interceptSearchRequest để bật tính năng gỡ lỗi cho tiện ích tìm kiếm. Ví dụ:

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

  return request;