إنشاء واجهة بحث باستخدام أداة البحث

توفّر أداة البحث المصغّرة واجهة بحث قابلة للتخصيص لتطبيقات الويب. لا تتطلّب الأداة سوى جزء صغير من رمز HTML وجافا سكريبت لتنفيذها، وتتيح ميزات البحث الشائعة، مثل الفلاتر والتقسيم على صفحات. يمكنك أيضًا تخصيص أجزاء من الواجهة باستخدام CSS وJavaScript.

إذا كنت بحاجة إلى مرونة أكبر من تلك التي توفّرها الأداة، ننصحك باستخدام واجهة برمجة التطبيقات Query API. للحصول على معلومات حول إنشاء واجهة بحث باستخدام Query API، يُرجى الرجوع إلى إنشاء واجهة بحث باستخدام Query API.

إنشاء واجهة بحث

يتطلّب إنشاء واجهة البحث عدة خطوات:

  1. إعداد تطبيق بحث
  2. إنشاء معرّف عميل للتطبيق
  3. إضافة ترميز HTML لمربّع البحث والنتائج
  4. تحميل الأداة على الصفحة
  5. إعداد التطبيق المصغّر

إعداد تطبيق بحث

يجب أن تتضمّن كل واجهة بحث تطبيق بحث محدّدًا في وحدة تحكّم المشرف. يوفر تطبيق البحث معلومات إضافية لطلب البحث، مثل مصادر البيانات والواجهات وإعدادات جودة البحث.

لإنشاء تطبيق بحث، يُرجى الرجوع إلى مقالة إنشاء تجربة بحث مخصّصة.

إنشاء معرّف عميل للتطبيق

بالإضافة إلى الخطوات الواردة في ضبط إذن الوصول إلى Google Cloud Search API، عليك أيضًا إنشاء معرّف عميل لتطبيق الويب.

إعداد مشروع

عند إعداد المشروع:

  • اختَر نوع العميل متصفّح الويب
  • قدِّم معرّف الموارد المنتظم (URI) الخاص بالمصدر لتطبيقك.
  • ملاحظة حول معرّف العميل الذي تم إنشاؤه ستحتاج إلى معرّف العميل لإكمال الخطوات التالية. لا يلزم إدخال سر العميل للوحدة.

لمزيد من المعلومات، يُرجى الاطّلاع على بروتوكول 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() لتحميل عميل واجهة برمجة التطبيقات ووحدات "تسجيل الدخول باستخدام 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() بعد تحميل جميع الوحدات.

إعداد التطبيق المصغّر

أولاً، عليك تهيئة مكتبة العميل من خلال استدعاء gapi.client.init() أو gapi.auth2.init() باستخدام معرّف العميل الذي تم إنشاؤه ونطاق https://www.googleapis.com/auth/cloud_search.query. بعد ذلك، استخدِم الفئتين gapi.cloudsearch.widget.resultscontainer.Builder وgapi.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 لحماية التطبيق.

لمزيد من المعلومات، يُرجى الاطّلاع على استخدام ميزة "تسجيل الدخول باستخدام Google" مع تطبيقات تكنولوجيا المعلومات.

تخصيص الواجهة

يمكنك تغيير مظهر واجهة البحث من خلال مجموعة من الأساليب:

  • تجاوز الأنماط باستخدام CSS
  • تزيين العناصر باستخدام محوّل
  • إنشاء عناصر مخصّصة باستخدام أداة ربط

تجاوز الأنماط باستخدام CSS

تتضمّن أداة البحث CSS خاصًا بها لتصميم عناصر الاقتراحات والنتائج، بالإضافة إلى عناصر التحكّم في تقسيم الصفحات. يمكنك إعادة تصميم هذه العناصر حسب الحاجة.

أثناء التحميل، تحمّل أداة البحث تلقائيًا ورقة الأنماط التلقائية. يحدث ذلك بعد تحميل أوراق الأنماط الخاصة بالتطبيق، ما يؤدي إلى زيادة أولوية القواعد. لضمان أن تحظى أنماطك بالأولوية على الأنماط التلقائية، استخدِم محدّدات العناصر الأصلية لزيادة خصوصية القواعد التلقائية.

على سبيل المثال، لن يكون للقاعدة التالية أي تأثير إذا تم تحميلها في علامة link أو style ثابتة في المستند.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

بدلاً من ذلك، يجب تحديد القاعدة باستخدام المعرّف أو الفئة الخاصة بالحاوية الرئيسية المحدّدة في الصفحة.

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

للاطّلاع على قائمة بفئات الدعم وأمثلة على رمز HTML الذي تنتجه الأداة، يُرجى الرجوع إلى فئات CSS المتوافقة.

تزيين العناصر باستخدام محوّل

لتزيين عنصر قبل عرضه، أنشئ مسجّلاً يستخدم إحدى طرق التزيين، مثل decorateSuggestionElement أو decorateSearchResultElement..

على سبيل المثال، تضيف المحوّلات التالية فئة مخصّصة إلى عناصر الاقتراحات والنتائج.

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

لتسجيل المحوّل عند تهيئة التطبيق المصغّر، استخدِم طريقة setAdapter() الفئة Builder المعنية:

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

قد تعدّل أدوات التزيين سمات العنصر الحاوي بالإضافة إلى أي عناصر فرعية. يمكن إضافة عناصر فرعية أو إزالتها أثناء عملية التزيين. ومع ذلك، إذا كنت ستجري تغييرات هيكلية على العناصر، ننصحك بإنشاء العناصر مباشرةً بدلاً من تزيينها.

إنشاء عناصر مخصّصة باستخدام أداة ربط

لإنشاء عنصر مخصّص لاقتراح أو حاوية لفئات البحث أو نتيجة بحث، أنشئ مسهّلاً وسجِّله، على أن ينفّذ createSuggestionElement أو createFacetResultElement أو createSearchResultElement على التوالي.

توضّح المحوّلات التالية كيفية إنشاء عناصر مخصّصة للاقتراحات ونتائج البحث باستخدام علامات <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;
}

لتسجيل المحوّل عند تهيئة الأداة، استخدِم طريقة setAdapter() لفئة Builder المعنية:

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 لعدة قيود:

  • يجب إرفاق فئة 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 لتعديل الطلبات المُرسَلة إلى واجهة برمجة التطبيقات الخاصة بالبحث قبل تنفيذها.

على سبيل المثال، يعترض المحوّل البرمجي التالي الطلبات لحصر الاستعلامات على مصدر يختاره المستخدم:

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

لتسجيل المحوّل عند تهيئة الأداة، استخدِم طريقة setAdapter() عند إنشاء 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 التالي لعرض مربّع اختيار للفلترة حسب المصادر:

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 في المحوّل.

تثبيت إصدار واجهة برمجة التطبيقات

يستخدم التطبيق المصغّر تلقائيًا أحدث إصدار ثابت من واجهة برمجة التطبيقات. لإصدار محدّد، اضبط مَعلمة الإعداد cloudsearch.config/apiVersion على الإصدار المفضّل قبل بدء التطبيق المصغّر.

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

سيتم ضبط إصدار واجهة برمجة التطبيقات تلقائيًا على 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;