Arama widget'ıyla arama arayüzü oluşturma

Arama widget'ı, web uygulamaları için özelleştirilebilir arama arayüzü sunar. Widget, özellikler ve sayfalandırma gibi yaygın arama özelliklerini uygulamak ve etkinleştirmek için sadece az miktarda HTML ve JavaScript gerektirir. Ayrıca arayüzün bölümlerini CSS ve JavaScript ile de özelleştirebilirsiniz.

Widget'ın sunduğundan daha fazla esnekliğe ihtiyacınız varsa Query API'yi kullanabilirsiniz. Query API ile arama arayüzü oluşturma hakkında bilgi için Query API ile arama arayüzü oluşturma bölümüne bakın.

Arama arayüzü oluşturma

Arama arayüzünü oluşturmak birkaç adım gerektirir:

  1. Bir arama uygulamasını yapılandırın
  2. Uygulama için istemci kimliği oluşturma
  3. Arama kutusu ve sonuçlar için HTML işaretlemesi ekleme
  4. Widget'ı sayfaya yükleyin
  5. Widget'ı başlatma

Bir arama uygulamasını yapılandırın

Her arama arayüzünün Yönetici Konsolu'nda tanımlanmış bir arama uygulaması olması gerekir. Arama uygulaması, sorgu için veri kaynakları, özellikler ve arama kalitesi ayarları gibi ek bilgiler sağlar.

Bir arama uygulaması oluşturmak için Özel arama deneyimi oluşturma başlıklı makaleye bakın.

Uygulama için istemci kimliği oluşturma

Google Cloud Search API'ye erişimi yapılandırma bölümündeki adımlara ek olarak, web uygulaması için bir istemci kimliği de oluşturmanız gerekir.

Proje yapılandırma

Projeyi yapılandırdığınızda:

  • Web tarayıcısı istemci türünü seçin.
  • Uygulamanızın kaynak URI'sını sağlayın.
  • Oluşturulan istemci kimliğinin notu. Sonraki adımları tamamlamak için istemci kimliğine ihtiyacınız olacak. Widget için istemci gizli anahtarı gerekli değildir.

Daha fazla bilgi için İstemci Tarafı Web Uygulaması için OAuth 2.0 bölümüne bakın.

HTML işaretlemesi ekleme

Widget'ın çalışması için az miktarda HTML gerekir. Aşağıdakileri sağlamanız gerekir:

  • Arama kutusu için bir input öğesi.
  • Öneri pop-up'ının sabitleneceği bir öğe.
  • Arama sonuçlarını içeren bir öğe.
  • (İsteğe bağlı) Özellik denetimlerini içerecek bir öğe sağlayın.

Aşağıdaki HTML snippet'i bir arama widget'ının HTML'sini gösterir. Bu widget, bağlanacak öğeler id özelliğiyle tanımlanır:

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>

Widget'ı yükleme

Widget, bir yükleyici komut dosyası aracılığıyla dinamik olarak yüklenir. Yükleyiciyi dahil etmek için <script> etiketini aşağıda gösterildiği gibi kullanın:

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>

Komut dosyası etiketinde onload geri çağırması sağlamanız gerekir. İşlev, yükleyici hazır olduğunda çağrılır. Yükleyici hazır olduğunda API istemcisi, Google ile Oturum Açma ve Cloud Search modüllerini yüklemek için gapi.load() yöntemini çağırarak widget'ı yüklemeye devam edin.

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)
}

Tüm modüller yüklendikten sonra initializeApp() işlevi çağrılır.

Widget'ı başlatma

Öncelikle, oluşturulan istemci kimliğiniz ve https://www.googleapis.com/auth/cloud_search.query kapsamıyla gapi.client.init() veya gapi.auth2.init() yöntemini çağırarak istemci kitaplığını başlatın. Ardından, widget'ı yapılandırmak ve HTML öğelerinize bağlamak için gapi.cloudsearch.widget.resultscontainer.Builder ve gapi.cloudsearch.widget.searchbox.Builder sınıflarını kullanın.

Aşağıdaki örnekte widget'ın nasıl başlatılacağı gösterilmektedir:

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'
    });
  });
}

Yukarıdaki örnekte, yapılandırma için şu şekilde tanımlanan iki değişkene referansta bulunulmuştur:

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/...";

Oturum açma deneyimini özelleştirme

Widget, varsayılan olarak kullanıcılardan oturum açmalarını ve sorgu yazmaya başladıklarında uygulamayı yetkilendirmelerini ister. Kullanıcılara daha özel bir oturum açma deneyimi sunmak için Web Siteleri için Google ile Oturum Açma özelliğini kullanabilirsiniz.

Kullanıcıları doğrudan yetkilendirin

Kullanıcının oturum açma durumunu izlemek ve gerektiğinde oturum açmak veya oturumu kapatmak için Google ile oturum açma özelliğini kullanın. Örneğin, aşağıdaki örnekte oturum açma değişikliklerini izlemek için isSignedIn durumu görülmektedir ve bir düğme tıklamasından oturum açma işlemi başlatmak için GoogleAuth.signIn() yöntemi kullanılmaktadır:

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();
};

Ayrıntılı bilgi için Google ile oturum açma başlıklı makaleyi inceleyin.

Kullanıcıların otomatik olarak oturum açmasını sağlama

Uygulamayı kuruluşunuzdaki kullanıcılar adına önceden yetkilendirerek oturum açma deneyimini daha da basitleştirebilirsiniz. Bu teknik, uygulamayı korumak için Cloud Identity Aware Proxy kullanılıyorsa de yararlıdır.

Daha fazla bilgi için Google ile Oturum Açma özelliğini BT Uygulamalarıyla Kullanma başlıklı makaleye göz atın.

Arayüzü özelleştirme

Çeşitli teknikler kullanarak arama arayüzünün görünümünü değiştirebilirsiniz:

  • Stilleri CSS ile geçersiz kılma
  • Öğeleri bir adaptörle süsleyin
  • Bağdaştırıcı kullanarak özel öğeler oluşturma

Stilleri CSS ile geçersiz kılma

Arama widget'ında, öneri ve sonuç öğelerinin stilini belirlemek için kendi CSS'sinin yanı sıra sayfalara ayırma denetimleri bulunur. Bu öğeleri gerektiği gibi yeniden biçimlendirebilirsiniz.

Yükleme sırasında, arama widget'ı varsayılan stil sayfasını dinamik olarak yükler. Bu durum, uygulama stil sayfaları yüklendikten sonra ortaya çıkar ve kuralların önceliği artar. Kendi stillerinizin varsayılan stillere göre öncelikli olmasını sağlamak için varsayılan kuralların belirginliğini artırmak amacıyla üst öğe seçicileri kullanın.

Örneğin, aşağıdaki kuralın dokümandaki statik link veya style etiketine yüklenmesi durumunda herhangi bir etkisi olmaz.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

Bunun yerine, kuralı sayfada belirtilen üst kapsayıcının kimliği veya sınıfıyla niteleyin.

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

Widget tarafından oluşturulan destek sınıflarının ve örnek HTML'nin listesi için Desteklenen CSS sınıfları referansına bakın.

Öğeleri bir adaptörle süsleyin

Bir öğeyi oluşturmadan önce süslemek için decorateSuggestionElement veya decorateSearchResultElement. gibi dekorasyon yöntemlerinden birini uygulayan bir bağdaştırıcı oluşturun ve yeniden kullanın.

Örneğin, aşağıdaki bağdaştırıcılar öneri ve sonuç öğelerine bir özel sınıf ekler.

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');
}

Widget'ı başlatırken bağdaştırıcıyı kaydetmek için ilgili Builder sınıfının setAdapter() yöntemini kullanın:

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();

Süslemeler, kapsayıcı öğenin özelliklerini ve alt öğeleri değiştirebilir. Süsleme sırasında alt öğeler eklenebilir veya kaldırılabilir. Ancak öğelerde yapısal değişiklikler yapıyorsanız öğeleri süslemek yerine doğrudan oluşturmayı düşünün.

Bağdaştırıcı kullanarak özel öğeler oluşturma

Bir öneri, façeta kapsayıcısı veya arama sonucu için özel öğe oluşturmak istiyorsanız createSuggestionElement, createFacetResultElement veya createSearchResultElement uygulamalarını arka arkaya uygulayan bir bağdaştırıcı oluşturup kaydedin.

Aşağıdaki bağdaştırıcılar, HTML <template> etiketleri kullanılarak özel öneri ve arama sonucu öğelerinin oluşturulmasını göstermektedir.

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;
}

Widget'ı başlatırken bağdaştırıcıyı kaydetmek için ilgili Builder sınıfının setAdapter() yöntemini kullanın:

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 ile özel façeta öğeleri oluşturmak çeşitli kısıtlamalara tabidir:

  • cloudsearch_facet_bucket_clickable CSS sınıfını, kullanıcıların bir paketi açıp kapatmak için tıkladığı öğeye eklemeniz gerekir.
  • Her paketi cloudsearch_facet_bucket_container CSS sınıfıyla bir kapsayıcı öğenin içine sarmalamanız gerekir.
  • Paketleri yanıtta göründüklerinden farklı bir sırada görüntüleyemezsiniz.

Örneğin, aşağıdaki snippet, özellikleri onay kutuları yerine bağlantılar kullanarak oluşturur.

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) {
  // ...
}

Arama davranışını özelleştirin

Arama uygulaması ayarları, bir arama arayüzünün varsayılan yapılandırmasını temsil eder ve statiktir. Kullanıcıların veri kaynaklarını açıp kapatmalarına izin vermek gibi dinamik filtreler veya özellikler uygulamak için arama isteğini bir bağdaştırıcıyla engelleyerek arama uygulaması ayarlarını geçersiz kılabilirsiniz.

Yürütmeden önce search API'ye yapılan istekleri değiştirmek için interceptSearchRequest yöntemiyle bir bağdaştırıcı uygulayın.

Örneğin, aşağıdaki bağdaştırıcı, sorguları kullanıcı tarafından seçilen bir kaynakla kısıtlamak için yapılan isteklere müdahale eder:

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;
}

Widget'ı başlatırken bağdaştırıcıyı kaydetmek için ResultsContainer aracını oluştururken setAdapter() yöntemini kullanın.

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();

Aşağıdaki HTML, kaynaklara göre filtreleme yapmak için bir seçim kutusu görüntülemek için kullanılır:

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>

Aşağıdaki kod değişikliği dinler, seçimi ayarlar ve gerekirse sorguyu yeniden yürütür.

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);
  }
}

Ayrıca bağdaştırıcıya interceptSearchResponse uygulayarak arama yanıtına müdahale edebilirsiniz.

API sürümünü sabitleme

Varsayılan olarak widget, API'nin en son kararlı sürümünü kullanır. Belirli bir sürümü kilitlemek için widget'ı başlatmadan önce cloudsearch.config/apiVersion yapılandırma parametresini tercih edilen sürüme ayarlayın.

serving/widget/public/basic/app.js
gapi.config.update('cloudsearch.config/apiVersion', 'v1');

API sürümü ayarlanmazsa veya geçersiz bir değere ayarlanırsa varsayılan olarak 1.0 olur.

Widget sürümünü sabitleme

Arama arayüzlerinde beklenmeyen değişiklikler olmasını önlemek için cloudsearch.config/clientVersion yapılandırma parametresini aşağıda gösterildiği gibi ayarlayın:

gapi.config.update('cloudsearch.config/clientVersion', 1.1);

Ayarlanmazsa veya geçersiz bir değere ayarlanırsa widget sürümü varsayılan olarak 1.0 olur.

Arama arayüzünün güvenliğini sağlama

Arama sonuçları son derece hassas bilgiler içeriyor. Özellikle tıklama korsanlığı saldırılarına karşı web uygulamalarının güvenliğini sağlamaya yönelik en iyi uygulamaları takip edin.

Daha fazla bilgi için OWASP Kılavuzu Projesi'ne bakın

Hata ayıklamayı etkinleştir

Arama widget'ında hata ayıklamayı etkinleştirmek için interceptSearchRequest aracını kullanın. Örneğin:

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

  return request;