Perché è necessario Lucene Luce?
La ricerca è una delle operazioni più comuni che eseguiamo più volte al giorno. Questa ricerca può riguardare più pagine Web che esistono sul Web o un'applicazione musicale o un repository di codice o una combinazione di tutti questi. Si potrebbe pensare che un semplice database relazionale possa supportare anche la ricerca. Questo è corretto. I database come MySQL supportano la ricerca full-text. Ma che dire del Web o di un'applicazione musicale o di un repository di codice o di una combinazione di tutti questi?? Il database non può memorizzare questi dati nelle sue colonne. Anche se così fosse, ci vorrà una quantità di tempo inaccettabile per eseguire la ricerca così grande.
Un motore di ricerca full-text è in grado di eseguire una query di ricerca su milioni di file contemporaneamente. La velocità con cui i dati vengono archiviati in un'applicazione oggi è enorme. L'esecuzione della ricerca full-text su questo tipo di volume di dati è un compito difficile. Questo perché le informazioni di cui abbiamo bisogno potrebbero esistere in un singolo file tra miliardi di file conservati sul web.
Come funziona Lucene?
La domanda ovvia che dovrebbe venirti in mente è: come fa Lucene a eseguire query di ricerca full-text così velocemente?? La risposta a questo, ovviamente, è con l'aiuto degli indici che crea. Ma invece di creare un indice classico, Lucene utilizza Indici invertiti.
In un indice classico, per ogni documento, raccogliamo l'elenco completo di parole o termini che il documento contiene. In un indice invertito, per ogni parola in tutti i documenti, memorizziamo quale documento e posizioniamo questa parola/termine in cui si trova. Questo è un algoritmo di alto livello che rende la ricerca molto semplice. Considera il seguente esempio di creazione di un indice classico:
Doc1 -> "Questo", "è", "semplice", "Lucene", "campione", "classico", "invertito", "indice"Doc2 -> "In esecuzione", "Elasticsearch", "Ubuntu", "Aggiorna"
Doc3 -> "RabbitMQ", "Lucene", "Kafka", "", "Spring", "Boot"
Se usiamo l'indice invertito, avremo indici come:
Questo -> (2, 71)Lucene -> (1, 9), (12,87)
Apache -> (12, 91)
Struttura -> (32, 11)
Gli indici invertiti sono molto più facili da mantenere. Supponiamo che se vogliamo trovare Apache nei miei termini, avrò risposte immediate con indici invertiti mentre con la ricerca classica verrà eseguito su documenti completi che potrebbe non essere stato possibile eseguire in scenari in tempo reale.
Flusso di lavoro Lucene
Prima che Lucene possa effettivamente cercare i dati, deve eseguire dei passaggi. Visualizziamo questi passaggi per una migliore comprensione:
Flusso di lavoro Lucene
Come mostrato nel diagramma, questo è ciò che accade in Lucene:
- Lucene riceve i documenti e altre fonti di dati
- Per ogni documento, Lucene prima converte questi dati in testo normale e poi gli analizzatori converte questa fonte in testo normale
- Per ogni termine nel testo in chiaro vengono creati gli indici invertiti
- Gli indici sono pronti per essere ricercati
Con questo flusso di lavoro, Lucene è un motore di ricerca full-text molto potente. Ma questa è l'unica parte che Lucene soddisfa. Dobbiamo eseguire il lavoro da soli. Diamo un'occhiata ai componenti necessari per l'indicizzazione.
Componenti Lucene
In questa sezione descriveremo i componenti di base e le classi Lucene di base utilizzate per creare gli indici:
- Directory: un indice Lucene memorizza i dati nelle normali directory del file system o in memoria se hai bisogno di maggiori prestazioni. È completamente la scelta delle app per archiviare i dati dove vuole, un database, la RAM o il disco.
- Documenti: I dati che forniamo al motore Lucene devono essere convertiti in testo normale. Per fare ciò, creiamo un oggetto Document che rappresenta quella fonte di dati. Successivamente, quando eseguiamo una query di ricerca, come risultato, otterremo un elenco di oggetti Document che soddisfano la query che abbiamo passato.
- Campi: i documenti sono popolati con una raccolta di campi. Un campo è semplicemente un paio di (nome, valore) elementi. Quindi, durante la creazione di un nuovo oggetto Document, dobbiamo riempirlo con quel tipo di dati accoppiati. Quando un campo è indicizzato in modo inverso, il valore del campo è tokenizzato ed è disponibile per la ricerca. Ora, mentre usiamo i campi, non è importante memorizzare la coppia effettiva ma solo l'indicizzazione invertita. In questo modo, possiamo decidere quali dati sono solo ricercabili e non importanti da salvare. Vediamo un esempio qui:
Indicizzazione del campo
Nella tabella sopra, abbiamo deciso di memorizzare alcuni campi e altri non sono memorizzati. Il campo del corpo non è memorizzato ma indicizzato. Ciò significa che l'e-mail verrà restituita come risultato quando viene eseguita la query per uno dei Termini per il contenuto del corpo.
- Termini: I termini rappresentano una parola del testo. I termini vengono estratti dall'analisi e dalla tokenizzazione dei valori di Fields, quindi Il termine è l'unità più piccola su cui viene eseguita la ricerca.
- Analizzatori: Un analizzatore è la parte più cruciale del processo di indicizzazione e ricerca. È l'analizzatore che converte il testo in chiaro in token e termini in modo che possano essere ricercati. Bene, questa non è l'unica responsabilità di un analizzatore. Un analizzatore utilizza un tokenizzatore per creare token. Un analizzatore svolge anche le seguenti attività:
- Stemming: un analizzatore converte la parola in uno stem. Ciò significa che "fiori" viene convertito nella parola radice "fiore". Quindi, quando viene eseguita una ricerca per "fiore", il documento verrà restituito.
- Filtraggio: un analizzatore filtra anche le parole di arresto come 'The', 'is' etc. poiché queste parole non attirano alcuna domanda da eseguire e non sono produttive.
- Normalizzazione: questo processo rimuove gli accenti e altri segni di carattere.
Questa è solo la normale responsabilità di StandardAnalyzer.
Esempio di applicazione
Useremo uno dei tanti archetipi Maven per creare un progetto di esempio per il nostro esempio. Per creare il progetto esegui il seguente comando in una directory che utilizzerai come spazio di lavoro:
archetipo mvn:generate -DgroupId=com.linuxhint.esempio -DartifactId=LH-LuceneExample -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=falseSe stai eseguendo maven per la prima volta, ci vorranno alcuni secondi per eseguire il comando generate perché Maven deve scaricare tutti i plugin e gli artefatti necessari per eseguire l'attività di generazione. Ecco come appare l'output del progetto:
Configurazione del progetto
Una volta creato il progetto, sentiti libero di aprirlo nel tuo IDE preferito. Il prossimo passo è aggiungere le dipendenze Maven appropriate al progetto. Ecco il pom.xml con le dipendenze appropriate:
Infine, per comprendere tutti i JAR che vengono aggiunti al progetto quando aggiungiamo questa dipendenza, possiamo eseguire un semplice comando Maven che ci consente di vedere un albero delle dipendenze completo per un progetto quando aggiungiamo alcune dipendenze ad esso. Ecco un comando che possiamo usare:
dipendenza mvn: alberoQuando eseguiamo questo comando, ci mostrerà il seguente albero delle dipendenze:
Infine, creiamo una classe SimpleIndexer che viene eseguita
import java.io.File;
import java.io.Lettore di file;
import java.io.IOException;
importare organizzazione.apache.lucene.analisi.Analizzatore;
importare organizzazione.apache.lucene.analisi.standard.Analizzatore standard;
importare organizzazione.apache.lucene.documento.Documento;
importare organizzazione.apache.lucene.documento.Campo Memorizzato;
importare organizzazione.apache.lucene.documento.Campo di testo;
importare organizzazione.apache.lucene.indice.IndexWriter;
importare organizzazione.apache.lucene.indice.IndexWriterConfig;
importare organizzazione.apache.lucene.negozio.FSDirectory;
importare organizzazione.apache.lucene.utile.Versione;
public class SimpleIndexer
private static final String indexDirectory = "/Users/shubham/somewhere/LH-LuceneExample/Index";
private static final String dirToBeIndexed = "/Users/shubham/somewhere/LH-LuceneExample/src/main/java/com/linuxhint/example";
public static void main(String[] args) genera l'eccezione
File indexDir = new File(indexDirectory);
File dataDir = new File(dirToBeIndexed);
Indicizzatore SimpleIndexer = new SimpleIndexer();
int numIndexed = indexer.index(indexDir, dataDir);
Sistema.su.println("Totale file indicizzati " + numIndexed);
private int index(File indexDir, File dataDir) genera IOException
Analizzatore analizzatore = nuovo StandardAnalyzer(Versione.LUCENE_46);
IndexWriterConfig config = new IndexWriterConfig(Versione.LUCENE_46,
analizzatore);
IndexWriter indexWriter = nuovo IndexWriter(FSDirectory.open(indexDir),
configurazione);
File[] files = dataDir.listFiles();
for (File f : file)
Sistema.su.println("Indicizzazione file " + f.getCanonicalPath());
Documento doc = nuovo Documento();
documento.add(new TextField("content", new FileReader(f)));
documento.add(new StoredField("fileName", f.getCanonicalPath()));
indiceWriter.addDocument(doc);
int numIndexed = indexWriter.maxDoc();
indiceWriter.vicino();
restituisce numIndexed;
In questo codice, abbiamo appena creato un'istanza Document e aggiunto un nuovo campo che rappresenta il contenuto del file. Ecco l'output che otteniamo quando eseguiamo questo file:
File di indicizzazione /Users/shubham/somewhere/LH-LuceneExample/src/main/java/com/linuxhint/example/SimpleIndexer.GiavaFile totali indicizzati 1
Inoltre, viene creata una nuova directory all'interno del progetto con il seguente contenuto:
Dati indice
Analizzeremo ciò che tutti i file vengono creati in questi Index in altre lezioni a venire su Lucene.
Conclusione
In questa lezione, abbiamo visto come funziona Apache Lucene e abbiamo anche realizzato una semplice applicazione di esempio basata su Maven e Java.