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

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

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

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

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

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

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

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

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

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

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

إعداد مشروع

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

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

لمزيد من المعلومات، يمكنك الاطّلاع على OAuth 2.0 لتطبيق الويب من جهة العميل.

إضافة ترميز HTML

تتطلب الأداة عددًا صغيرًا من HTML لكي تعمل. عليك توفير ما يلي:

  • عنصر input لمربّع البحث.
  • عنصر لربط النافذة المنبثقة للاقتراح.
  • عنصر يتضمّن نتائج البحث
  • (اختياري) قدِّم عنصرًا لتضمين عناصر التحكّم في الواجهات.

يعرض مقتطف HTML التالي رمز HTML لأداة البحث، حيث يتم تحديد العناصر التي سيتم ربطها من خلال السمة id:

العرض/التطبيقات المصغّرة/العامة/مع_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> كما هو موضّح:

العرض/التطبيقات المصغّرة/العامة/مع_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.

العرض/التطبيقات المصغّرة/العامة/مع_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.

يوضّح المثال التالي كيفية إعداد التطبيق المصغّر:

العرض/التطبيقات المصغّرة/العامة/مع_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'
    });
  });
}

يوضّح المثال أعلاه متغيرَين للضبط المحدّدَين:

العرض/التطبيقات المصغّرة/العامة/مع_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() لبدء تسجيل الدخول من خلال زر:

العرض/التطبيقات المصغّرة/العامة/مع_تسجيل الدخول/تطبيق.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 Aware Aware Proxy لحماية التطبيق.

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

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

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

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

إلغاء الأنماط باستخدام CSS

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

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

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

.cloudsearch_suggestion_container {
  font-size: 14px;
}

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

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

للحصول على قائمة بفئات الدعم ومثال HTML الذي تم إنشاؤه بواسطة الأداة، اطّلِع على مرجع فئات CSS المتوافقة.

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

لتزيين عنصر ما قبل العرض، عليك إنشاء محوّل وتنفيذه بطريقة منطبقة، مثل decorateSuggestionElement أو decorateSearchResultElement.

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

مخصّص للعرض المصغّر / التطبيق المصغّر / علني مع withdedeated_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 المعنية:

مخصّص للعرض المصغّر / التطبيق المصغّر / علني مع withdedeated_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>.

العرض/التطبيقات المصغّرة/المتاحة للجميع/باستخدام_مخصّص_عنصر_التطبيق.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 المعنية:

العرض/التطبيقات المصغّرة/المتاحة للجميع/باستخدام_مخصّص_عنصر_التطبيق.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.
  • ولا يمكنك عرض الحِزم بترتيب مختلف عن ظهورها في الردّ.

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

العرض/التطبيقات المصغّرة/المتاحة للجميع/باستخدام_مخصّص_مخصّص/التطبيق
/**
 * 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 قبل التنفيذ.

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

العرض/التطبيقات المصغّرة/المتاحة للجميع/مع_طلب_اعتراض/تطبيقات.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

العرض/التطبيقات المصغّرة/المتاحة للجميع/مع_طلب_اعتراض/تطبيقات.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 التالي لعرض مربّع اختيار للفلترة حسب المصادر:

العرض/التطبيقات المصغّرة/المتاحة للجميع/مع_طلب_اعتراض/فهرس.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>

يرصد الرمز التالي التغيير ويحدِّد الاختيار ويعيد تنفيذ طلب البحث إذا لزم الأمر.

العرض/التطبيقات المصغّرة/المتاحة للجميع/مع_طلب_اعتراض/تطبيقات.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 على النسخة المفضّلة قبل إعداد الأداة.

العرض/التطبيقات المصغّرة/العامة/الأساسية/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;