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

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

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

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

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

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

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

تهيئة تطبيق بحث

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

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

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

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

إعداد مشروع

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

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

للحصول على معلومات إضافية، يُرجى الاطِّلاع على OAuth 2.0 لتطبيق الويب من جهة العميل.

إضافة ترميز HTML

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

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

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

serve/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> كما هو موضح:

serve/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.

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

serve/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..

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

serve/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 المعنيّة:

serve/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>.

serve/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 المعنية:

serve/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.
  • لا يمكنك عرض الحزم بترتيب مختلف عما تراه في الاستجابة.

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

serve/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 لتعديل الطلبات المقدّمة إلى واجهة برمجة تطبيقات البحث قبل التنفيذ.

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

serve/widget/public/with_request_disputeor/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

serve/widget/public/with_request_disputeor/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 التالي لعرض مربّع اختيار للفلترة حسب المصادر:

serve/widget/public/with_request_disputeor/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>

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

serve/widget/public/with_request_disputeor/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 على الإصدار المفضّل قبل إعداد الأداة.

serve/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;