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

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

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

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

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

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

ضبط تطبيق بحث

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

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

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

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

إعداد مشروع

عند ضبط المشروع:

  • اختَر نوع العميل متصفِّح الويب.
  • قدِّم عنوان 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

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

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

على سبيل المثال، ليس للقاعدة التالية أي تأثير إذا تم تحميلها في علامة 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 بشكل متكرر.

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

لتسجيل المحوِّل عند إعداد الأداة، استخدِم الطريقة 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 لتعديل الطلبات التي تم إرسالها إلى 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;
}

لتسجيل المحوِّل عند إعداد التطبيق المصغّر، استخدِم الإجراء 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 لتفعيل تصحيح الأخطاء في تطبيق "بحث Google" المصغّر. مثال:

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

  return request;