Questa è la traduzione dell'articolo Sitewide Search On A Shoe String di Christian Heilmann pubblicato originariamente su 24 Ways il 4 Dicembre 2008.
Una delle domande che più mi venivano rivolte quando realizzavo siti per le piccole aziende era se potevo creare un motore di ricerca per il sito. I visitatori dovevano essere in grado di fare ricerche solo su quel sito e trovare le cose che loro interessavano senza che il gestore del sito dovesse preoccuparsi di aggiungere a mano sulle varie pagine link verso "articoli correlati" o "articoli in evidenza".
All'epoca le cose non erano davvero semplici: bisognava scriversi da soli un tool per lo scraping, usare ht://dig o affidarsi a servizi a pagamento come quelli offerti da Yahoo, Altavista e successivamente Google. Nel primo caso bisognava ingoiare l'amara pillola dell'indicizzazione dei contenuti presenti sul sito e della loro archiviazione in un database per un accesso veloce, nel secondo caso ad essere colpito era il portafoglio.
I tempi sono cambiati, e oggi potete ottenere le stesse funzionalità gratuitamente usando il servizio di Yahoo chiamato BOSS (Build your own search service). La cosa più interessante di BOSS è che consente di usare un grandissimo numero di accessi al giorno e che è possibile gestire i dati restituiti come output in qualunque formato desideriate. Un'altra caratteristica valida è che tra le opzioni di output offre anche JSON-P, il che rende possibile usarlo senza alcun componente lato server!
Iniziamo con il form HTML
Per aggiungere un servizio di ricerca al vostro sito, potete iniziare con un semplice modulo HTML che potete usare senza Javascript. La maggior parte dei motori di ricerca vi consentono di filtrare i risultati in base al dominio. In questo caso effettueremo ricerche solo sul sito "bbc.co.uk". Se usate Yahoo come ricerca standard, il form potrebbe essere così:
<form id="customsearch" action="http://search.yahoo.com/search"> <div> <label for="p">Search this site:</label> <input type="text" name="p" id="term"> <input type="hidden" name="vs" id="site" value="bbc.co.uk"> <input type="submit" value="go"> </div> </form>
L'equivalente usando Google sarà:
<form id="customsearch" action="http://www.google.co.uk/search"> <div> <label for="p">Search this site:</label> <input type="text" name="as_q" id="term"> <input type="hidden" name="as_sitesearch" id="site" value="bbc.co.uk"> <input type="submit" value="go"> </div> </form>
In ogni caso assicuratevi di usare l'ID term
per il termine usato nella ricerca dal momento che proprio term
useremo per il nostro script. Per rendere le cose più semplici, usate anche un ID chiamato customsearch
sul form.
Per usare BOSS, poi, dovreste ottenere da Yahoo una API per sviluppatori e sostituirla a quella usata nelle demo che vedremo. C'è un tracking dei click sui risultati della ricerca per verificare quanto successo ha l'applicazione, così dovreste usare la vostra API.
Aggiungere la magia di BOSS
BOSS è un API REST. Significa che potete usarlo in qualunque richiesta HTTP o in un browser semplicemente aggiungendo i parametri giusti all'URL. Diciamo, per esempio, di voler cercare la parola "christmas" sul sito "bbc.co.uk". Tutto quello che dobbiamo fare è aprire il seguente URL:
Provatelo cliccandoci sopra: vedrete i risultati in formato XML. Noi però non vogliamo XML, per cui eliminiamo il parametro format=xml
e otteniamo il risultato nel formato JSON:
JSON ha molto più senso quando potete inviare l'output ad una funzione e usarlo immediatamente. Perché ciò avvenga tutto quello che dovrete fare è aggiungere un parametro callback e il JSON sarà racchiuso in una chiamata di funzione. Diciamo per esempio che vogliamo richiamare la funzione SITESEARCH.found ()
quando i dati sono ricevuti. Possiamo fare così:
Potete usare immediatamente tutto ciò in uno nodo script se volete. Il codice seguente mostra il numero totale di risultati trovati per il termine "christmas" sul sito "bbc.co.uk" sotto forma di alert:
<script type="text/javascript"> var SITESEARCH = {}; SITESEARCH.found = function(o){ alert(o.ysearchresponse.totalhits); } </script> <script type="text/javascript" src="http://boss.yahooapis.com/ysearch/web/v1/christmas?sites=bbc.co.uk&callback=SITESEARCH.found&appid=Kzv_lcHV34HIybw0GjVkQNnw4AEXeyJ9Rb1gCZSGxSRNrcif_HdMT9qTE1y9LdI-"> </script>
Il nostro esempio, tuttavia, sarà un po' più utile di questo.
Migliorare il form di ricerca
Ecco lo script che migliora il form di ricerca per mostrare i risultati sotto di esso (demo):
SITESEARCH = function(){ var config = { IDs:{ searchForm:'customsearch', term:'term', site:'site' }, loading:'Loading results...', noresults:'No results found.', appID:'YOUR-APP-ID', results:20 }; var form; var out; function init(){ if(config.appID === 'YOUR-APP-ID'){ alert('Please get a real application ID!'); } else { form = document.getElementById(config.IDs.searchForm); if(form){ form.onsubmit = function(){ var site = document.getElementById(config.IDs.site).value; var term = document.getElementById(config.IDs.term).value; if(typeof site === 'string' && typeof term === 'string'){ if(typeof out !== 'undefined'){ out.parentNode.removeChild(out); } out = document.createElement('p'); out.appendChild(document.createTextNode(config.loading)); form.appendChild(out); var APIurl = 'http://boss.yahooapis.com/ysearch/web/v1/' + term + '?callback=SITESEARCH.found&sites=' + site + '&count=' + config.results + '&appid=' + config.appID; var s = document.createElement('script'); s.setAttribute('src',APIurl); s.setAttribute('type','text/javascript'); document.getElementsByTagName('head')[0].appendChild(s); return false; } }; } } }; function found(o){ var list = document.createElement('ul'); var results = o.ysearchresponse.resultset_web; if(results){ var item,link,description; for(var i=0,j=results.length;i<j;i++){ item = document.createElement('li'); link = document.createElement('a'); link.setAttribute('href',results[i].clickurl); link.innerHTML = results[i].title; item.appendChild(link); description = document.createElement('p'); description.innerHTML = results[i]['abstract']; item.appendChild(description); list.appendChild(item); } } else { list = document.createElement('p'); list.appendChild(document.createTextNode(config.noresults)); } form.replaceChild(list,out); out = list; }; return{ config:config, init:init, found:found }; }();
Non fatevi intimorire. Analizziamo il codice un po' alla volta.
Iniziamo creando un modulo chiamato SITESEARCH
e gli assegniamo un oggetto di configurazione:
SITESEARCH = function(){ var config = { IDs:{ searchForm:'customsearch', term:'term', site:'site' }, loading:'Loading results...', appID:'YOUR-APP-ID', results:20 }
Gli oggetti di configurazione sono una grande idea per rendere il codice più semplice da modificare. In questo caso potete definire diversi ID oltre a quelli visti in precedenza (un messaggio da mostrare mentre i risultati sono caricati, uno per quando non ci sono risultati, l'ID dell'applicazione, il numero di risultati che sarà mostrato).
Nota: ribadisco che dovrete il parametro "YOUR-APP-ID" con quello reale ottenuto da BOSS.
var form; var out; function init(){ if(config.appID === 'YOUR-APP-ID'){ alert('Please get a real application ID!'); } else {
Definiamo form
e out
come variabili per essere sicuri che tutti i metodi nel modulo abbiamo accesso ad esse. Controlliamo poi se c'è un ID di applicazione reale definito. Se non ci fosse lo script non funziona.
form = document.getElementById(config.IDs.searchForm); if(form){ form.onsubmit = function(){ var site = document.getElementById(config.IDs.site).value; var term = document.getElementById(config.IDs.term).value; if(typeof site === 'string' && typeof term === 'string'){
Se abbiamo successo con l'ID di applicazione, controlliamo che il form con l'ID fornito esista e applichiamo un evento onsubmit
. La prima cosa che otteniamo sono i valori del sito su cui vogliamo effettuare la ricerca e il termine inserito nel campo di ricerca: verifichiamo che si tratti di stringhe.
if(typeof out !== 'undefined'){ out.parentNode.removeChild(out); } out = document.createElement('p'); out.appendChild(document.createTextNode(config.loading)); form.appendChild(out);
Se sono entrambi stringhe, verifichiamo se out
è 'non definito'. Creeremo un messaggio di caricamento e di seguito la lista dei risultati della ricerca che andremo ad assegnare a questa variabile. Così, se out
è definito, sarà una vecchia versione della ricerca e abbiamo dunque bisogno di rimuoverla.
Creiamo poi un paragrafo con il messaggio di caricamento e lo appendiamo al form.
var APIurl = 'http://boss.yahooapis.com/ysearch/web/v1/' + term + '?callback=SITESEARCH.found&sites=' + site + '&count=' + config.results + '&appid=' + config.appID; var s = document.createElement('script'); s.setAttribute('src',APIurl); s.setAttribute('type','text/javascript'); document.getElementsByTagName('head')[0].appendChild(s); return false; } }; } } };
Ora è tempo di chiamare le API di BOSS assemblando l'URL REST corretto, creando un nodo di tipo script e aggiungengendolo alla sezione head del documento. Usiamo false
per essere sicuri che il form non venga reinviato poiché vogliamo restare sulla pagina.
Notate che stiamo usando SITESEARCH.found
come metodo di callback: significa che dobbiamo definire quest'ultimo per gestire i dati restituiti dall'API.
function found(o){ var list = document.createElement('ul'); var results = o.ysearchresponse.resultset_web; if(results){ var item,link,description;
Creiamo una nuova lista e poi otteniamo l'array resultset_web
dai dati restituiti dalla chiamata all'API. Se non vengono restituiti risultati, l'array non esisterà (ecco perché dovremo verificare che ci sia). Fatto tutto ciò, possiamo definire tre variabili per conservare il titolo dell'item, il link e la descrizione del link.
for(var i=0,j=results.length;i<j;i++){ item = document.createElement('li'); link = document.createElement('a'); link.setAttribute('href',results[i].clickurl); link.innerHTML = results[i].title; item.appendChild(link); description = document.createElement('p'); description.innerHTML = results[i]['abstract']; item.appendChild(description); list.appendChild(item); }
Poi facciamo un loop sull'array dei risultati e mettiamo insieme una lista con i titoli sotto forma di link e un paragrafo con il sommario. Notate come abbiamo definito abstract
(è una parola riservata in Javascript2).
} else { list = document.createElement('p'); list.appendChild(document.createTextNode(config.noresults)); } form.replaceChild(list,out); out = list; };
Se non ci sono risultati, definiamo un paragrafo con un messaggio di avviso come list
. In ogni caso rimpiazziamo il vecchio out
(il messaggio di caricamento) con list
e ridefiniamo out
come list
.
return{ config:config, init:init, found:found }; }();
Ciò che rimane da fare è fare un return per le proprietà e i metodi che vogliamo rendere pubblici. In questo caso found
deve essere pubblico perché ad esso accede quanto restituito dalle API di BOSS. Facciamo un return per init
per renderlo accessibile e per config
per consentire agli implementatori di fare l'override di queste proprietà.
Usare lo script
Per usare questo script dovrete solo aggiungerlo dopo il form definito nel documento, sostituire la API key con la vostra e richiamare init()
:
<form id="customsearch" action="http://search.yahoo.com/search"> <div> <label for="p">Search this site:</label> <input type="text" name="p" id="term"> <input type="hidden" name="vs" id="site" value="bbc.co.uk"> <input type="submit" value="go"> </div> </form> <script type="text/javascript" src="boss-site-search.js"></script> <script type="text/javascript"> SITESEARCH.config.appID = 'copy-the-id-you-know-to-get-where'; SITESEARCH.init(); </script>
Possibili sviluppi
Questo è solo un semplice esempio di ciò che è possibile fare con BOSS. Potete definire linguaggi e regioni, rintracciare e mostrare immagini e news, mixare i risultati con altre fonti di dati prima di visualizzarli. Una funzionalità molto interessante è che aggiungendo un parametro view=keyterms
all'URL potete ottenere le keywords di ciascun risultato per andare ancora più a fondo con la ricerca. Un esempio di questo approccio in PHP è disponibile su Yahoo Developer Network. Per Javascript c'è una soluzione chiamata yboss a vostra disposizione.