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

يوفّر تطبيق البحث المصغّر واجهة بحث قابلة للتخصيص لتطبيقات الويب. لا تتطلّب الأداة سوى جزء صغير من رمز HTML وJavaScript لتنفيذها وتفعيل ميزات البحث الشائعة، مثل السمات وتقسيم الصفحات. يمكنك أيضًا تخصيص أجزاء من الواجهة باستخدام 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 Sign-in" و"Cloud Search" و"Google API Client Library".

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() بعد loadingتحميل كل الوحدات.

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

أولاً، عليك إعداد مكتبة العملاء من خلال استدعاء 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 الخاص بها لتنسيق عناصر الاقتراحات والنتائج، بالإضافة إلى عناصر التحكّم في تقسيم الصفحات. ويمكنك إعادة تصميم هذه العناصر حسب الحاجة.

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

على سبيل المثال، لن يكون للقاعدة التالية أي تأثير في حال تحميلها في علامة static 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 لتفعيل تصحيح الأخطاء لأداة البحث المصغّرة. على سبيل المثال:

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

  return request;