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

Arama widget'ı, web uygulamaları için özelleştirilebilir bir arama arayüzü sağlar. Widget'ın uygulanması için yalnızca az miktarda HTML ve JavaScript gerekir. Ayrıca, kategoriler ve sayfalandırma gibi yaygın arama özelliklerini etkinleştirir. 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 edinmek için Query API ile arama arayüzü oluşturma başlıklı makaleyi inceleyin.

Arama arayüzü oluşturma

Arama arayüzünü oluşturmak için birkaç adım gerekir:

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

Arama uygulamasını yapılandırma

Her arama arayüzünde, Yönetici Konsolu'nda tanımlanmış bir arama uygulaması olmalıdır. Arama uygulaması, sorgu için veri kaynakları, özellikleri ve arama kalitesi ayarları gibi ek bilgiler sağlar.

Arama uygulaması oluşturmak için Özel arama deneyimi oluşturma başlıklı makaleyi inceleyin.

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ırması

Projeyi yapılandırırken:

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

Daha fazla bilgi için İstemci tarafı web uygulamaları için OAuth 2.0 başlıklı makaleyi inceleyin.

HTML işaretleme ekleme

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

  • Arama kutusu için bir input öğesi.
  • Öneri pop-up'ını sabitlemek için kullanılacak bir öğe.
  • Arama sonuçlarını içeren bir öğe.
  • (İsteğe bağlı) Yönleri kontrollerini içeren bir öğe sağlayın.

Aşağıdaki HTML snippet'inde, bağlanacak öğelerin id özellikleriyle tanımlandığı bir arama widget'ının HTML'si gösterilmektedir:

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 aşağıdaki gibi <script> etiketini 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 bir onload geri çağırma işlevi sağlamanız gerekir. Yükleyici hazır olduğunda işlev çağrılır. Yükleyici hazır olduğunda API istemcisini, Google ile oturum açma ve Cloud Search modüllerini yüklemek için gapi.load()'i ç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)
}

initializeApp() işlevi, tüm modüller yüklendikten sonra ç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()'yi ç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 aşağıdaki şekilde tanımlanan iki değişkene referans verilmektedir:

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 sorgu yazmaya başladıklarında oturum açmalarını ve uygulamaya yetki vermelerini ister. Kullanıcılara daha özel bir oturum açma deneyimi sunmak için Web siteleri için Google ile oturum açma'yı kullanabilirsiniz.

Kullanıcıları doğrudan yetkilendirme

Kullanıcının oturum açma durumunu izlemek ve gerektiğinde kullanıcıların oturumunu açmak veya 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özlemlenir ve oturum açma işlemini bir düğme tıklamasıyla başlatmak için GoogleAuth.signIn() yöntemi kullanılı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();
};

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

Kullanıcıların oturumunu otomatik olarak açma

Kuruluşunuzdaki kullanıcılar adına uygulamaya önceden yetki vererek oturum açma deneyimini daha da kolaylaştırabilirsiniz. Bu teknik, uygulamayı korumak için Cloud Identity-Aware Proxy kullanılıyorsa da yararlıdır.

Daha fazla bilgi için BT uygulamalarıyla Google ile oturum açma özelliğini kullanma başlıklı makaleyi inceleyin.

Arayüzü özelleştirme

Arama arayüzünün görünümünü çeşitli teknikleri kullanarak değiştirebilirsiniz:

  • Stilleri CSS ile geçersiz kılma
  • Öğeleri adaptörle süsleme
  • Adaptörle özel öğeler oluşturma

Stilleri CSS ile geçersiz kılma

Arama widget'ı, stil önerisi ve sonuç öğelerinin yanı sıra sayfalandırma denetimleri için kendi CSS'sine sahiptir. Bu öğeleri gerektiği gibi yeniden biçimlendirebilirsiniz.

Arama widget'ı, yükleme sırasında varsayılan stil sayfasını dinamik olarak yükler. Bu durum, uygulama stil sayfaları yüklendikten sonra gerçekleşir ve kuralların önceliğini artırır. Kendi stillerinizin varsayılan stillere göre öncelikli olmasını sağlamak için varsayılan kuralların özgünlüğünü artırmak üzere üst öğe seçicileri kullanın.

Örneğin, aşağıdaki kural dokümanda statik bir link veya style etiketine yüklenirse hiçbir etkisi olmaz.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

Bunun yerine, kuralı sayfada tanımlanan üst öğe kapsayıcının kimliği veya sınıfıyla tanımlayın.

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

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

Öğeleri adaptörle süsleme

Bir öğeyi oluşturmadan önce süslemek için decorateSuggestionElement veya decorateSearchResultElement. gibi süsleme yöntemlerinden birini uygulayan bir bağdaştırıcı oluşturup kaydedin.

Örneğin, aşağıdaki bağdaştırıcılar öneri ve sonuç öğelerine özel bir 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üsleyiciler, kapsayıcı öğesinin yanı sıra tüm alt öğelerin özelliklerini değiştirebilir. Dekorasyon 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ı deneyin.

Adaptörle özel öğeler oluşturma

Öneri, yöne ayırma kapsayıcısı veya arama sonucu için özel öğe oluşturmak üzere sırasıyla createSuggestionElement, createFacetResultElement veya createSearchResultElement öğesini 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 nasıl oluşturulacağını gösterir.

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 yön öğeleri oluşturmak çeşitli kısıtlamalara tabidir:

  • Kullanıcıların bir paketi etkinleştirmek veya devre dışı bırakmak için tıkladığı öğeye cloudsearch_facet_bucket_clickable CSS sınıfını eklemeniz gerekir.
  • Her grubu, CSS sınıfı cloudsearch_facet_bucket_container olan bir kapsayıcı öğeye sarmanız gerekir.
  • Paketleri, yanıtta göründüklerinden farklı bir sırada oluşturamazsınız.

Örneğin, aşağıdaki snippet, onay kutuları yerine bağlantılar kullanarak yönleri 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ştirme

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ı değiştirmesine izin vermek gibi dinamik filtreler veya özellikler uygulamak için arama isteğini bir adaptörle durdurarak arama uygulaması ayarlarını geçersiz kılabilirsiniz.

Arama API'sine gönderilen istekleri yürütmeden önce değiştirmek için interceptSearchRequest yöntemiyle bir bağdaştırı uygulayın.

Örneğin, aşağıdaki bağdaştırıcı, sorguları kullanıcı tarafından seçilen bir kaynağa kısıtlama isteklerini durdurur:

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 öğesini 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();

Kaynaklara göre filtreleme yapmak için bir seçim kutusu görüntülemek üzere aşağıdaki HTML 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 izler, 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, adaptöre interceptSearchResponse uygulayarak arama yanıtını da durdurabilirsiniz.

API sürümünü sabitleme

Widget varsayılan olarak 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 beklenmedik değişiklikler olmasını önlemek için cloudsearch.config/clientVersion yapılandırma parametresini aşağıdaki gibi ayarlayın:

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

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

Arama arayüzünü güvence altına alma

Arama sonuçları son derece hassas bilgiler içeriyor. Web uygulamalarını, özellikle tıklama tuşuyla saldırılara karşı güvence altına almak için en iyi uygulamaları uygulayın.

Daha fazla bilgi için OWASP Guide Project (OWASP Kılavuz Projesi) sayfasını inceleyin.

Hata ayıklamayı etkinleştirme

Arama widget'ı için hata ayıklamayı açmak üzere interceptSearchRequest simgesini kullanın. Örneğin:

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

  return request;