Creare un'interfaccia di ricerca con il widget Ricerca

Il widget di ricerca fornisce un'interfaccia di ricerca personalizzabile per le applicazioni web. Il widget richiede solo una piccola quantità di codice HTML e JavaScript per l'implementazione e consente di utilizzare funzionalità di ricerca comuni come i filtri e la paginazione. Puoi anche personalizzare parti dell'interfaccia con CSS e JavaScript.

Se hai bisogno di una maggiore flessibilità rispetto a quella offerta dal widget, valuta la possibilità di utilizzare l'API Query. Per informazioni sulla creazione di un'interfaccia di ricerca con l'API Query, consulta la sezione Creare un'interfaccia di ricerca con l'API Query.

Creare un'interfaccia di ricerca

La creazione dell'interfaccia di ricerca richiede diversi passaggi:

  1. Configurare un'applicazione di ricerca
  2. Generare un ID client per l'applicazione
  3. Aggiungere il markup HTML per la casella di ricerca e i risultati
  4. Caricare il widget sulla pagina
  5. Inizializzare il widget

Configurare un'applicazione di ricerca

Ogni interfaccia di ricerca deve avere un'applicazione di ricerca definita nella Console di amministrazione. L'applicazione di ricerca fornisce ulteriori informazioni per la query, ad esempio le origini dati, i facet e le impostazioni di qualità della ricerca.

Per creare un'applicazione di ricerca, consulta Crea un'esperienza di ricerca personalizzata.

Generare un ID client per l'applicazione

Oltre ai passaggi descritti in Configurare l'accesso all'API Google Cloud Search, devi anche generare un ID client per l'applicazione web.

Configurare un progetto

Quando configuri il progetto:

  • Seleziona il tipo di client Browser web.
  • Fornisci l'URI di origine della tua app.
  • Nota dell'ID client creato. Per completare i passaggi successivi, devi disporre dell'ID client. Il client secret non è obbligatorio per il widget.

Per ulteriori informazioni, consulta l'articolo OAuth 2.0 per applicazioni web lato client.

Aggiungere il markup HTML

Per funzionare, il widget richiede una piccola quantità di HTML. Devi fornire:

  • Un elemento input per la casella di ricerca.
  • Un elemento a cui ancorare il popup dei suggerimenti.
  • Un elemento per contenere i risultati di ricerca.
  • (Facoltativo) Fornisci un elemento per contenere i controlli delle sfaccettature.

Il seguente snippet HTML mostra il codice HTML di un widget di ricerca, in cui gli elementi da associare sono identificati dall'attributo 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>

Caricare il widget

Il widget viene caricato dinamicamente tramite uno script di caricamento. Per includere il caricatore, utilizza il tag <script> come mostrato:

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>

Devi fornire un callback onload nel tag script. La funzione viene chiamata quando il caricatore è pronto. Quando il caricatore è pronto, continua a caricare il widget chiamando gapi.load() per caricare il client API, l'accesso con Google e i moduli di Cloud Search.

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

La funzione initializeApp() viene chiamata dopo il caricamento di tutti i moduli.

Inizializzare il widget

Innanzitutto, inizializza la libreria client chiamando gapi.client.init() o gapi.auth2.init() con l'ID client generato e l'ambito https://www.googleapis.com/auth/cloud_search.query. Poi, utilizza le classi gapi.cloudsearch.widget.resultscontainer.Builder e gapi.cloudsearch.widget.searchbox.Builder per configurare il widget e associarlo agli elementi HTML.

L'esempio seguente mostra come inizializzare il widget:

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

L'esempio precedente fa riferimento a due variabili per la configurazione definite come:

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

Personalizzare l'esperienza di accesso

Per impostazione predefinita, il widget chiede agli utenti di accedere e autorizzare l'app quando iniziano a digitare una query. Puoi utilizzare Google Sign-in per i siti web per offrire agli utenti un'esperienza di accesso più personalizzata.

Autorizzare gli utenti direttamente

Utilizza Accedi con Google per monitorare lo stato di accesso dell'utente e accedere o uscire dagli utenti in base alle necessità. Ad esempio, il seguente esempio osserva lo stato isSignedIn per monitorare le modifiche di accesso e utilizza il metodo GoogleAuth.signIn() per avviare l'accesso da un clic su un pulsante:

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

Per ulteriori dettagli, vedi Accedi con Google.

Accedere automaticamente agli utenti

Puoi semplificare ulteriormente l'esperienza di accesso preautorizzando l'applicazione per conto degli utenti della tua organizzazione. Questa tecnica è utile anche se utilizzi Cloud Identity Aware Proxy per proteggere l'applicazione.

Per ulteriori informazioni, vedi Utilizzare Accedi con Google con le app IT.

Personalizzare l'interfaccia

Puoi modificare l'aspetto dell'interfaccia di ricerca utilizzando una combinazione di tecniche:

  • Sovrascrivere gli stili con CSS
  • Decorare gli elementi con un adattatore
  • Creare elementi personalizzati con un adattatore

Sovrascrivere gli stili con CSS

Il widget di ricerca è dotato di un proprio CSS per lo stile degli elementi di suggerimento e dei risultati nonché dei controlli di paginazione. Puoi modificare lo stile di questi elementi in base alle tue esigenze.

Durante il caricamento, il widget di ricerca carica dinamicamente il foglio di stile predefinito. Ciò si verifica dopo il caricamento dei fogli di stile dell'applicazione, aumentando la priorità delle regole. Per assicurarti che i tuoi stili abbiano la precedenza su quelli predefiniti, utilizza i selettori di antenati per aumentare la specificità delle regole predefinite.

Ad esempio, la seguente regola non ha effetto se caricata in un tag link o style statico nel documento.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

Qualifica invece la regola con l'ID o la classe del contenitore predecessore dichiarato nella pagina.

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

Per un elenco delle classi supportate e dell'HTML di esempio prodotto dal widget, consulta il riferimento Classi CSS supportate.

Decorare gli elementi con un adattatore

Per decorare un elemento prima del rendering, crea e registra un adattatore che implementi uno dei metodi di decorazione, ad esempio decorateSuggestionElement o decorateSearchResultElement..

Ad esempio, i seguenti adattatori aggiungono una classe personalizzata agli elementi di suggerimento e risultato.

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

Per registrare l'adattatore durante l'inizializzazione del widget, utilizza il metodo setAdapter() della rispettiva classe 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();

I decoratori possono modificare gli attributi dell'elemento contenitore e di tutti gli elementi secondari. Gli elementi secondari possono essere aggiunti o rimossi durante la decorazione. Tuttavia, se apporti modifiche strutturali agli elementi, valuta la possibilità di crearli direttamente anziché decorarli.

Creare elementi personalizzati con un adattatore

Per creare un elemento personalizzato per un suggerimento, un contenitore di sfaccettature o un risultato di ricerca, crea e registra un adattatore che implementi createSuggestionElement, createFacetResultElement o createSearchResultElement rispettivamente.

I seguenti adattatori illustrano la creazione di elementi personalizzati di suggerimenti e risultati di ricerca utilizzando i tag 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;
}

Per registrare l'adattatore durante l'inizializzazione del widget, utilizza il metodo setAdapter() della rispettiva classe 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();

La creazione di elementi di sfaccettatura personalizzati con createFacetResultElement è soggetta a diverse limitazioni:

  • Devi collegare la classe CSS cloudsearch_facet_bucket_clickable all'elemento su cui gli utenti fanno clic per attivare/disattivare un bucket.
  • Devi racchiudere ogni bucket in un elemento contenitore con la classe CSS cloudsearch_facet_bucket_container.
  • Non puoi eseguire il rendering dei bucket in un ordine diverso da quello in cui vengono visualizzati nella risposta.

Ad esempio, il seguente snippet esegue il rendering delle sfaccettature utilizzando i link anziché le caselle di controllo.

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

Personalizzare il comportamento della ricerca

Le impostazioni dell'applicazione di ricerca rappresentano la configurazione predefinita per un'interfaccia di ricerca e sono statiche. Per implementare filtri o facet dinamici, ad esempio per consentire agli utenti di attivare/disattivare le origini dati, puoi ignorare le impostazioni dell'applicazione di ricerca intercettando la richiesta di ricerca con un adattatore.

Implementa un adattatore con il metodo interceptSearchRequest per modificare le richieste effettuate all'API Search prima dell'esecuzione.

Ad esempio, il seguente adattatore intercetta le richieste per limitare le query a una sorgente selezionata dall'utente:

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

Per registrare l'adattatore durante l'inizializzazione del widget, utilizza il metodo setAdapter() durante la creazione di 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();

Il seguente codice HTML viene utilizzato per visualizzare una casella di selezione per il filtro in base alle origini:

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>

Il codice seguente rileva la modifica, imposta la selezione e riesegue la query, se necessario.

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

Puoi anche intercettare la risposta alla ricerca implementando interceptSearchResponse nell'adattatore.

Fissa la versione dell'API

Per impostazione predefinita, il widget utilizza l'ultima versione stabile dell'API. Per bloccare una versione specifica, imposta il parametro di configurazione cloudsearch.config/apiVersion sulla versione preferita prima di inizializzare il widget.

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

Se non è impostata o è impostata su un valore non valido, la versione dell'API verrà impostata su 1.0 per impostazione predefinita.

Bloccare la versione del widget

Per evitare modifiche impreviste alle interfacce di ricerca, imposta il parametro di configurazione cloudsearch.config/clientVersion come mostrato di seguito:

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

Se non è impostata o è impostata su un valore non valido, la versione del widget sarà 1.0 per impostazione predefinita.

Proteggere l'interfaccia di ricerca

I risultati di ricerca contengono informazioni altamente sensibili. Segui le best practice per proteggere le applicazioni web, in particolare dagli attacchi di clickjacking.

Per ulteriori informazioni, consulta il progetto OWASP Guide.

Attivare il debug

Utilizza interceptSearchRequest per attivare il debug per il widget di ricerca. Ad esempio:

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

  return request;