Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Ricerca dinamica con AJAX

Sviluppo di uno script che, contestualmente alla digitazione dei caratteri, dà suggerimenti sui termini contenuti nella cache
Sviluppo di uno script che, contestualmente alla digitazione dei caratteri, dà suggerimenti sui termini contenuti nella cache
Link copiato negli appunti

Nell'articolo delle scorse settimane abbiamo discusso su come sviluppare una cache per l'accesso alla base dati. Abbiamo visto come il suo utilizzo possa migliorare le performance del sistema con dei semplici accorgimenti. In questo articolo prenderemo spunto da quanto prodotto finora per aggiungere un elemento dinamico molto in voga nei siti web che utilizzano tecniche di rappresentazione AJAX.

Probabilmente, navigando per il web, spesso vi è capitato di compilare dei moduli di ricerca che, contestualmente alla digitazione dei caratteri, vi davano dei suggerimenti (sulle ricerche più frequenti, ad esempio, o su quelle recenti). Quello che svilupperemo noi è uno script che in fase di digitazione del testo del modulo di ricerca, vi darà dei suggerimenti su quei termini contenuti nella cache.

Modifiche Lato Server

La classe che gestisce le operazioni sul layer di persistenza, nell'articolo sulle cache, è la classe astratta Database, che viene poi implementata dal proxy (DatabaseProxy) e dalla classe concreta (DatabaseConcrete). La prima modifica che dovremo fare sarà estendere il funzionamento di questo modulo software con l'aggiunta di un metodo che restituisca le keyword di ricerca mantenute in cache.

Per non perdere di vista la struttura progettuale che abbiamo introdotto nello scorso articolo andremo a modificare direttamente la gerarchia presentata (anziché fare operazioni di estensione della gerarchia delle classi), aggiungendo il metodo showKeywords:

Listato 1. Carica una lista di keyword recenti

//Database.java

public abstract Collection<String> showKeywords(String key);
...

Come intuitivamente si può capire, il compito di questo metodo è quello di restituire una collezione di keyword. L'implementazione concreta è appannaggio della classe DatabaseProxy (quella dove risiede concretamente la struttura dati della cache):

Listato 2. Ricerca e restituisce i risultati

//DatabaseProxy.java

//Ricerchiamo tutte le keyword presenti nella cache, che iniziano con la keyword KEY

public Collection<String> showKeywords(String key) {
  Collection<String> toRet=new ArrayList();
  // Recupero tutte le chiavi di ricerca presenti in cache
  Set ks=cache.keySet();
  Iterator<String> it=ks.iterator();
  
  //Iteriamo il set di key, se c'e' match, lo aggiungiamo alla lista di ritorno
  while(it.hasNext()){
    String tmpKey=it.next();
    //lavoriamo su oggetti NON case sensitive
    if (tmpKey.toLowerCase().startsWith(key.toLowerCase()))
      toRet.add(tmpKey);
  }
  return toRet;
}

Utilizzando il metodo keySet() sulla struttura dati abbiamo tutte le chiavi di ricerca contenute in cache. A questo punto controlliamo se tutte queste chiavi di ricerca combaciano con la stringa key, la chiave di ricerca passata come riferimento.

Così, se l'intero set avrà il contenuto {mare, martello, terra, gelato, macchia}, richiamare il metodo passando come riferimento la stringa "mar" restituirà il set {mare, martello}.

È evidente che per poter essere compilata, anche la classe DatabaseConcrete deve implementare il metodo showKeywords (che in realtà potevamo anche implementare come vuoto nella classe astratta, e poi fare l'override solo su quella proxy). Noi lo implementiamo sollevando un'eccezione:

Listato 3. Implementazione di showKeywords

//DatabaseConcrete.java
..//
  //Questa classe non deve eseguire questo metodo

  public Collection<String> showKeywords(String key) {
    throw new UnsupportedOperationException();
  }
..//

Il controller, dovrà dunque mediare l'operazione su questo layer. L'unico controllo che verrà fatto è verificare che l'insieme non sia vuoto, altrimenti restituire il risultato.

Listato 4. Legge una lista di keyword e la restituisce già formattata all'utente

//Controller.java
..//

private void doShowSuggest(HttpServletRequest request, HttpServletResponse response) throws IOException {
  String key=request.getParameter("key");
  if (!key.equals("")){
    Collection<String> list=db.showKeywords(key);
    if (list.isEmpty()){
      response.getWriter().println("Nessun suggerimento");
    }else{
      response.getWriter().println("suggerimenti: ");
      for (String tmp : list){
        response.getWriter().println(tmp+",");
      }
    }
  }
}
..//

Per questioni di semplicità la formattazione viene già definita a questo livello (è hardcoded). Una migliore strategia sarebbe stata quella di fare il forward verso una pagina JSP e lasciare ad essa la funzione di formattazione, in modo, tra le altre cose, di poterla modificare in futuro più semplicemente.

Modifiche Lato Client

Lato server abbiamo tutto quello di cui c'è bisogno; facendo un primo test sulla servlet potremo vedere come risultato una lista di keyword associate ad ogni nostra richiesta. Come abbiamo visto nell'articolo su Ajax e JSP, la potenza di AJAX è la chiamata asincrona di risorse web.

Nel nostro caso, dalla pagina di ricerca effettueremo una chiamata asincrona alla funzione della servlet ogni volta che l'utente digita un nuovo carattere nel modulo di ricerca, mostrando il risultato in un altro elemento della pagina. Per effettuare la chiamata asincrona avremo bisogno di utilizzare uno script Javascript (utilizziamo lo script suggerito nella guida AJAX opportunamente modificato alle nostre esigenze). Creiamo una semplice funzione che richiamerà quella della libreria:

Listato 5. Mostra i suggerimenti nell'elemento SUGGEST

//ajax.js
..//

function showSuggestion(input_value){
  //L'azione che effettua la lettura di dati
  url='./controller?op=showSuggest&key='+input_value
  //Carico il contenuto dell'azione dentro un elemento
  caricaTesto('SUGGEST',url);
}
..//

// accetta una stringa contenente il nome di un file da leggere
function caricaTesto(elemento,nomeFile) {
  ..//
}
..//

La funzione caricaTesto (vedi l'esempio per il codice completo) carica il contenuto dell'url nomeFile all'interno dell'elemento specificato. Quindi, la funzione showSuggestion crea l'url specificando il path della servlet (aggiungendo la keyword digitata) e richiama la funzione sull'elemento "SUGGEST".

La pagina dovrà dunque contenere un elemento il cui id è "SUGGEST" in modo da caricare al suo interno i suggerimenti in maniera dinamica.

Vediamo quindi la pagina di ricerca:

Listato 6. Pagina di ricerca

//result.jsp
..//

<h1>Web Cache</h1>
<hr>
<p>
  <form action="controller" method="post">
    <input type="hidden" name="op" value="search" /><br/>
    Cerca: <input type="text" name="key" onkeyup="javascript:showSuggestion(this.value)" /><br/>
    <input type="submit" /><br/>
    <input name="fc" type="checkbox" value="true" /> (forza la cache)
  </form>
  <div id="SUGGEST" style="font-size:12px;margin-left:250px;">
  
  </div>
</p>
..//

Le modifiche rispetto all'originale sono definite in grassetto, il resto rimane immutato. Sotto il form aggiungiamo l'elemento SUGGEST, che conterrà il risultato dell'elaborazione del suggerimento. Inoltre, siccome vogliamo che l'operazione sia dinamica, ad ogni nuovo carattere, inserito dall'utente durante la ricerca, verrà caricata (asincronamente, utilizzando lo script javascript) la lista di suggerimenti attraverso la funzione onkeyup.

A questo punto è solo una questione di design definire quale sia il miglior modo per mostrare i suggerimenti. Noi ci siamo limitati a mostrarli in maniera orizzontale, voi potreste decidere una visualizzazione verticale, oppure aggiungere eventi su ogni singolo suggerimento (un tooltip ad esempio), oppure un link al cui click ci sia il completamento automatico del modulo di ricerca.

Risultato esecuzione

È bene tenere a mente che un'operazione del genere aumenta in maniera sostanziale l'interazione tra i client ed il server, in quanto ad ogni carattere digitato si genera uno scambio informativo bidirezionale oltre ad un'elaborazione (seppure semplice e su dati presenti in memoria volatile) sul server.

Vediamo con delle immagini un esempio:

Figura 1. Suggerimenti per la ricerca della key "ma"
Test dell'applicazione
Figura 2. Suggerimenti per la ricerca della key "mazz"
Test dell'applicazione con una parola diversa

Ti consigliamo anche