Sunteți pe pagina 1din 18

Lucene

Lucene1 este o bibliotecă de funcţii gratuită folosită în căutarea documentară,


creată iniţial în Java de Doug Cutting. Crearea ei a fost suportată de fundaţia Apache
Software şi este distribuită sub licenţa oferită de Apache Software. În prezent Lucene are
support pentru mai multe limbaje de programare, din care amintim Delphi, Perl, C#, C++,
Python, Ruby şi PHP.

Potrivită pentru orice aplicaţie care necesită indexare după text şi posibilităţi de
căutare, Lucene este recunoscută pentru facilităţile de căutare folosind atât motoarele de
căutare de pe Internet cât şi pentru căutarea locală. Librăria Lucene permite doar
indexarea şi căutarea şi nu conţine opţiuni de crawling (căutarea automată de pagini web)
sau funcţionalităţi de parsare HTML. Proiectul Apache denumit Nutch2 se bazează pe
Lucene şi oferă aceste funţionalităţi, iar proiectul Apache cu numele Solr3 este un server
care pune la dispoziţie facilităţi de căutare bazate pe Lucene.

Sub-proiecte Lucene

Apache Lucene este o bibliotecă performantă, cu numeroase funcţionalităţi în


ceea ce priveşte indexarea şi căutarea de text. Proiectul Apache Lucene conţine mai multe
sub-proiecte, dintre care amintim:

• Lucene Java: este un subproiect dezvoltat în Java ce furnizează funcţii pentru


indexare şi căutare;

• Nutch: a fost construit pornind de la varianta Lucene Java, oferind în plus


suport pentru aplicaţiile ce necesită căutarea pe web;

• Hadoop: este o varianta distribuită a proiectului Nutch;

1
http://lucene.apache.org/java/docs/index.html
2
http://en.wikipedia.org/wiki/Nutch
3
http://en.wikipedia.org/wiki/Solr

1
• Lucy: este o bibliotecă independentă scrisă în C, derivată din Lucene, cu
interfeţe scrise în limbajele Perl şi Ruby;

• Solr: este un server performant construit pe baza librăriilor Lucene, cu API-uri


suplimentare pentru XML/HTTP şi JSON/Python/Ruby. În plus acesta
permite evidenţierea textelor întoarse de motorul de căutare, căutare avansată,
caching şi o interfaţă web pentru administrare;

• Lucene.Net: este o librărie C# obţinută prin transformarea funcţiilor conţinute


de Lucene Java, folosind Microsoft .NET Framework. Acest proiect nu este
încă finalizat şi este încă în dezvoltare.

În Figura 1 este prezentat modul de în care funcţiile Lucene pot fi integrate într-o
aplicaţie care necesită operaţii de căutare şi indexare. Putem observa modul în care
aplicaţia interacţionează cu module Lucene, care sunt responsabile cu indexarea
documentelor şi căutarea folosind indexul creat:

Figura 1. Modul de funcţionare al Lucene

2
Concepte Lucene

Conceptele fundamentale Lucene sunt documentul, câmpul şi termenul


(Gospodnetic, Hatcher, 2005). Documentul reprezintă o colecţie de câmpuri. Putem gândi
documentul ca un document virtual – o porţiune de date, precum o pagină web, un e-mail,
sau un fişier text – pe care vom dori să facem căutări la un moment dat. Sursa iniţială a
documentului (precum o bază de date, un document word, un capitol dintr-o carte, şi aşa
mai departe) nu este importantă pentru Lucene. Metadatele documentului precum autorul,
titlul, subiectul, data ultimei modificări, etc. sunt indexate şi salvate separat ca fiind
câmpuri ale documentului. Termenul este unitatea de bază folosită în căutare. Acesta
constă din perechi de şiruri de caractere formate din numele câmpului şi valoarea acestui
câmp. Aceste perechi sunt create de Lucene în structura sa internă în timpul procesului de
indexare, şi prin urmare nu cade în sarcina noastră să le gestionăm.

Figura 2: Conceptele Lucene

În urma operaţiilor de indexare a unei colecţii de documente după regulile Lucene


se obţine un index. Un index stochează informaţii statistice despre termeni şi permite o
căutare foarte eficientă bazată pe acestea. Indecşii Lucene se încadrează în categoria

3
indecşilor inverşi deoarece, pentru un termen dat pot fi afişate documentele care conţin
termenul, ceea ce e invers ordinii naturale în care documentele afişează termenii.

Pentru o colecţie de documente, presupunând că un document este stocat într-un


fişier, pentru a realiza indexarea este nevoie ca fiecare fişier să fie transformat într-un
document Lucene populat cu câmpuri conţinând informaţii din fişier.

Un index Lucene poate fi compus din mai mulţi sub-indecşi, numiţi segmente,
fiecare din aceştia fiind un index independent şi în care se pot face căutări separate.
Indecşii evoluează prin crearea de noi segmente din documente nou adăugate sau în urma
unirii unor segmente existente.

Fiecare segment conţine următoarele informaţii:

• Nume de câmpuri – reprezintă numele de câmpuri folosite în index.

• Valorile stocate în câmpuri – pentru fiecare document, conţine o listă de


perechi atribut-valoare, unde atributele sunt nume de câmpuri. Acestea sunt
folosite pentru stocarea informaţiilor auxiliare despre document, ca de
exemplu titlul, url-ul sau identificatorul pentru acces la o bază de date. Setul
de valori de câmpuri stocate este ceea ce întoarce fiecare potrivire la o căutare.

• Dicţionar de termeni – conţine toţi termenii folosiţi în toate câmpurile din


toate documentele indexate. Dicţionarul conţine de asemenea numărul de
documente care conţin termenul şi frecvenţa sa.

• Frecvenţa termenilor este calculată pentru fiecare termen din dicţionar în


funcţie de numărul documentelor care le conţin şi frecvenţa cu care acestea
apar în documentele respective.

• Poziţia termenilor este reţinută pentru fiecare termen din dicţionar şi


reprezintă poziţiile unde termenul este găsit în documente.

• Factori de normalizare sunt calculaţi pentru fiecare câmp din fiecare


document, iar aceste valori vor fi multiplicate pentru a calcula numărul de
căutari cu succes asupra acelui câmp.

4
• Vectorii de termeni sunt construiţi pentru fiecare câmp din fiecare document
(numite uneori şi vectorii documentului). Un vector de termeni este format din
textul termenilor şi frecvenţa fiecăruia.

• Documentele şterse sunt salvate într-un fişier optional.

Interogări Lucene

Interogările pe baza cărora se fac căutările sunt create folosind o listă iniţială de
cuvinte. Este important pentru succesul căutărilor ca interogările să fie construite cât mai
bine în funcţie de cuvintele care vor fi folosite în căutare. Dacă interogările sunt prea
generale atunci vom obţine foarte multe documente, iar relevanţa documentelor rezultate
va fi scăzută. În schimb, dacă interogările sunt prea specifice, atunci numărul
documentelor întoarse va fi mic, însă va creşte posibilitatea obţinerii documentelor
relevante.

O interogare conţine o succesiune de cuvinte (termeni), prefixate sau nu de un


operator, formându-se astfel o expresie regulată pe care se bazează căutarea. Există două
tipuri de termeni: termeni simpli şi fraze. Termenii simpli sunt reprezentaţi de un singur
cuvânt, frazele fiind grupuri de cuvinte cuprinse între ghilimele.

De asemenea trebuie precizat că o interogare Lucene suportă şi precizarea de


câmpuri ce trebuie să aibă o anumită valoare, aceasta fiind realizată prin specificarea
numelui câmpului urmat de „:” şi valoarea pe care trebuie să o aibă.

De exemplu, pentru a preciza că se caută documente cu titlul „Ernest


Hemingway” care să conţină textul „faimos romancier, nuvelist, realizator de povestiri”,
interogarea trebuie să aibă forma următoare:

title:”Ernest Hemingway” AND ”faimos romancier, nuvelist,


realizator de povestiri”

Aşa cum am precizat anterior, interogările sunt de fapt expresii regulate scrise
folosind o anumită sintaxă, şi de aceea este firesc să poată fi folosite în cadrul lor şi

5
simbolurile „?” sau „*” cu acelaşi înteles pe care îl au în toate limbajele de programare
care suportă folosirea de expresii regulate. Astfel, „?” înlocuieşte orice caracter şi „*”
specifică faptul că pot fi oricâte caractere, inclusiv 0.

Ca operatori booleeni, interogările Lucene acceptă combinaţii folosind următorii


operatori: OR, AND, NOT, +, -. Semnificaţiile lor sunt prezentate în continuare:

• OR – este operatorul implicit de conjuncţie (dacă nu se foloseşte nici un


operator între doi termeni consecutivi, implicit operatorul considerat va fi
OR). Acesta leagă doi termeni consecutivi, iar în urma căutării vor fi întoarse
documentele care conţin cel puţin unul din cei doi termeni.

• AND – caută documentele în care există ambii termeni, fiind echivalentul


operatorului de intersecţie de mulţimi.

• NOT – exclude din listă documentele care conţin termenul ce urmează după
operator, fiind echivalentul operatorului de diferenţă de mulţimi. Este
important de ştiut faptul ca acest operator nu poate fi folosit de unul singur
într-o interogare, fiind obligatorie prezenţa cel puţin a încă unui termen în
afară de cel care urmează lui NOT. De exemplu, o interogare de tipul NOT
„Alan Mathison Turing” nu va întoarce nici un rezultat;

• + – specifică fapul că termenul care îi urmează trebuie să apară obligatoriu în


fiecare document rezultat;

• – – specifică faptul că termenul care îi urmează trebuie să nu apară în nici un


document rezultat.

Lucene suportă de asemenea în cadrul interogărilor şi gruparea de termeni,


aceasta fiind realizată cu ajutorul parantezelor pentru a evita confuziile în legătură cu
ceea ce se caută. De exemplu, pentru o interogare cu forma:

(povestire OR istorisire) AND („Conan Doyle” OR Doyle)

6
rezultatele vor conţine unul dintre cuvintele „povestire” sau „istorisire” şi unul dintre
numele Doyle sau Conan Doyle.

De multe ori însă rezultatele unei interogări nu sunt suficiente sau relevante, de
aceea au fost căutate metode de a le îmbunătăţi. Mai jos sunt prezentate câteva strategii
folosite de grupul UAIC în construirea sistemelor de întrebare răspuns participante la
competiţiile CLEF20074 şi CLEF20085:

Pentru o interogare care nu întoarce destule rezultate se poate mări numărul


cuvintelor folosite în interogare şi astfel probabilitatea de a găsi un răspuns, prin
adăugarea sinonimelor cuvintelor iniţiale din interogare. De exemplu, pentru întrebarea
„Ce album a lansat trupa Queen în anul 1991 la casa de discuri EMI?” avem în
documentele indexate secvenţa de text „Innuendo este un album al formaţiei Queen,
lansat în anul 1991 de casa de discuri EMI.”. Evident, sistemul va întoarce acest
fragment de text cu un scor mare dacă interogarea va conţine şi sinonimul cuvântul
„trupa”, „formaţia”. Astfel, secvenţa de text de mai sus va căpăta o relevanţă mai mare şi
evident astfel se va creşte şi probabilitatea sistemului care va căuta răspunsul în acest
fragment de text.

Entităţile de tip nume sau datele calendaristice pe care le folosim în interogare


sunt mai importante decât celelate entităţi. Prin urmare, o interogare care va conţine astfel
de entităţi va trebui să folosească priorităţi pentru termenii din interogare, iar priorităţile
alocate entităţile de tip nume sau pentru datele calendaristice vor fi mai mari.

În urma prelucrării unei întrebări la care se caută răspuns, se identifică focusul


întrebării, care reprezintă cel mai important cuvânt folosit în identificarea răspunsului.
Acesta trebuie inclus ca fiind obligatoriu în interogare. De cele mai multe ori un articol al
cărui titlu este chiar focusul unei întrebări va conţine răspunsul la acea întrebare. De
aceea, adăugarea opțională a apariţiei focusului în titlu va duce la creşterea şanselor
găsirii răspunsului corect în secvenţele cu relevanţă mare.

Pentru o întrebare de tip DEFINIŢIE (în care răspunsul căutat reprezintă o


definiţie) de multe ori lista de cuvinte folosite pentru a construi interogarea este restrânsă.

4
CLEF2007: http://www.clef-campaign.org/2007.html
5
CLEF2008: http://www.clef-campaign.org/2008.html

7
De aceea, pentru a restrânge numărul de secvenţe de text întoarse de o căutare trebuie
adăugate verbele care marchează o definiţie: a fi, a defini, a reprezenta, a arăta. De
asemenea, ca la cazul anterior adăugarea optională a condiţiilor ca în titlu să avem
cuvintele care apar în întrebare va duce la creşterea şanselor găsirii răspunsului corect în
secvenţele cu relevanţă mare.

În urma aplicării acestor îmbunătăţiri, o interogare a sistemelor de întrebare-


răspuns construite va căuta documente cu referinţă obligatorie la focusul întrebării, la
entităţile de tip nume şi la datele calendaristice, şi cu referinţă opţională la sinonimele
cuvintelor folosite în interogare, iar în cazul întrebărilor de tip definiţie vor fi căutate
documente în care sa apară obligatoriu atât focusul întrebării cât şi unul dintre verbele „a
fi”, „a defini”, „a reprezenta”, „a arăta”. De asemenea, s-au adăugat opţiuni de căutare a
documentelor care au ca titlu focusul întrebării sau cuvintele care apar în întrebările de tip
definiţie.

Scorul Lucene

Relevanţa unui document obţinut de Lucene pornind de la o interogare a


utilizatorului este un număr cuprins între 0 şi 1 şi este calculată cu ajutorul unei
combinaţii dintre Modelul Spaţiu Vector (VSM) al Regăsirii Informaţiilor şi Modelul
Boolean. Ideea generală a VSM este următoarea: cu cât creşte numărul de apariţii ale
unui termen din interogare într-un document relativ la numărul de apariţii ale termenului
în toate documentele din colecţie, cu atât creşte relevanţa documentului respectiv în ceea
ce priveşte interogarea. Modelul Boolean este folosit pentru a scădea numărul
documentelor pentru care trebuie calculată relevanţa, pe baza folosirii logicii booleene în
specificaţiile interogării. De asemenea, Lucene rafinează acest model prin metode proprii
şi mai trebuie menţionat faptul că în calculul relevanţei se face o diferenţă clară în modul
de indexare a colecţiei, deci indexări realizate diferit vor întoarce relevanţe diferite pentru
documente pe care s-a făcut aceeaşi căutare.

8
Formula generală de calculare a scorului de bază Lucene este dată mai jos:

Acest scor este calculat pentru fiecare document în parte, iar documentele care au
această valoare peste 0, vor fi documentele care s-au potrivit căutării în index. În faza
următoare se identifică documentul cu scorul cel mai mare. În caz că acest scor este peste
valoarea 1 se normalizează toate scorurile în funcţie de această valoare, astfel încât să
avem toate scorurile documentelor care s-au potrivit cel mult 1.

În tabela de mai jos sunt explicate elementele care apar în formula de calculare a
scorului Lucene.

Element Descriere

tf(t in d) Frecvenţa termenului (t) în documentul (d)

idf(t) Inversul frecvenţei termenului (t) în document

boost(t.field in d) Valoarea asociată câmpului în momentul


indexării

lengthNorm(t.field in d) Valoarea normalizată a câmpului, care a fost


obţinută folosind numărul de termeni din cadrul
câmpului. Această valoare este calculată în
momentul indexării şi este salvată în index.

coord(q, d) Factorul de coordonare este calculat în funcţie de


termenii documentului care pot fi folosiţi în
interogare.

queryNorm(q) Valoarea normalizată pentru o interogare,


folosind suma pătratelor ponderilor asociate
termenilor interogării.

9
Clasele de bază Lucene

Să aruncăm o privire la clasele de bază ce sunt folosite de motorul de indexare şi


căutare Lucene.

• Document – Clasa Document este folosită pentru a reprezenta un document în


Lucene. Vom indexa obiecte Document şi vom obţine obiecte Document ca
rezultat al căutărilor.

• Field – Clasa Field reprezintă o secţiune a clasei Document. Obiectul de tip Field
va conţine un nume pentru secţiune şi datele curente.

• Analyzer – Clasa Analyzer este o clasă abstractă ce oferă o interfaţă care va fi


preluată de clasa Document şi va fi transformată în termeni ce pot fi indexaţi. Sunt
câteva implementări foarte utile ale acestei clase, dar cea folosită mai des este
clasa StandardAnalyzer.

• IndexWriter – Clasa IndexWriter este folosită pentru a crea şi a administra


indecşii.

• IndexSearcher – Clasa IndexSearcher este folosită pentru a căuta în cadrul unui


index.

• QueryParser – Clasa QueryParser este folosită pentru a construi un parser ce


poate căuta în cadrul unui index.

• Query – Clasa Query este o clasă abstractă ce conţine criteriul de căutare creat de
clasa QueryParser.

• Hits – Clasa Hits conţine obiecte Document ce sunt întoarse de execuţia


obiectului Query folosind indexul.

10
Indexarea unui Document

Primul pas constă în instalarea aplicaţiei Lucene. Acest lucru se face foarte simplu
urmând instrucţiunile de pe pagina de start6.

Să presupunem ca vrem să scriem un program cu ajutorul căruia profesorii să-şi


poată încărca în indexul Lucene articolele proprii. Programul trebuie să permită ca
profesorii să-şi introducă numele, titlul articolului, şi să selecteze dintr-o listă de categorii
ce descriu articolul. Vom presupune de asemenea că acest program va depozita articolul
într-un spaţiu accesibil de pe web. Pentru a indexa acest articol vom avea nevoie de
articol, numele autorului, data când acesta a fost scris, subiectul articolului, titlul
articolului, şi adresa URL unde este acesta localizat. Cu aceste informaţii putem construi
un program ce poate indexa în mod eficient o colecţie de articole şi care să ne permită
mai apoi regăsirea uşoară în această colecţie.

În continuare prezentăm structura clasei de bază ce va face indexarea incluzând de


asemenea şi importurile de care avem nevoie.

Scheletul clasei de bază incluzând şi importurile

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;

import java.util.Date;

public class ArticleIndexer {

6
http://lucene.apache.org/

11
}

Primul lucru pe care e nevoie să-l facem este să găsim o modalitate de a


transforma articolul nostru într-un obiect de tip Document.

Metoda de a crea un Document dintr-un articol

private Document createDocument(String article, String author,


String title, String topic,
String url, Date dateWritten) {

Document document = new Document();


document.add(Field.Text("author", author));
document.add(Field.Text("title", title));
document.add(Field.Text("topic", topic));
document.add(Field.UnIndexed("url", url));
document.add(Field.Keyword("date", dateWritten));
document.add(Field.UnStored("article", article));
return document;
}

Mai întâi se creează un obiect de tip Document, care va fi asociat fiecărui articol
pe care vrem să-l indexăm. După care trebuie completate cîmpurile articolului din cadrul
clasei Document. Numele considerate sunt la întâmplare şi funcţionează precum cheile
dintr-un HashMap. În cazul nostru am considerat cîmpurile “author”, “title”, “topic”,
“url”, “date” şi “article”, şi le-am completat cu valorile corespunzătoare articolului pe
care vrem să-l indexăm. Perechile (câmp, valoare) reprezintă termenii documentului
current. Singura condiţie este ca numele folosite să fie de tip String. Metoda de adăugare
add a unui Document va lua un obiect de tip Field pe care-l vom construi folosind una din
metodele statice din clasa Field. Există patru metode disponibile pentru a adăuga obiecte
de tip Field la un Document.

• Field.Keyword – Informaţia este memorată şi indexată, dar nu este tokenizată.


Aceasta este foarte utilă pentru informaţiile ce trebuie scrise fără a fi modificate

12
precum sunt cele de tip dată. De fapt, Field.Keyword poate considera un obiect de
tip Date ca intrare.

• Field.Text – Informaţia este memorată, indexată şi tokenizată. În câmpurile


Field.Text nu trebuie folosite pentru cantităţi mari de date, cum este de exemplu
conţinutul articolului, deoarece indexul va creşte foarte mult întrucât va conţine o
copie completă a articolului, plus o variantă tokenizată.

• Field.UnStored – Informaţia nu este scrisă, dar este indexată şi tokenizată.


Cantităţile mari de date precum conţinutul articolului trebuie plasate în acest
câmp.

• Field.UnIndexed – Informaţia este scrisă, dar nu este indexată sau tokenizată.


Această informaţie este folosită ca dată pe care vrem s-o obţinem ca rezultat al
căutării, fără a avea posibilitatea de a căuta în ea. În exemplu nostru, deoarece nu
vrem să permitem căutarea în URL-ul articolului, nu există motive să-l indexăm,
dar îl vom dori ca rezultat atunci când găsim un rezultat al căutării.

Acum având un obiect de tip Document, avem nevoie să considerăm un


IndexWriter pentru a scrie acest Document în index.

Indexarea Multicriterială

Din modul de funcţionare al motorului Lucene nu avem restricţii din punct de


vedere al numărului câmpurilor pe care le adăugăm la index, şi de asemenea nu avem
restricţii a modului în care adăugăm obiectele de tip Field la un Document. Prin urmare
este posibil să adăugăm mai multe câmpuri pe care să le folosim în procesul de căutare în
index. Pentru aplicaţia noastră, deja am adăugat câmpurile “author”, “title”, “topic”,
“url”, “date” şi “article”, dar pe lângă acestea putem adăuga şi alte câmpuri pe care să le
folosim în procesul de căutare în index cum ar fi lista referinţelor din articolul curent,
abstractul articolului sau cuvintele cheie.

Ce este important de ştiut este faptul că atunci când vom folosi indexul în
procesul de căutare, va trebui să specificăm care din aceste câmpuri este folosit implicit

13
în procesul de căutare. Vom vedea în continuare cum se specifică acest câmpul şi modul
în care putem căuta după mai multe câmpuri în acelaşi timp.

Metoda de a adăuga un Document la index

String indexDirectory = "lucene-index";

private void indexDocument(Document document) throws Exception {


Analyzer analyzer = new StandardAnalyzer();
IndexWriter writer = new IndexWriter(indexDirectory, analyzer,
false);
writer.addDocument(document);
writer.optimize();
writer.close();
}

Vom crea mai întâi un StandardAnalyzer şi apoi pe baza acestuia vom creea un
IndexWriter. În constructor vom specifica directorul unde va fi creat indexul. Valoarea
booleană de la sfârşitul constructorului îi va spune lui IndexWriter fie că va trebui să
creeze un nou index, fie că va trebui să adauge la un index existent. Când se adaugă un
nou document la un index existent va trebui să specificăm false. Apoi, vom adăuga
obiectul Document la index. La sfârşit, se optimizează şi se închide indexul. Dacă se vor
adăuga mai multe obiecte Document, va trebui ca de fiecare dată să optimizăm şi să
închidem indexul, până când toate obiectele Document vor fi adăugate la index.

Mai jos este metoda care pune la un loc toţi aceşti paşi.

Metoda care face indexarea

public void indexArticle(String article, String author,


String title, String topic,
String url, Date dateWritten)
throws Exception {
Document document = createDocument(article, author,

14
title, topic,
url, dateWritten);
indexDocument(document);
}

Prin execuţia acestui cod pentru un articol el va fi adăugat la index. Prin


modificarea valorii booleene din constructorul IndexWriter la true, se va creea un nou
index. Prin urmare acest lucru trebuie făcut prima dată când se crează indexul sau de
fiecare dată când se doreşte reconstruirea lui. Odată indexul terminat de construit, se
poate căuta un articol.

Căutarea în Index

Am adăugat articolele noastre la index şi acum dorim să le căutăm. Presupunând


că am creat şi o interfaţă prieteneasă pentru utilizatorii noştri, trebuie doar să luăm
cererea de la un utilizator şi să o rulăm folosind indexul creat de noi. Deoarece am
adăugat câteva tipuri de câmpuri, utilizatorii programului pot folosi diverse creiterii de
căutare. După cum vom vedea, vom putea specifica care câmp este implicit folosit la
căutare, dar de asemenea vom vedea cum vom putea căuta în orice câmpuri care se află în
index.

Codul necesar efectuării căutării este prezentat mai jos.

Căutarea în index – câmpul searchCriteria va fi oferit de utilizator

IndexSearcher is = new IndexSearcher(indexDirectory);


Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser("article", analyzer);
Query query = parser.parse(searchCriteria);
Hits hits = is.search(query);

15
Cu toate că sunt implicate o mulţime de clase, operaţia de căutare este relativ
simplă. Primul lucru pe care trebuie să-l realizăm este de a creea un obiect IndexSearcher
care foloseşte directorul în care articolele au fost indexate. După aceea se creează un
obiect StandardAnalyzer. Obiectul StandardAnalyzer este trimis constructorului unui
QueryParser împreună cu numele câmpului implicit folosit în căutare. Acesta va fi
câmpul ce va fi folosit implicit dacă utilizatorul nu va specifica un câmp în criteriul de
căutare. În cazul nostru câmpul după care se face căutarea implicită este article. Apoi se
parsează criteriul curent de căutare introdus de utilizator, obţinând un obiect de tip Query.
În continuare se execută obiectul de tip Query folosind obiectul de tip IndexSearcher.
Acesta va întoarce un obiect Hits, care este o colecţie a tuturor articolelor ce verifică
criteriul de căutare specificat.

Extragerea obiectelor Document din obiectul Hits se face folosind metoda doc() a
obiectului Hits.

Extragerea obiectelor Document

for (int i=0; i<hits.length(); i++) {


Document doc = hits.doc(i);
// display the articles that were found to the user
}
is.close();

Clasa Document are o metodă get() care poate fi utilizată pentru a extrage
informaţiile ce au fost scrise în index. De exemplu, pentru a obţine autorul dintr-un obiect
de tip Document ar trebui să apelăm metoda doc.get("author"). Deoarece am adăugat
chiar articolul ca un câmp Field.UnStored, încercarea de a-l accesa folosind metoda get
ne va întoarce null. Oricum, deoarece am adăugat URL-ul unui articol la index, vom
putea să-l identificăm şi să-l afişăm utilizatorului în lista cu rezultatele. Va trebui ca de
fiecare dată să închidem IndexSearcher după ce am terminat extragerea tuturor obiectelor
de tip Document. Încercările de a extrage un Document după închiderea acestuia va
genera o eroare:

java.io.IOException: Bad file descriptor

16
Specificarea criteriilor de căutare

Lucene oferă o mulţime de moduri în care putem face căutarea, incluzând


interogări care conţin AND OR şi NOT, căutări fuzzy, căutari în vecinătate, căutări cu
caractere speciale, şi căutări în domeniu. Să vedem câteva exemple:

Exemplul 1: să căutăm articolele profesorului Henry ce conţin informaţii despre


relativity şi quantum physics. Interogarea Lucene va fi:

author:Henry relativity AND "quantum physics"

cu semnificaţia: se caută Henry în câmpul author, iar în câmpul article se caută relativity
şi "quantum physics" (reamintim că implicit căutarea se face după câmpul article folosit
în constructorul QueryParser). După cum observăm putem face simultan căutarea în
două câmpuri ale indexului creat: author şi article.

Exemplul 2: să identificăm articolele ce conţin fraza ”string theory” şi nu conţin


Einstein. Interogarea Lucene este mai jos:

"string theory" NOT Einstein

Exemplul 3: să căutăm articolele care conţin Kepler la cel mult 5 cuvinte de Galileo:

"Galileo Kepler"~5

Exemplul 4: să găsim articolele profesorului Johnson scrise în January anul 2008:

author:Johnson date:[01/01/2008 TO 01/31/2008]

După cum am văzut dacă nu specificăm un câmp atunci căutarea s-a făcut după câmpul
article. De asemenea, am putut să facem căutări după orice câmp din Document atâta
timp cât acesta a fost adăugat ca Field.UnIndexed.

17
Concluzii

Lucene este un motor de căutare care, deşi utilizeată tehnici complexe de indexare
şi regăsire, este extrem de uşor de folosit. O facilitatea importantă a motorului Lucene
vine din faptul că putem adăuga oricâte câmpuri la index pe care le putem folosi mai apoi
în procesul de căutare.

Lucene nu caută automat în documentele noastre, dar oferă o metodă pentru


scrierea propriilor noastre căutări. Cu toate că Lucene suportă doar text simplu, există
disponibile clase Java care pot transforma în text simplu documente HTML, XML,
documente Word, şi fişiere PDF. Majoritatea acestor clase sunt disponibile pornind de la
situl web Lucene.

Bibliografie

• Gospodnetic, O., Hatcher, E. 2005. Lucene in Action. Manning Publications Co.

• Paul, T. 2004. The Lucene Search Engine: Adding search to your applications.
http://www.javaranch.com/journal/2004/04/Lucene.html

Links

• Lucene Resources: http://wiki.apache.org/jakarta-lucene/Resources

18

S-ar putea să vă placă și