- la stringa da espandere
- Un oggetto Searcher inizializzato a partire dall'indice creato su wordnet
- Un oggetto Analyzer
- La String contenente il field che nella costruzione della query espansa verrà considerato come field di ricerca.
- un float per il boosting dei sinonimi.
Lucene e Wordnet
Lucene SpellChecker - "Il Forse Cercavi"
G.Morreale
"Forse Cercavi: Java"
- Minimum Edit distance: Calcolare il numero di inserimenti, cancellazioni e sostituzioni necessarie a trasformare una stringa(Quella presupposta erronea) in un'altra(quella presupposta corretta)
- Similiarity Key: Basato su un dizionario dove sotto la stessa chiave ci sono le stringhe similiari.
- Letter n-gram: n indica il numero di lettere all'interno di una sotto-sequenza di una parola. Es. 3-grams per la parola lucene "luc", "uce", "cen","ene". Confrontare due n-gram può dare con buona approssimazione un suggerimento. Per approfondimenti (http://en.wikipedia.org/wiki/N-gram)
- Scrittura del dizionario
- Ricerca all'interno del dizionario
- IndexMaker
- BaseIndexMaker
- SpellIndexMaker
- SearchResult
- SearchEngine
- SimpleSearchEngine
- SuggestAndSeachEngine
List<Query> suggested = se.suggest(sentence, 2);
//stampa query suggerite
System.out.println(suggested);
//Se esiste almento un suggerimento..
if (suggested != null && suggested.size() > 0)
{
//effettuiamo una nuova ricerca con la prima query suggerita
res = se.search(suggested.get(0));
i = 1;
//stampa risultati con la nuova query.
for (SearchResult s : res)
{
System.out.println(i++ + "° " + s.getDoc().get("descrizione") + " - " + s.getScore());
}
}
public String[] suggestSimilar(String arg0, int arg1, IndexReader arg2, String arg3, boolean arg4) throws IOException
Questo overloading è possibile avvalersi di due criteri per l'ottenimento dei risultati.
- La distanza di editing tra l'input e il suggerimento
- La popolarità del suggerimento all'interno dell'indice originale (ribadisco: non nel dizionario).
public abstract TokenStream tokenStream(String arg0, Reader arg1)
Ci consente di navigare tra i token ottenuti.
Bene, visto però che il metodo suggest realizzato nella classe SuggestAndSearchEngine ritorna diverse Query(diversi suggerimenti), è opportuno creare un metodo suggest in grado di ottenere un solo suggerimento per input.
Tale metodo sarà di supporto al metodo finale suggestComposite la cui realizzazione è obiettivo del paragrafo.
Ne incollo l'implementazione (Da inserire nella classe SuggestAndSearchEngine )
/** * Data una singola parola è in grado di generare il suggerimento * @param word - input su cui generare il suggerimento * @return - Il suggerimento, esso viene proposto sottoforma di Term in modo * da permettere la costruzione di Query composte da più termini */ public Term suggest(String word) { Term term = null; try { //Inizializzazione oggetto chiave del metodo! SpellChecker spellChecker = new SpellChecker(spellDictionaryDir); //Se la query string esiste nel dizionario vuol dire che non ha //senso cercare un suggerimento! if (!spellChecker.exist(word)) { //ricerca di parole similiari all'interno del dizionario IndexReader indexReader = IndexReader.open(indexDirectory); String[] similiarWords = spellChecker.suggestSimilar(word, 1,indexReader,defaultField,true); //String[] similiarWords = spellChecker.suggestSimilar(word, 1); if (similiarWords.length != 0) { //data la parole similiari si procede alla costruzione del Term term = new Term(defaultField, similiarWords[0]); } } } catch (IOException ex) { Logger.getLogger(SuggestAndSeachEngine.class.getName()).log(Level.SEVERE, null, ex); } finally { return term; } }
Anche il metodo suggestComposite per semplicità fornisce un solo suggerimento per input.
Ecco la firma del metodo
public Query suggestComposite(String queryString)
- Suddivide in token l'input
- Per ogni token richiede un suggerimento
- Concatena i vari suggerimenti all'interno di una query (tale query è una PhraseQuery, addatta alla composizione con più termini)
Lucene - Un motore di ricerca in java
G.Morreale
- Index - Indice che raccoglie i diversi document
- Document - Rappresentazione dei documenti
- Field - Elementi di documenti composti dalla coppia nome/valore.
- Se memorizzare o meno il valore nell'indice (Vedi campi Field.Store: COMPRESS, NO, YES)
Di solito si tende a non memorizzare nell'indice qui valori sui quali non si effettua la ricerca, e a indicizzare con compressione qualora i valori sono "grandi" (es. documenti di testo di diversi KB)
- Se indicizzare o meno il valore e come (Vedi campi Field.Index: ANALYZED, ANALYZED_NO_NORMS,NO,,NOT_ANALYZED,NOT_ANALIZED_NO_NORMS)
I valori più usati, non indicati nella javadoc come Expert, sono
- ANALYZED - Indicizza i token utilizzando l'analyzer(*)
- NO - Non indicizzare del tutto
- NOT_ANALYZED - Indicizza ma senza l'uso dell'analyzer(*)
(*)Riguardo l'analyzer ne parlo in seguito.
new Document()
cognome rossi
nome giovanni
numero 123123 123
new Document()
cognome verdi
nome mario
numero 789 789 789
..
new Document()
..
..
..
- SimpleAnalyzer - Suddivide il valore in token e converte l'input in soli caratteri minuscoli.
- StopAnalyzer - Funziona come il precedente ma filtra l'indicizzazione su piccoli token che nella lingua inglese occorrono con alta probabilità (a, an, the, etc). E' possibile aggiungere o modificare l'array di token sul quale filtrare l'indicizzazione.
- StandardAnalyzer - Come lo StopAnalizer con l'aggiunta di filtri su apostrofi, acronimi e altre parole che possono sporcare il risultato della ricerca.
//------------------------------------------------
//--------------Creazione Struttura---------------
//------------------------------------------------
//creazione di un analyzer standard
Analyzer analyzer = new StandardAnalyzer();
//Memorizza l'indice in RAM:
//Per inserire ad esempio i dati su file, usare Directory dir = FSDirectory.getDirectory("path");
Directory directory = new RAMDirectory();
//Creazione istanza per la scrittura dell'indice
//Tale istanza viene fornita di analyzer, di un boolean per indicare se ricreare o meno da zero
//la struttura e di una dimensione massima (o infinita IndexWriter.MaxFieldLength.UNLIMITED)
IndexWriter iwriter = new IndexWriter(directory, analyzer, true, new IndexWriter.MaxFieldLength(25000));
//costruiamo un indice con solo 2 documenti
//creazione documento
Document doc = new Document();
String text = "Il cane corre dietro il gatto";
//creazione del campo con indicazione di memorizzazione(Store.YES) e indicizzazione con analyzer(ANALYZED)
Field field = new Field("testo", text, Field.Store.YES,Field.Index.ANALYZED))
//Aggiunta campo al documento
doc.add(field);
//creazione secondo campo con la data, non indicizzato.
field = new Field("data",new Date().toString(), Field.Store.YES, Field.Index.NO)
doc.add(field);
//aggiunta documento all'indice
iwriter.addDocument(doc);
//creazione secondo documento, come sopra
doc = new Document();
text = "il gatto è velocissimo";
Field field = new Field("testo", text, Field.Store.YES,Field.Index.ANALYZED))
doc.add(field);
field = new Field("data",new Date().toString(), Field.Store.YES, Field.Index.NO)
doc.add(field);
iwriter.addDocument(doc);
//chiusura indice (spostare il codice nella clausola finally!)
iwriter.close();
//----------------------------------------------
//--------------Ricerca-------------------------
//----------------------------------------------
//Creazione dell'oggetto per la ricerca indicando la struttura (directory) su cui lavorare e l'analyzer
IndexSearcher isearcher = new IndexSearcher(directory,analyzer);
//Catturiamo l'input dell'utente
String sentence = JOptionPane.showInputDialog("sentence");
//Creazione della query, viene indicato il campo di default sul quale effettuare la ricerca.
QueryParser parser = new QueryParser("testo", analyzer);
Query query = parser.parse("sentence ");
//Effettua la ricerca ottenendo l'oggetto TopDocs
TopDocs topDocs = isearcher.search(query,1000);
//Stampa del conteggio numero di hits.
System.out.println("Numero di hits " + topDocs.totalHits);
//Array dei risultati
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
int i = 1;
for (ScoreDoc sc : scoreDocs)
{
System.out.print(i++ + "° - ");
//Attraverso l'oggetto scoreDoc è possibile ottenere un indice che passato all metodo
//indexSearcher.doc restituisce un Document dal quale estrarre i vari campi
System.out.println(indexSearcher.doc(sc.doc).get("testo"));
}
indexSearcher.close();
directory.close();
Document1
text = Il cane corre dietro il gatto
Document2
text = Il gatto corre veloce