Il motore di ricerca Lucene: potente, flessibile e gratuito

Non lasciarti ingannare dal numero di versione basso - 0.04 dell'agosto 2000. Il motore di ricerca Lucene è un toolkit di ricerca robusto, potente e flessibile, pronto ad affrontare molti problemi di ricerca comuni. E poiché ora è disponibile con la più flessibile licenza open source LGPL, anche il prezzo (gratuito!) È giusto.

Doug Cutting, uno sviluppatore esperto di strumenti di ricerca e recupero del testo, ha creato Lucene. Cutting è l'autore principale del motore di ricerca V-Twin (parte dello sforzo del sistema operativo Copland di Apple) ed è attualmente un architetto senior di Excite. Ha progettato Lucene per semplificare l'aggiunta di capacità di indicizzazione e ricerca a un'ampia gamma di applicazioni, tra cui:

  • Posta elettronica ricercabile: un'applicazione di posta elettronica potrebbe consentire agli utenti di cercare i messaggi archiviati e aggiungere nuovi messaggi all'indice non appena arrivano.
  • Ricerca della documentazione in linea: un lettore di documentazione, basato su CD, sul Web o incorporato nell'applicazione, potrebbe consentire agli utenti di cercare documentazione in linea o pubblicazioni archiviate.
  • Pagine Web ricercabili: un browser Web o un server proxy potrebbe creare un motore di ricerca personale per indicizzare ogni pagina Web visitata da un utente, consentendo agli utenti di rivisitare facilmente le pagine.
  • Ricerca sul sito web: un programma CGI potrebbe consentire agli utenti di cercare nel tuo sito web.
  • Ricerca di contenuto: un'applicazione potrebbe consentire all'utente di cercare documenti salvati per contenuti specifici; questo potrebbe essere integrato nella finestra di dialogo Apri documento.
  • Controllo della versione e gestione dei contenuti: un sistema di gestione dei documenti può indicizzare i documenti o le versioni dei documenti, in modo che possano essere facilmente recuperati.
  • Notizie e feed di servizi di filo: un server di notizie o un relay potrebbe indicizzare gli articoli non appena arrivano.

Naturalmente, molti motori di ricerca potrebbero svolgere la maggior parte di queste funzioni, ma pochi strumenti di ricerca open source offrono la facilità d'uso, la rapida implementazione e la flessibilità di Lucene.

Ho usato Lucene per la prima volta durante lo sviluppo di Eyebrowse, uno strumento open source basato su Java per la catalogazione e la navigazione nelle mailing list. (Vedere Risorse per un collegamento.) Un requisito fondamentale per Eyebrowse era la capacità flessibile di ricerca e recupero dei messaggi. Richiedeva un componente di indicizzazione e di ricerca che aggiornava in modo efficiente la base dell'indice all'arrivo di nuovi messaggi, consentiva a più utenti di cercare e aggiornare la base dell'indice contemporaneamente e scalare agli archivi contenenti milioni di messaggi.

Ogni altro motore di ricerca open source che ho valutato, inclusi Swish-E, Glimpse, iSearch e libibex, era in qualche modo poco adatto ai requisiti di Eyebrowse. Ciò avrebbe reso l'integrazione problematica e / o dispendiosa in termini di tempo. Con Lucene, ho aggiunto l'indicizzazione e la ricerca a Eyebrowse in poco più di mezza giornata, dal download iniziale al codice completamente funzionante! Questo è stato meno di un decimo del tempo di sviluppo che avevo preventivato e ha prodotto un risultato più integrato e ricco di funzionalità rispetto a qualsiasi altro strumento di ricerca che ho considerato.

Come funzionano i motori di ricerca

Creare e mantenere un indice invertito è il problema centrale quando si costruisce un efficiente motore di ricerca per parole chiave. Per indicizzare un documento, è necessario prima scansionarlo per produrre un elenco di registrazioni . I post descrivono le occorrenze di una parola in un documento; generalmente includono la parola, un ID documento e possibilmente la posizione o la frequenza della parola all'interno del documento.

Se si considerano le registrazioni come tuple del modulo , un insieme di documenti produrrà un elenco di registrazioni ordinate per ID documento. Ma per trovare in modo efficiente i documenti che contengono parole specifiche, dovresti invece ordinare i messaggi per parola (o sia per parola che per documento, il che renderà le ricerche multiparole più veloci). In questo senso, la creazione di un indice di ricerca è fondamentalmente un problema di ordinamento. L'indice di ricerca è un elenco di messaggi ordinati per parola.

Un'implementazione innovativa

La maggior parte dei motori di ricerca utilizza B-tree per mantenere l'indice; sono relativamente stabili rispetto all'inserimento e hanno caratteristiche di I / O ben comportate (le ricerche e gli inserimenti sono operazioni O (log n)). Lucene ha un approccio leggermente diverso: invece di mantenere un singolo indice, crea più segmenti di indice e li unisce periodicamente. Per ogni nuovo documento indicizzato, Lucene crea un nuovo segmento di indice, ma unisce rapidamente segmenti piccoli a segmenti più grandi: ciò mantiene piccolo il numero totale di segmenti in modo che le ricerche rimangano veloci. Per ottimizzare l'indice per una ricerca rapida, Lucene può unire tutti i segmenti in uno, utile per gli indici aggiornati di rado. Per evitare conflitti (o sovraccarico di blocco) tra lettori di indice e autori, Lucene non modifica mai i segmenti in posizione, ma ne crea di nuovi. Quando si uniscono i segmenti,Lucene scrive un nuovo segmento e cancella i vecchi, dopo che tutti i lettori attivi lo hanno chiuso. Questo approccio si adatta bene, offre allo sviluppatore un alto grado di flessibilità nel sostituire la velocità di indicizzazione con la velocità di ricerca e ha caratteristiche I / O desiderabili sia per l'unione che per la ricerca.

Un segmento di indice Lucene è costituito da diversi file:

  • Un indice del dizionario contenente una voce per ogni 100 voci nel dizionario
  • Un dizionario contenente una voce per ogni parola unica
  • Un file di registrazione contenente una voce per ogni registrazione

Poiché Lucene non aggiorna mai i segmenti in posizione, possono essere archiviati in file flat anziché in complicati B-tree. Per un rapido recupero, l'indice del dizionario contiene gli offset nel file del dizionario e il dizionario conserva gli offset nel file delle registrazioni. Lucene implementa anche una serie di trucchi per comprimere il dizionario e pubblicare i file, riducendo così l'I / O del disco, senza incorrere in un notevole sovraccarico della CPU.

Valutazione dei motori di ricerca

Altri motori di ricerca open source ampiamente utilizzati includono Swish-E, Glimpse, libibex, freeWAIS e iSearch. Come ogni pacchetto software, ognuno è ottimizzato per l'uso in situazioni particolari; spesso è difficile distribuire questi strumenti al di fuori dei domini previsti. Considera le seguenti caratteristiche quando valuti un motore di ricerca:

  • Indicizzazione incrementale rispetto a quella in batch: alcuni motori di ricerca supportano solo l'indicizzazione in batch; una volta creato un indice per una serie di documenti, l'aggiunta di nuovi documenti diventa difficile senza reindicizzare tutti i documenti. L'indicizzazione incrementale consente di aggiungere facilmente documenti a un indice esistente. Per alcune applicazioni, come quelle che gestiscono feed di dati in tempo reale, l'indicizzazione incrementale è fondamentale. Lucene supporta entrambi i tipi di indicizzazione.
  • Fonti di dati: molti motori di ricerca possono indicizzare solo file o pagine web. Ciò ostacola le applicazioni in cui i dati indicizzati provengono da un database o in cui esistono più documenti virtuali in un singolo file, come un archivio ZIP. Lucene consente agli sviluppatori di consegnare il documento all'indicizzatore tramite un Stringo un InputStream, consentendo l'astrazione dell'origine dati dai dati. Tuttavia, con questo approccio, lo sviluppatore deve fornire i lettori appropriati per i dati.
  • Controllo dell'indicizzazione : alcuni motori di ricerca possono eseguire automaticamente la scansione attraverso un albero di directory o un sito Web per trovare i documenti da indicizzare. Sebbene ciò sia utile se i dati sono già archiviati in questo modo, gli indicizzatori basati su crawler spesso offrono una flessibilità limitata per le applicazioni che richiedono un controllo granulare sui documenti indicizzati. Poiché Lucene opera principalmente in modalità incrementale, consente all'applicazione di trovare e recuperare i documenti.
  • Formati di file: alcuni motori di ricerca possono indicizzare solo documenti di testo o HTML; altri supportano un meccanismo di filtro, che offre una semplice alternativa all'indicizzazione di documenti di elaborazione testi, documenti SGML e altri formati di file. Lucene sostiene un tale meccanismo.
  • Codifica dei contenuti: alcuni motori di ricerca trattano un documento come un unico flusso di token; altri consentono la specifica di più campi dati all'interno di un documento, come "oggetto", "astratto", "autore" e "corpo". Ciò consente query semanticamente più ricche come "l'autore contiene Hamilton E il corpo contiene la costituzione". Lucene supporta la codifica dei contenuti trattando i documenti come raccolte di campi e supporta le query che specificano i campi da cercare.
  • Elaborazione di testi interrotti : parole comuni, come "a", "e" e "il", aggiungono poco valore a un indice di ricerca. Ma poiché queste parole sono così comuni, catalogarle contribuirà notevolmente al tempo di indicizzazione e alla dimensione dell'indice. La maggior parte dei motori di ricerca non indicizzerà determinate parole, chiamate stop words. Alcuni usano un elenco di parole di arresto, mentre altri selezionano statisticamente le parole di arresto. Lucene gestisce le parole di arresto con il Analyzermeccanismo più generale , che verrà descritto in seguito, e fornisce la StopAnalyzerclasse, che elimina le parole di arresto dal flusso di input.
  • Stemming: spesso, un utente desidera che una query per una parola corrisponda ad altre parole simili. Ad esempio, una query per "jump" dovrebbe probabilmente corrispondere anche alle parole "jumped", "jumper" o "jumps". Ridurre una parola alla sua forma radice è chiamata stemming . Lucene non implementa ancora lo stemming, ma potresti facilmente aggiungere uno stemmer tramite una Analyzerclasse più sofisticata .
  • Funzionalità di query: i motori di ricerca supportano una varietà di funzionalità di query. Alcuni supportano query booleane complete; altri supportano solo e query. Alcuni restituiscono un punteggio di "rilevanza" con ogni hit. Alcuni possono gestire query di adiacenza o prossimità - "ricerca seguita dal motore" o "Knicks vicino a Celtics" - altri possono cercare solo per singole parole chiave. Alcuni possono cercare più indici contemporaneamente e unire i risultati per fornire un punteggio di pertinenza significativo. Lucene supporta un'ampia gamma di funzionalità di query, comprese tutte quelle sopra elencate. Tuttavia, Lucene non supporta la preziosa query Soundex, o "suona come".
  • Concorrenza: più utenti possono eseguire ricerche in un indice contemporaneamente? Un utente può cercare un indice mentre un altro lo aggiorna? Lucene consente agli utenti di cercare un indice a livello transazionale, anche se un altro utente sta aggiornando contemporaneamente l'indice.
  • Supporto non inglese: molti motori di ricerca presumono implicitamente che l'inglese sia la lingua di destinazione; questo è evidente in aree come gli elenchi di parole non significative, gli algoritmi di stemming e l'uso della prossimità per trovare la corrispondenza con le query di frase. Poiché Lucene preelabora il flusso di input tramite la Analyzerclasse fornita dallo sviluppatore, è possibile eseguire il filtraggio specifico della lingua.

Sebbene non esaustivo, l'elenco sopra offre un punto di partenza per la valutazione di un motore di ricerca per un particolare progetto. Alcuni strumenti di ricerca sono poco adatti a determinate attività: la comprensione dei requisiti dell'applicazione può aiutarti a scegliere lo strumento giusto per il lavoro.

Usando Lucene

Illustrerò come utilizzare Lucene per creare, popolare e cercare un indice. Per chiarezza, le istruzioni di importazione e la gestione delle eccezioni sono state omesse dai programmi di esempio. In queste illustrazioni, ho memorizzato l'indice di ricerca nel filesystem (puoi memorizzare gli indici ovunque, ad esempio, in memoria o in un database). I file indicizzati sono semplici file di testo. Con Lucene, puoi anche indicizzare facilmente altri formati di documenti e documenti non archiviati nei file.

Crea un indice

The simple program CreateIndex.java creates an empty index by generating an IndexWriter object and instructing it to build an empty index. In this example, the name of the directory that will store the index is specified on the command line.

public class CreateIndex { // usage: CreateIndex index-directory public static void main(String[] args) throws Exception { String indexPath = args[0]; IndexWriter writer; // An index is created by opening an IndexWriter with the // create argument set to true. writer = new IndexWriter(indexPath, null, true); writer.close(); } } 

Index text documents

IndexFile.java shows how to add documents -- the files named on the command line -- to an index. For each file, IndexFiles creates a Document object, then calls IndexWriter.addDocument to add it to the index. From Lucene's point of view, a Document is a collection of fields that are name-value pairs. A Field can obtain its value from a String, for short fields, or an InputStream, for long fields. Using fields allows you to partition a document into separately searchable and indexable sections, and to associate metadata -- such as name, author, or modification date -- with a document. For example, when storing mail messages, you could put a message's subject, author, date, and body in separate fields, then build semantically richer queries like "subject contains Java AND author contains Gosling." In the code below, we store two fields in each Document: path, to identify the original file path so it can be retrieved later, and body, for the file's contents.

public class IndexFiles { // usage: IndexFiles index-path file . . . public static void main(String[] args) throws Exception { String indexPath = args[0]; IndexWriter writer; writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false); for (int i=1; i