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

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

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

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

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

  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 لحماية التطبيق.

لمزيد من المعلومات، يُرجى الاطّلاع على استخدام "تسجيل الدخول بحساب 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 بالعنصر الذي ينقر عليه المستخدمون لتبديل مجموعة.
  • يجب التفاف كل حزمة في عنصر رئيسي باستخدام الفئة cloudsearch_facet_bucket_container في CSS.
  • لا يمكنك عرض الحِزم بترتيب مختلف عن الذي تظهر في الرد.

على سبيل المثال، يعرض المقتطف التالي الواجهات باستخدام الروابط بدلاً من مربعات الاختيار.

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;